0

Can setting up environment variables replace using update-alternatives?

If not, when shall we use which way? Thanks.

My question comes from reading https://askubuntu.com/a/895024/1471

Too complex most of the answers for me.

Initially, Oracle decided to be able to have several versions of Java installed, based on setting some environment variables.

It was easy, but too complex for those who didn't know those variables, and somebody invented "update-java-alternatives".

"update-java-alternatives" has proven to be simple, when everything is configured for you, you simply have to execute this program, and choose the version you want.

The problem is that this solution is too complex to configure, if you have to configure yourself (you have to configure it for each and every command of java).

The best answer is go back to the basics.

set in your .bash_profile (for your user) or in /etc/profile (for every user) the following variables:

JAVA_HOME=<The home of your new java distribution>

PATH=<The bin directory of your new java distribution>:$PATH

In my case this was, even easier... I already had a file in /etc/profile.d with the following content (just updated it to the new directory structure):

export J2SDKDIR=/usr/lib/jvm/jdk1.8.0_121 
export J2REDIR=/usr/lib/jvm/jdk1.8.0_121/jre
export PATH=/usr/lib/jvm/jdk1.8.0_121/bin:/usr/lib/jvm/jdk1.8.0_121/db/bin:/usr/lib/jvm/jdk1.8.0_121/jre/bin:$PATH
export JAVA_HOME=/usr/lib/jvm/jdk1.8.0_121
export DERBY_HOME=/usr/lib/jvm/jdk1.8.0_121/db

And that's all!!!

Tim
  • 101,790

2 Answers2

2

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

  1. determines the name that it has been invoked as (the basename of its 0th argument);
  2. 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);
  3. sets JAVA_HOME to the value of that root directory name; and
  4. 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

  1. 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;
  2. sets JAVA_HOME to the value of that root directory name; and
  3. 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

JdeBP
  • 68,745
1

When it comes to selecting a program to run, environment variables and update-alternatives are complementary. The former allow any one to define a value for a setting, where supported by whatever software is being used; the latter allow the system administrator to choose a default provider for a common command.

For example, the administrator can choose a system’s default editor and pager by configuring the editor and pager alternatives respectively; users can choose their preferred editor and pager by setting the EDITOR (or VISUAL) and PAGER environment variables respectively.

Thus environment variables don’t replace update-alternatives, they are intended for different use-cases. They can however override the choice set by update-alternatives.

Stephen Kitt
  • 434,908