• ES

JAVA - JENV VS SDKMAN!

MANAGING SEVERAL JAVA VERSIONS WITHOUT LOSING YOUR MIND

JAVA - JENV VS SDKMAN!

If you work with Java long enough, you hit a point where one project needs Java 8, another needs Java 17, the new one needs Java 21 and, while we're at it, somebody on the team wants to try the latest release. Installing and uninstalling JDKs by hand, or changing JAVA_HOME in your .bashrc every time you switch projects, is one of those things you do twice, and by the third time you go looking for a tool.

The two best-known options are jEnv and SDKMAN!. I used SDKMAN! for years without giving it much thought, and at my last company I had to use jEnv, because it was the team standard. And I'll tell you up front: I never really warmed to it. Let me tell you about both, and why.

The difference underneath

Even though they look like they do the same thing, they take different approaches:

  • jEnv only manages Java versions. It doesn't install anything. You install the JDKs yourself (with brew, your system's package manager, or by downloading them) and then you tell jEnv where they are.
  • SDKMAN! installs and manages. You ask for a Java version, it downloads it, installs it and leaves it ready to use. And not just Java: also Maven, Gradle, Kotlin, Groovy, Scala, JBang and a good handful of other tools from the JVM world.

That difference, which looks small on paper, is the one that shapes your day-to-day.

jEnv

Installing it on macOS, where it's used the most, would go something like this:

brew install jenv
echo 'eval "$(jenv init -)"' >> ~/.zshrc

And now comes the part I like least. Since jEnv doesn't install Java, you first install it somewhere else, and then you register it by hand:

brew install openjdk@21
jenv add /opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home

And so on for every version you want to use. Then, to pick a project's version, you go to its folder and run:

jenv local 21

That creates a .java-version file in the directory, which you can commit to the repository, and from then on, every time you enter that folder, jEnv uses that version. This part is well thought out, to be fair.

What's not so well thought out is the JAVA_HOME thing. By default, jEnv changes the java you run, but not the JAVA_HOME variable, which is precisely the one Maven, Gradle, your IDE and half the ecosystem use to know which Java to use. For that you have to enable a plugin:

jenv enable-plugin export

If you forget, you end up with java -version saying one thing and Maven compiling with another. And I can assure you I'm not the only one who has lost some time on this: it's by far the most repeated complaint in the project's issues.

Add to that the fact that, when you register a JDK, jEnv creates several aliases for the same version (21, 21.0, openjdk64-21.0.x...), and that every new JDK you install is one more manual step, and you'll understand why, on a team where everyone had their machine set up their own way, "it works on my machine" came up more often than I'd have liked.

SDKMAN!

Installation is a script:

curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"

From there, everything is the sdk command. To see which Java versions are available, from all vendors (Temurin, Corretto, Zulu, GraalVM...):

sdk list java

To install one, you use the identifier that shows up in that list (the version numbers in this post are just examples, use the ones you get):

sdk install java 21.0.8-tem

And to switch versions, you have two options:

sdk use java 17.0.16-tem       # current terminal only
sdk default java 21.0.8-tem    # default for all terminals

Here JAVA_HOME updates itself, no plugins or anything. You change the version and everything else finds out.

To pin a project's version, the equivalent of jEnv's .java-version is the .sdkmanrc file:

sdk env init

That generates the file with the version you're using, and anyone who clones the repo only has to run sdk env install for SDKMAN! to install exactly what the project needs. And if you want it to switch versions automatically when you enter the folder, just enable sdkman_auto_env=true in ~/.sdkman/etc/config.

The best part, for me, is that the same file can also pin the Maven or Gradle version:

java=21.0.8-tem
maven=3.9.11

One file in the repo, one command, and the whole team works with exactly the same versions.

Are they still current?

Both are still alive, but at very different paces. SDKMAN! is very active, and in fact it's slowly rewriting its commands in Rust to make them faster. jEnv still works, but I get the impression that its maintenance is quite a bit slower.

And if you work with several languages at once (Java, Node, Python...), these days it's also worth a look at tools like mise or asdf, which manage versions of everything with a single tool. But if your world is the JVM, SDKMAN! is still, in my opinion, the most comfortable option.

To wrap up

jEnv isn't a bad tool: it's lightweight, it does what it promises, and if you already have your JDKs installed with brew, it fits well. But when you compare it with SDKMAN!, which installs, manages, takes care of JAVA_HOME and also brings you Maven and Gradle in the same package, it's hard to find an advantage. What bothered me wasn't so much the tool itself as all the manual steps it left in everyone's hands. And on a team, every manual step is a chance for somebody's machine to behave differently from everyone else's.


Tweet Send
0 Comments
Loading...