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 ofuse
- 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 ofdefault
- Lists the versions that are not installed
- Weeds out the headers and footers (
tail
andhead
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!