My Profile Photo

Bits and pieces


Between bits and bytes and all other pieces.
A tech blog about Clojure, Software Architecture, and Distributed Systems


Switching between multiple Java JDK versions in Emacs

Like many people who work with JVM languages, I do have many version of Java JDK installed on my machine. There are few utilities which help managing which version a given project should use and how to switch quickly between versions. Some of the most popular are:

Others prefer, a much simpler way to switch between JDKs like what is described in Managing Multiple JDKs on macOS.

Similarly to the previous article, I have a small function in my ~/.profile which allows me to quickly switch between installed versions from a terminal prompt.

# Add this to your bash profile (~/.bash_rc, ~/.bash_profile, ~/.profile)
#
# Base directory where all the different versions are
# installed. This is the default directory for MacOS
#
export JDK_BASE=/Library/Java/JavaVirtualMachines

function switch-java() {


    jvms=( `\ls -1 $JDK_BASE` )
    pref='jdk-'

    if [ "$1" = "8" ] ; then
        pref='jdk1.'
    fi

    if [ "$1" != "" ] ; then
        SELECTION=$(\ls -1 $JDK_BASE | grep $pref$1)
    fi

    if [ "$SELECTION" = "" ] ; then
        echo "Switch to which JVM?"
        for i in ${!jvms[@]}; do
            printf "   %2d - %s\n" $i ${jvms[$i]}
        done

        while [[ $SELECTION -ge ${#jvms[@]} ]] || [[ $SELECTION -lt 0 ]] || [[ $SELECTION == '' ]] ; do
            read -p "Select a JVM: " SELECTION
        done

        SELECTION=${jvms[$SELECTION]}
    fi

    echo '-----------------------------------------------------------------'
    echo "Switching to Java: $SELECTION"
    echo "Full path: $JDK_BASE/$SELECTION/Contents/Home"
    echo ":java-cmd \"$JDK_BASE/$SELECTION/Contents/Home/bin/java\""
    echo '-----------------------------------------------------------------'
    export JAVA_HOME=$JDK_BASE/$SELECTION/Contents/Home
    export JAVA_CMD=$JAVA_HOME/bin/java
    export PATH=$JAVA_HOME/bin:$PATH
    java -version
    echo '-----------------------------------------------------------------'

}

With this script you can select among the installed versions with switch-java -L or jump to the desired version like switch-java 12.

This short terminal cast shows a how to use it:

Now this works extremely well for all the terminal uses, however, most of my development is currently done in Clojure using Emacs + Cider.

CIDER is a amazing IDE which truly harnesses the power of Clojure REPL to enable a great experience with REPL Driven Development (thanks @bbatsov). However, there is no easy way to switch java version from inside Emacs when firing the REPL. So in true Emacs spirit, I made my own solution.

Add this to your ~/.emacs.d/init.el and re-evaluate your buffer (M-x eval-buffer).

;;
;; switch java
;;
(setq JAVA_BASE "/Library/Java/JavaVirtualMachines")

;;
;; This function returns the list of installed
;;
(defun switch-java--versions ()
  "Return the list of installed JDK."
  (seq-remove
   (lambda (a) (or (equal a ".") (equal a "..")))
   (directory-files JAVA_BASE)))


(defun switch-java--save-env ()
  "Store original PATH and JAVA_HOME."
  (when (not (boundp 'SW_JAVA_PATH))
    (setq SW_JAVA_PATH (getenv "PATH")))
  (when (not (boundp 'SW_JAVA_HOME))
    (setq SW_JAVA_HOME (getenv "JAVA_HOME"))))


(defun switch-java ()
  "List the installed JDKs and enable to switch the JDK in use."
  (interactive)
  ;; store original PATH and JAVA_HOME
  (switch-java--save-env)

  (let ((ver (completing-read
              "Which Java: "
              (seq-map-indexed
               (lambda (e i) (list e i)) (switch-java--versions))
              nil t "")))
    ;; switch java version
    (setenv "JAVA_HOME" (concat JAVA_BASE "/" ver "/Contents/Home"))
    (setenv "PATH" (concat (concat (getenv "JAVA_HOME") "/bin/java")
                           ":" SW_JAVA_PATH)))
  ;; show version
  (switch-java-which-version?))


(defun switch-java-default ()
  "Restore the default Java version."
  (interactive)
  ;; store original PATH and JAVA_HOME
  (switch-java--save-env)

  ;; switch java version
  (setenv "JAVA_HOME" SW_JAVA_HOME)
  (setenv "PATH" SW_JAVA_PATH)
  ;; show version
  (switch-java-which-version?))


(defun switch-java-which-version? ()
  "Display the current version selected Java version."
  (interactive)
  ;; displays current java version
  (message (concat "Java HOME: " (getenv "JAVA_HOME"))))

Here a short breakdown on the functions switch-java-*

  • switch-java-which-version? - Displays the current selected version in the message area.
  • switch-java-default - It restores the default Java version, the one in you system defaults.
  • switch-java - It lists the installed JDKs and prompt for a selection.

To switch version, just press M-x switch-java and select the desired version. Here is a small screencast on how to use it:

switch-java

Disclaimer: I’m not very proficient in Emacs Lisp and its ecosystem, surely, there are better ways (or more elegant ways) to achieve the same functionality. If you have any suggestion on how to improve I’ll be happy to hear your feedback.

comments powered by Disqus