Yes; and this is how some systems indeed do it.
FreeBSD's/TrueOS's javavm
command from its javavmwrapper
package
On FreeBSD/TrueOS all of the commands that are supplied in JREs are symbolic links in /usr/local/bin/
from those names (appletviewer
, checkvms
, extcheck
, idlj
, jar
, jarsigner
, java
, java-rmi.cgi
, javac
, javadoc
, javah
, javap
, jcmd
, jconsole
, jdb
, jdeps
, jhat
, jinfo
, jjs
, jmap
, jps
, jrunscript
, jsadebugd
, jstack
, jstat
, jstatd
, keytool
, manvm
, native2ascii
, orbd
, pack200
, policytool
, registervm
, rmic
, rmid
, rmiregistry
, schemagen
, serialver
, servertool
, tnameserv
, unpack200
, unregistervm
, wsgen
, wsimport
, and xjc
) to /usr/local/bin/javavm
.
The length of that list, and the fact that one usually wants all of these commands to employ the same JVM, is why Debian/Ubuntu has update-java-alternatives
. To slightly simplify: update-java-alternatives
is a program that knows all of those names, and invokes update-alternatives
on all of them. (In the Debian/Ubuntu system it is possible to have different commands invoking different JREs by using update-alternatives
to set individual commands rather than by using update-java-alternatives
to set them all in one fell swoop. This is not possible with FreeBSD's/TrueOS's javavmwrapper
mechanism.)
The approach of FreeBSD's/TrueOS's javavmwrapper
package is simply to include all of the possible command names, and instead of moving symbolic links around as one changes from JRE to JRE, employ a wrapper program that makes the decision at runtime, programatically rather than as the result of symbolic links in the filesystem.
/usr/local/bin/javavm
is that wrapper program. It
- determines the name that it has been invoked as (the basename of its 0th argument);
- scans the
/usr/local/etc/javavms
file for the root directory name of a suitable JRE (matching the user's choice of operating system, vendor, and version as set in JAVA_OS
, JAVA_VENDOR
, and JAVA_VERSION
environment variables);
- sets
JAVA_HOME
to the value of that root directory name; and
- overlays itself with either
${JAVA_HOME}/bin/${basename}
or ${JAVA_HOME}/jre/bin/${basename}
depending from which is found.
service bundles with the nosh toolset
When creating service bundle run
programs that use the nosh toolset, either one can rely upon the fact that the system administrator has provided a "universal" java
command that picks the right JRE, using something like Debian's/Ubuntu's alternatives
system or using something like the FreeBSD/TrueOS javavmwrapper
system …
#!/bin/nosh
#A distributed, scalable Time Series Database written on top of HBase
machineenv
envdir env
hardlimit -o 65536
softlimit -o hard
setuidgid -- opentsdb
sh -c "exec java ${JAVA_OPTS} -Xmx6000 -classpath \"${CLASSPATH}:${HBASE_CONF}\" -enableassertions -enablesystemassertions ${BIGTABLE_SUPPORT} -DLOG_FILE_PREFIX=/var/log/opentsdb/\"${MACHINEID}\"- net.opentsdb.tools.TSDMain"
… or one can make use of the toolset's find-matching-jvm
tool.
#!/bin/nosh
#A distributed, scalable Time Series Database written on top of HBase
machineenv
envdir env
hardlimit -o 65536
softlimit -o hard
find-matching-jvm --version 1.6 --version 1.7 --version 1.8
setuidgid -- opentsdb
sh -c "exec \"${JAVA_HOME}/bin/java\" ${JAVA_OPTS} -Xmx6000 -classpath \"${CLASSPATH}:${HBASE_CONF}\" -enableassertions -enablesystemassertions ${BIGTABLE_SUPPORT} -DLOG_FILE_PREFIX=/var/log/opentsdb/\"${MACHINEID}\"- net.opentsdb.tools.TSDMain"
find-matching-jvm
does a subset of the work of javavm
and has its selection of JRE driven by command-line options rather than environment variables; and it also knows more sources of JVM configuration information. It
- looks for the root directory name of a suitable JRE (matching the user's choice of operating system, vendor, and version as set by the command-line options), scanning the
/usr/local/etc/javavms
file if it exists, the /usr/lib/jvm
directory if it does not, and a hardwired search path if even that does not;
- sets
JAVA_HOME
to the value of that root directory name; and
- chain-loads to the next program, specified as the remainder of its command-line arguments.
In the example the next program (actually the next program but one) is the shell, which is told to overlay itself with "${JAVA_HOME}/bin/java"
.
Fundamentals
The fundamentals here are that either one uses the filesystem and a set of symbolic links to encode the decision that maps from a generic command such as /usr/bin/java
to the particular JRE desired, or one provides a command that finds the desired JRE programmatically by scanning configuration information. Debian's/Ubuntu's alternatives system does the former. FreeBSD's/TrueOS's wrapper system does the latter.
But there is a spectrum here, as shown by the nosh service bundle run
program examples.
The JAVA_HOME
environment variable is a convention that can be employed as an intermediary. (The Java VM and language themselves do not require this environment variable, note.) One can locate the JRE, place its root in the JAVA_HOME
environment variable and use the convention of always invoking ${JAVA_HOME}/bin/somecommand
directly.
This is of course the convention that the person setting the environment in .profile
is employing. Xe is setting JAVA_HOME
to xyr choice, and expecting the convention of invoking everything as ${JAVA_HOME}/bin/somecommand
, which xe achieves by dint of adding (the equivalent of) ${JAVA_HOME}/bin/
to xyr shell's command search path.
The FreeBSD/TrueOS javavm
wrapper and the nosh toolset's find-matching-jvm
tool both use this convention themselves, as described, and respect it if they find it in use already. If they find the JAVA_HOME
environment variable already set, they just skip all of the steps up to and including the point where they would set it themselves.
The Debian/Ubuntu alternatives system does not. It's somewhat hard to make a symbolic link aware of environment variables, and the alternatives system encodes the already-made decision (made at the point where the system administrator ran update-alternatives
explicitly or indirectly as part of update-java-alternatives
) into symbolic links in /etc/alternatives/
.
This is why it is important to also set the PATH
to include (the equivalent of) ${JAVA_HOME}/bin/
on Debian/Ubuntu. Without doing so, invoking java
will find /usr/bin/java
which will search the symbolic links of the alternatives system to find the preselected JRE. Only by explicitly adding to the shell's command search path will ${JAVA_HOME}/bin/
be found directly.
Further reading