Coursier: How to install multiple Java JDK/JVMs

On June 25, 2020, I tweeted that Coursier was providing a way to make installing Scala and Scala-related tools easier. I’ll look into that more soon, but in this tutorial I’ll show something different: How to install multiple JVMs/JDKs with Coursier.

Note 1: If you only want to see what the Coursier commands look like, skip down to the “cheatsheet” section at the bottom of this article to see the common Coursier/Java commands.

Note 2: I use the terms JDK and JVM interchangeably below. To me you’re actually installing a JDK and then using a JVM when you use the java command, but other people seem to use the terms interchangeably, so I do as well.

Getting rid of my old JVMs

The first thing I did was to delete all of the JVMs I installed last week with Homebrew. I used these commands to uninstall JDK 11, JDK 14, and jEnv:

brew uninstall openjdk@11    // uninstall jdk 11
brew cask uninstall java     // uninstall jdk 14
brew uninstall jenv          // uninstall jenv

I also had to remove some jEnv settings from my ~/.bash_profile file.

Installing Coursier

Next, I visited the Coursier installation page, and installed its cs command on my MacOS system with this command:

curl -fLo cs https://git.io/coursier-cli-macos &&
    chmod +x cs &&
    (xattr -d com.apple.quarantine cs || true) &&
    ./cs

That creates a cs file in my current directory, and I moved that to my bin directory, ~/bin.

List installed JDKs

To make sure my system was clean I used this Coursier command to list all installed JVMs:

cs java --installed

That command came back empty. I also tried to run java -v, and it told me there were no installed JDKs. (On a Mac you can also use the /usr/libexec/java_home command.)

List available JDKs

To list all of the available JDKs — all of the JDKs Coursier knows about — use this command:

cs java --available

That lists 250 available options, so I then ran this command:

cs java --available | grep adopt

That cuts the list down to 48 options. I’m only interested in JDK 11 and JDK 14 right now, and these are the two I want:

  • adopt:1.11.0-7
  • adopt:1.14.0-1

Installing a JDK

It turns out that there are at least two ways to install a JDK with Coursier. If you want to be very clear about which one you want to install, use these commands to get the JDK 11 and 14 versions listed above:

cs java --jvm adopt:1.11.0-7
cs java --jvm adopt:1.14.0-1

Or, to get the latest version of each JDK, use these commands:

cs java --jvm 11
cs java --jvm 14

Note that these JDKs are installed in these directories on a MacOS system:

~/Library/Caches/Coursier/jvm/adopt@1.11.0-7
~/Library/Caches/Coursier/jvm/adopt@1.14.0-1

Now when I run the Coursier installed command, I see this output:

$ cs java --installed
adopt:1.11.0-7
adopt:1.14.0-1

Note that if you don’t have any other JDKs installed and just want to install the “default” JDK, you can use this command:

cs java

The way this command works is:

  • If you don’t have any other JDKs installed, that command installs the default JDK, which is currently Java 1.8.x
  • If you do have a JDK installed and set up as shown below, that command runs the JDK’s java command

As noted, there are many other Java versions available, so look at the output from the available command to see those versions.

Specifying a JDK to use

Now that the JDKs are on your system, you need a way to declare which one you want to use. At this point if I try to run a java command on my system I see this output:

$ java
No Java runtime present, requesting install.

(Then I see a MacOS dialog appear with a prompt about installing Java.) So I know that the JDKs are on my system, but the system can’t find them.

You can show the JAVA_HOME environment for each installed JDK with these commands:

$ cs java --jvm 11 --env
export JAVA_HOME="/Users/al/Library/Caches/Coursier/jvm/adopt@1.11.0-7/Contents/Home"

$ cs java --jvm 14 --env
export JAVA_HOME="/Users/al/Library/Caches/Coursier/jvm/adopt@1.14.0-1/Contents/Home"

To set the JAVA_HOME variable to use JDK 14, you can now use this command:

$ eval "$(cs java --jvm 14 --env)"

After issuing that command, I see this output when running the java-version command:

$ java -version
openjdk version "14.0.1" 2020-04-14
OpenJDK Runtime Environment AdoptOpenJDK (build 14.0.1+7)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 14.0.1+7, mixed mode, sharing)

Similarly I can switch to JDK 11 with this command:

$ eval "$(cs java --jvm 11 --env)"

