Hashdot elevates Java-platform script interpreters to first class status on Unix-like operating systems. It provides a script aware replacement to the stock 'java' launcher, and thus avoids numerous issues in using the 'java' launcher to bootstrap a script interpreter. All relevant interpreter and JVM options (i.e: Java heap size) may be specified directly in a script header and/or via system profiles, without resorting to environment variables, command line arguments, and the additional wrapper shell scripts needed to maintain them.
Hashdot can support any Java-platform language. Sample profiles are provided for Jython, JRuby, Scala, Groovy, Rhino Javascript, and Clojure. Examples below are given using JRuby.
Hashdot is released under GPL v3 with a linking exception. See License.
See also relevent usage examples and news in the void * blog.
Hashdot supports direct Unix hashbang interpreter declarations without the need to use 'env', embedded shell scripts, or other novelties required with the 'java' launcher:
#!/usr/bin/hashdot ...the script...
Hashdot properties may be set in a script header or in profiles. Properties are used to control Hashdot and JVM startup. Hashdot properties are also mapped to Java system properties, and thus may influence the JDK, script interpreter, or other script components. Example script header:
#!/usr/bin/hashdot # Example Header # # Add Java VM options: #.hashdot.vm.options += -Xmx500m -Xss1024k # # Add jars to the system classpath from my.app.home: #.my.app.home = /opt/myapp #.java.class.path += ${my.app.home}/lib/*.jar # # Set default encoding Java system property: #.file.encoding = UTF-8 ...the script...
A profile is a set of properties in a *.hdp file under the system profile directory (default: /etc/hashdot/profiles). A profile is referenced via the special "hashdot.profile" directive. The following example is a complete JRuby script:
#!/usr/bin/hashdot #.hashdot.profile = jruby #.hashdot.vm.options += -Xmx256m puts "hello world!"
Where the script overrides of the jruby.hdp default heap size of 500m.
If hashdot is started using an executable name other than 'hashdot', it attempts to load an initial profile of the same name. So if the 'jruby.hdp' profile is installed, and 'jruby' symlinked to 'hashdot' under /usr/bin, as in:
hashdot* jruby -> hashdot*
Then the above script example can be further reduced to:
#!/usr/bin/jruby #.hashdot.vm.options += -Xmx256m puts "hello world!"
Use of a profile-specific symlink is the preferred approach and is required for script languages needing an alternative hashdot.header.comment, since a profile could not otherwise be specified in a script header.
Provided that the above "/usr/bin/jruby" symlink is the first "jruby" in PATH, the following will also be launched by hashdot:
#!/usr/bin/env jruby puts "hello world!"
Hashdot launching can thus be made completely transparent and compatible with the hashbang directives in existing scripts, without modification. Similarly, various command line invocations of a profile symlink can be successfully launched:
% jruby -d hello.rb hello world!
% cat hello.rb | jruby hello world!
% jruby -e 'puts "hello world!"' hello world!
Hashdot invoked script processes are reported by UNIX utilities such as "ps", "pgrep", or "top" using the script name and script arguments instead of as "java" or the full java arguments gore. Thus Java script interpreters under hashdot are reported the same as with native interpreters like bash. Example script "myservice":
[ "ps -p #{Process.pid}", "ps -f -p #{Process.pid}" ].each do |command| puts command IO.popen( command, "r" ) do |f| puts f.readlines end puts end
When run using the "jruby" symlink to hashdot:
% /usr/bin/jruby ./myservice ps -p 16774 PID TTY TIME CMD 16774 pts/2 00:00:01 myservice ps -f -p 16774 UID PID PPID C STIME TTY TIME CMD david 16774 27647 99 14:25 pts/2 00:00:01 /usr/bin/jruby ./myservice
When run using the current JRuby provided wrapper script and 'java' launcher (line breaks added):
% /opt/dist/jruby-1.1.4/bin/jruby ./myservice ps -p 16792 PID TTY TIME CMD 16792 pts/2 00:00:00 java ps -f -p 16792 UID PID PPID C STIME TTY TIME CMD david 16792 27647 89 14:26 pts/2 00:00:00 /opt/java/jdk_sun_1.6.0_05_x32/bin/java\ -client -Djruby.memory.max=500m -Djruby.stack.max=1024k -Xmx500m -Xss1024k \ -Xbootclasspath/a:/opt/dist/jruby-1.1.4/lib/jruby.jar -classpath \ /opt/dist/jruby-1.1.4/lib/bsf.jar:/opt/dist/jruby-1.1.4/lib/jruby.jar:\ /opt/dist/jruby-1.1.4/lib/profile.jar -Djruby.home=/opt/dist/jruby-1.1.4 \ -Djruby.lib=/opt/dist/jruby-1.1.4/lib -Djruby.script=jruby \ -Djruby.shell=/bin/sh org.jruby.Main ./myservice
Hashdot supports hashdot.daemonize, hashdot.io_redirect.*, and hashdot.pid_file properties, conveniently packaged in a provided "daemon" profile, to invoke a script as a detached UNIX daemon process. This includes full support for directing process STDERR and STDOUT (including JVM) to a specified file.
Hashdot has been tested under Linux (x86_32 and x86_64) and Mac OS X. See INSTALL in the source distribution root directory for additional notes on building.