Switch Among Java Versions: FZF and SDKMAN!

The bang in the subject belongs to SDKMAN!, not me. It’s a tool for managing versions of various Java-world tools. Think nvm for Java, Maven, Groovy, et al.

I’ve been back in Java world lately for work, using Apache NiFi to create RDF Triples and push to Ontotext GraphDB. Our current NiFi installation runs on Java 8, so I’ve been swapping between Java 8 and the new hotness in Java 16, which I’m actually not saying ironically. I’ve been sleeping on Java, but it hasn’t conceded to JavaScript, Rust, or Go yet.

I used SDKMAN! to install Coretto, both 8 and 16, but the syntax for switching is laborious:

$ sdk use java 16.0.0.36.1-amzn

When I run just the plain sdk command, I see it offers a completion command:

$ sdk
Usage: sdk <command> [candidate] [version]
       sdk offline <enable|disable>
 commands:
        install   or i    <candidate> [version] [local-path]
        uninstall or rm   <candidate> <version>
        list      or ls   [candidate]
        use       or u    <candidate> <version>
        completion        <bash|zsh>
        default   or d    <candidate> [version]
        home      or h    <candidate> <version>
        env       or e    [init|install|clear]
        current   or c    [candidate]
        upgrade   or ug   [candidate]
        version   or v
        broadcast or b
        help
        offline           [enable|disable]
        selfupdate        [force]
        update
        flush             [archives|tmp|broadcast|version]
 candidate  :  the SDK to install: groovy, scala, grails, gradle, kotlin, etc.
                  use list command for comprehensive list of candidates
                  eg: $ sdk list
    version    :  where optional, defaults to latest stable if not provided
                  eg: $ sdk install groovy
    local-path :  optional path to an existing local installation
                  eg: $ sdk install groovy 2.4.13-local /opt/groovy-2.4.13

So I tried it:

$ sdk completion zsh
zsh is not supported yet.

That’s OK — I have fzf! (That bang is my own.)

SDKMAN! uses different tools under the covers to do things like list versions, so my first attempt to make a universal sdk use <candidate> function was going to get verbose, and I only ever switch among Java versions now. I opted to focus my efforts on Java only.

If you list the Java versions, you see this output:

$ sdk list java
================================================================================
 Available Java Versions
================================================================================
  Vendor        | Use | Version      | Dist    | Status     | Identifier