Note that on MacOS, this command just changes the JAVA_HOME environment variable.

Note that you can also list the JAVA_HOME value with this command:

$ cs java-home
/Users/al/Library/Caches/Coursier/jvm/adopt@1.11.0-7/Contents/Home

You can also clear the JAVA_HOME value with this command:

$ eval "$(cs java --disable)"

Uninstalling a JDK

I don’t see any commands to uninstall a JDK. But fortunately that’s pretty easy to do from the command line. On a Mac, just go to this directory, and then delete the JDK(s) you want to delete under that directory:

~/Library/Caches/Coursier/jvm

The Coursier documentation says that these are the “managed JVM directories” for Mac, Linux, and Windows systems:

Linux:   ~/.cache/coursier/jvm
MacOS:   ~/Library/Caches/Coursier/jvm
Windows: ?

I don’t see the Windows directory listed on this page or this page, and I don’t have a Windows system, so at the moment I don’t know what that directory is.

Coursier caches

Note that on a MacOS system, Coursier keeps its previous downloads cached in this directory:

~/Library/Caches/Coursier/v1

This cache includes files that are downloaded with Coursier via SBT, so if you delete this cache, SBT will need to re-download them when they are needed again.

Getting help

To get help on what Java-related commands are available, use this Coursier command:

$ cs java --help

There are more things you can do with Coursier, so see the main Coursier Java page for more details.

Coursier/Java cheatsheet

Here’s a cheatsheet that summarizes the Coursier Java commands that are available:

cs java --help        # java-related help
cs java --available   # list all available jvms
cs java --installed   # list installed jvms

cs java               # show the current JDK version
                      # OR install the default jvm (currently 1.8.x)

cs java --jvm adopt:1.14.0-1   # install specific jdk 14 version
cs java --jvm adopt:1.11.0-7   # install specific jdk 11 version

cs java --jvm 11               # install the latest jdk 11 (adopt@1.11.0-7)
cs java --jvm 14               # install jdk 14 (adopt@1.14.0-1)

cs java-home                   # show current JAVA_HOME
(such as ~/Library/Caches/Coursier/jvm/adopt@1.11.0-7/Contents/Home)

# show JAVA_HOME for JVM 11
$ cs java --jvm 11 --env
export JAVA_HOME="/Users/al/Library/Caches/Coursier/jvm/adopt@1.11.0-7/Contents/Home"

# show JAVA_HOME for JVM 14
$ cs java --jvm 14 --env
export JAVA_HOME="/Users/al/Library/Caches/Coursier/jvm/adopt@1.14.0-1/Contents/Home"

# set JAVA_HOME to JDK 14
$ eval "$(cs java --jvm 14 --env)"

# JVMs are installed here:
/Users/al/Library/Caches/Coursier/jvm

# Previous downloads are kept here:
/Users/al/Library/Caches/Coursier/v1

Summary: Coursier Pros & Cons

Coursier is an interesting command-line tool. Once you know the commands, and where things are installed, it’s a fast way to use multiple JVMs. The pros and cons I’ve seen so far are:

Pros:

  • Works on Mac, Linux, and Windows
  • It’s plenty fast
  • It lets you install and use multiple JVMs (many different ones)
  • You can use similar commands to install Scala and Scala tools
  • Everything goes in just a few spots, so if you want to manually delete things you can
  • It’s used by other tools like SBT and Ammonite
  • It has many other commands that I’ve barely looked at yet

Cons:

  • The commands to switch JVMs are not simple and easy to remember, but this can be remedied with some aliases and/or shell scripts
  • I didn’t see any commands to delete/uninstall JVMs, but you can do that manually

TBD:

  • I don’t know how updates work yet, i.e., how it works when there’s a new version of Java 11 or 14 in my examples

I just took another look at the jEnv docs, and I didn’t think about this earlier, but it looks like you should be able to use jEnv with the Coursier JDK installations. I found that jEnv made it easy to switch JDKs from the command line, and the Coursier commands are a little hard to remember, so I may try that.

Other possible solutions: Homebrew, jEnv, and SDKMAN

As I mentioned at the beginning of this article, I was using Homebrew and jEnv to manage multiple JDKs, and my takeaway there was that Homebrew is a little slow, but jEnv was easy to use.

I haven’t used SDKMAN, so I can’t compare the experience of using it. But I have its docs, and it looks like a terrific tool as well.