In Introducing Hashdot I laid out various problems with using the standard ‘java’ launcher and the ‘jruby’ wrapper bash script provided with the JRuby distribution. In this post, I’m going to describe in detail an alternative installation layout and setup of JRuby using Hashdot to best advantage. Some goals for this setup:

  1. Any and all invocations of ‘jruby’ should be launched via hashdot to take advantage of central configuration, better process report, and hashdot script headers where appropriate.

  2. No user environment dependencies. We want JRuby to function properly for any user on the system, without requiring each user to maintain environment variables, etc. in their own profile.

  3. A separate gem repository for JRuby, independent of the versioned JRuby distribution. This will enable JRuby upgrades without needing to rebuild or copy to a new gem repository.

  4. MRI-ruby and JRuby need to coexist. This means ‘ruby’ launches MRI and ‘jruby’ launches JRuby. Such utilities as ‘ri’, ‘gem’, and ‘rake’ should invoke MRI ruby, while we also want convenient access to the same via JRuby as ‘jri’, ‘jgem’, ‘jrake’, etc.

  5. The current latest ‘jruby’ interpreter, ‘jgem’, ‘jri’, ‘jrake’ and any bin scripts installed in the jruby gem repository should be available on a system standard PATH.

Layout in /opt

The ‘/opt’ directory appears most appropriate for this kind of system wide but custom setup. Alternatively ‘/usr/local’ could be used. The following structure is recommended and will be described in detail below.

/opt
|-- bin
|   |-- hashdot
|   |-- jgem
|   |-- jirb -> ../dist/jruby/bin/jirb
|   |-- jrake -> ../jruby/gems/bin/rake
|   |-- jri -> ../dist/jruby/bin/ri
|   |-- jruby -> hashdot
|   `-- jruby-shortlived -> hashdot
|-- dist
|   |-- jdk_sun_1.6.0_05_x32
|   |-- jdk_sun_1.6.0_07_x32
|   |-- jruby -> jruby-1.1.4
|   |-- jruby-1.1.3
|   `-- jruby-1.1.4
|-- hashdot
|   `-- profiles
`-- jruby
    `-- gems

Create the /opt skeleton

As root:

% mkdir /opt /opt/bin /opt/dist /opt/jruby

Configure, make, install Hashdot

Configuring hashdot currently involves editing the Makefile including with the source distribution. Set the following Makefile variables:

INSTALL_BIN=/opt/bin
PROFILE_DIR=/opt/hashdot/profiles

Hashdot may then be built or rebuilt via ‘make clean all’. You might want to then customize the various profiles before running ‘make install’ as root. You can also customize them in /opt/hashdot/profiles after install.

Install distributions

As root, unpack the Java JDK (or JRE) and JRuby under /opt/dist. Note I prefer to use a more descriptive name including the full java version number. Since we also reference some jruby distro provided bin scripts, symlink the latest jruby-1.1.4 to jruby. As shown, its possible for multiple versions of java/jruby to coexist for testing purposes.

Create /opt/jruby/gems repository

To create a starting gems repository, as root:

% cp -a /opt/dist/jruby-1.1.4/lib/ruby/gems /opt/dist/jruby/gems

To make sure this repository is used for all jruby invocations, GEM_HOME and GEM_PATH environment variables are set via Hashdot in the jruby.hdp profile (see below.) This has an important advantage over setting in a user profile: The MRI ruby gem home/path will not be effected and can be kept separate.

The above list directory tree for /opt/bin should be self explanatory. We create ‘jirb’ and ‘jri’ symlinks to dist/jruby/bin in order to make these easy to use. Note however that ‘jruby -S ri’ will also still work for these. Similarly we create jrake as a symlink to gems/bin/rake, since in a development environment we will likely want MRI and jruby versions both available. Finally, we choose to customize ‘jgem’ by copying and changing the hashbang to:

#!/opt/bin/jruby-shortlived

Which gives a bit of a performance advantage in common usage. Note that with the given hashdot profiles, the server vm is default, which is almost always the best choice for any long lived application.

System PATH

Adjusting the standard system PATH is Linux (or UNIX) distribution dependent and might involve modifying /etc/profile or an equivalent. In any case, /opt/bin should be found early in the path (since we only include ‘j*’ commands here), and /opt/jruby/gems/bin should be added late in the path to get additional jruby gem bin scripts available, but without overriding any MRI ruby gem equivalents. For example:

/opt/bin:/usr/local/bin:/bin:/usr/sbin:/usr/bin:/opt/jruby/gems/bin

In the case where you need both the MRI and jruby gem bin script versions to coexist in the PATH, create a ‘j*’ symlink in /opt/bin for the jruby version.

Hashdot profiles

The customized profiles I use on my 32bit development environment are given below.

default.hdp:

# HashDot default launch profile
# This profile is always read before any other profiles or hashdot
# properties

## Setup the default JVM

# JVM install directory
hashdot.vm.home = /opt/dist/jdk_sun_1.6.0_07_x32

# Use "amd64" instead of i386 for linux 64bit platforms
hashdot.vm.arch     = i386

# Use server by default (see alternative in shortlived.hdp)
hashdot.vm.mode     = server

hashdot.vm.libbase  = ${hashdot.vm.home}/jre/lib/${hashdot.vm.arch}

# Setup libpath to find libjvm.so and dependencies.
hashdot.vm.libpath  = ${hashdot.vm.libbase}
hashdot.vm.libpath += ${hashdot.vm.libbase}/${hashdot.vm.mode}

# TZ often needed for Java
hashdot.env.TZ = America/Los_Angeles

jruby.hdp:

# HashDot launch profile for JRuby (http://jruby.codehaus.org/)

jruby.home    = /opt/dist/jruby-1.1.4
jruby.lib     = ${jruby.home}/lib
jruby.script  = jruby
jruby.shell   = /bin/sh

# Identical defaults to jruby launcher
# These can still be overridden by the individual scripts
hashdot.vm.options += -Xmx500m -Xss1024k

# Only jruby.jar is required for typical usage (scripts can require
# bsf.jar or JIP profiler if desired).
hashdot.vm.options += -Xbootclasspath/a:${jruby.lib}/jruby.jar

hashdot.main = org.jruby.Main

# Arguments following these flags are _not_ a script to scan for
# hashdot headers.
hashdot.parse_flags.value_args = -F -I -r

# Give up looking for a script header with any of these
hashdot.parse_flags.terminal = -C -e -S

# GEM PATH/HOME
hashdot.env.GEM_HOME = /opt/jruby/gems
hashdot.env.GEM_PATH = ${hashdot.env.GEM_HOME}

shortlived.hdp:

# HashDot profile for short lived processes.
# Overrides default.hdp to use the client VM.

# Note: for current amd64 java distros, this entire profile should be
# commented out to disable it, since these only include a server VM

hashdot.vm.mode     = client

hashdot.vm.libpath  = ${hashdot.vm.libbase}
hashdot.vm.libpath += ${hashdot.vm.libbase}/${hashdot.vm.mode}

jruby-shortlived.hdp:

# HashDot profile for shortlived jruby scripts.

# Extends shortlived (client VM) and (jruby) profiles with further
# startup time tweaks.
hashdot.profile = shortlived jruby

# JMX setup slows startup and wont be used
jruby.management.enabled=false