--------------------------------------------------------------------------------
 AdoptOpenJDK   |     | 16.0.0.j9    | adpt    |            | 16.0.0.j9-adpt      
                |     | 16.0.0.hs    | adpt    |            | 16.0.0.hs-adpt      
                |     | 11.0.10.j9   | adpt    |            | 11.0.10.j9-adpt     
                |     | 11.0.10.hs   | adpt    |            | 11.0.10.hs-adpt     
                |     | 8.0.282.j9   | adpt    |            | 8.0.282.j9-adpt     
                |     | 8.0.282.hs   | adpt    |            | 8.0.282.hs-adpt     
  Alibaba       |     | 11.0.9.4     | albba   |            | 11.0.9.4-albba      
                |     | 11.0.8       | albba   |            | 11.0.8-albba        
                |     | 8u272        | albba   |            | 8u272-albba         
                |     | 8.5.5        | albba   |            | 8.5.5-albba         
  Amazon        | >>> | 16.0.0.36.1  | amzn    | installed  | 16.0.0.36.1-amzn    
                |     | 15.0.2.7.1   | amzn    |            | 15.0.2.7.1-amzn     
                |     | 11.0.10.9.1  | amzn    |            | 11.0.10.9.1-amzn    
                |     | 8.282.08.1   | amzn    | installed  | 8.282.08.1-amzn     
  Azul Zulu     |     | 16.0.0       | zulu    |            | 16.0.0-zulu         
                |     | 16.0.0.fx    | zulu    |            | 16.0.0.fx-zulu      
                |     | 15.0.2.fx    | zulu    |            | 15.0.2.fx-zulu      
                |     | 11.0.10      | zulu    |            | 11.0.10-zulu        
                |     | 11.0.10.fx   | zulu    |            | 11.0.10.fx-zulu     
                |     | 8.0.282      | zulu    |            | 8.0.282-zulu        
                |     | 8.0.282.fx   | zulu    |            | 8.0.282.fx-zulu     
                |     | 7.0.292      | zulu    |            | 7.0.292-zulu        
                |     | 6.0.119      | zulu    |            | 6.0.119-zulu        
  BellSoft      |     | 16.0.0.fx    | librca  |            | 16.0.0.fx-librca    
                |     | 16.0.0       | librca  |            | 16.0.0-librca       
                |     | 11.0.10.fx   | librca  |            | 11.0.10.fx-librca   
                |     | 11.0.10      | librca  |            | 11.0.10-librca      
                |     | 8.0.282.fx   | librca  |            | 8.0.282.fx-librca   
                |     | 8.0.282      | librca  |            | 8.0.282-librca      
  GraalVM       |     | 21.0.0.2.r11 | grl     |            | 21.0.0.2.r11-grl    
                |     | 21.0.0.2.r8  | grl     |            | 21.0.0.2.r8-grl     
                |     | 20.3.1.2.r11 | grl     |            | 20.3.1.2.r11-grl    
                |     | 20.3.1.2.r8  | grl     |            | 20.3.1.2.r8-grl     
                |     | 19.3.5.r11   | grl     |            | 19.3.5.r11-grl      
                |     | 19.3.5.r8    | grl     |            | 19.3.5.r8-grl       
  Java.net      |     | 17.ea.16     | open    |            | 17.ea.16-open       
                |     | 17.ea.15     | open    |            | 17.ea.15-open       
                |     | 17.ea.14     | open    |            | 17.ea.14-open       
                |     | 17.ea.6.lm   | open    |            | 17.ea.6.lm-open     
                |     | 17.ea.5.lm   | open    |            | 17.ea.5.lm-open     
                |     | 17.ea.4.lm   | open    |            | 17.ea.4.lm-open     
                |     | 17.ea.2.pma  | open    |            | 17.ea.2.pma-open    
                |     | 16           | open    |            | 16-open             
                |     | 11.0.10      | open    |            | 11.0.10-open        
                |     | 11.0.2       | open    |            | 11.0.2-open         
                |     | 8.0.282      | open    |            | 8.0.282-open        
                |     | 8.0.265      | open    |            | 8.0.265-open        
  Mandrel       |     | 21.0.0.0     | mandrel |            | 21.0.0.0-mandrel    
                |     | 20.3.1.2     | mandrel |            | 20.3.1.2-mandrel    
                |     | 20.1.0.4     | mandrel |            | 20.1.0.4-mandrel    
  SAP           |     | 16           | sapmchn |            | 16-sapmchn          
                |     | 15.0.2       | sapmchn |            | 15.0.2-sapmchn      
                |     | 11.0.10      | sapmchn |            | 11.0.10-sapmchn     
  TravaOpenJDK  |     | 11.0.9       | trava   |            | 11.0.9-trava        
                |     | 8.0.232      | trava   |            | 8.0.232-trava 
================================================================================      
 Use the Identifier for installation:
     $ sdk install java 11.0.3.hs-adpt
================================================================================

Edit 7/1/2021: I’m fuzzy on when something is “installed” vs “local only,” but we have to account for either in the Status column. All commands below have been updated accordingly.

The identifier in the final column is the string we want. And for our function, we want to show only the versions that:

  • Are installed or “local only”
  • Are not currently in use

The command to get those versions is:

$ sdk list java | grep 'installed\|local only' | grep -v '>>>'

From that line, we want to grab the last field (the identifier), and then pipe all those identifiers to fzf. The number of fields per line isn’t consistent, however, so how do we grab the last field? awk to the rescue. It offers a predefined variable called NF that contains “the number of fields in the current record“. So our command to send just the identifiers for the installed Java versions is:

$ sdk list java | grep 'installed\|local only' | grep -v '>>>' | awk '{print $NF}' | fzf

We want to send the result of this to SDKMAN!’s use command, so we make a function called javau:

javau() {
   sdk use java $(sdk list java | grep 'installed\|local only' | grep -v '>>>' | awk '{print $NF}' | fzf)
 }

But why stop there? SDKMAN! also allows you to set the default version of a tool, so let’s make a javad command that has two differences:

  • Uses default instead of use
  • Doesn’t eliminate the currently-used version from the list

Here’s our function:

javad() {
   sdk default java $(sdk list java | grep 'installed\|local only' | awk '{print $NF}' | fzf)
 }

Finally, we might as well create a function to make it easier to install new Java versions. It has the following differences:

  • Uses install instead of default
  • Lists the versions that are not installed
  • Weeds out the headers and footers (tail and head will be useful here)

Here’s the javai command:

javai() {
   sdk install java $(sdk list java | tail -n +6 | head -n -5 | grep -v 'installed\|local only' | awk '{print $NF}' | fzf)
 }

Never settle for typing too much!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.