DEV Community

Stop hand-juggling JAVA_HOME: automatic per-project JDK switching with Jolta

If you work on more than one Java codebase, you've probably lived this: one service targets JDK 17, another needs 21, and there's a legacy app that will only ever build on 11. Every context switch means resetting JAVA_HOME, or remembering to run a version-switch command, or discovering ten minutes into a weird compiler error that you're on the wrong JDK.

There are good tools for this already - SDKMAN!, jenv, asdf, mise. I've used most of them. I built Jolta because I wanted something that leaned all the way into automatic, per-project switching with as close to zero ceremony as possible, and that treated Windows as a real target. This post is the honest tour, including where the existing tools might still suit you better.

Full disclosure: Jolta is my project. MIT-licensed, on GitHub at OneAppPlatform/jolta.

The idea, borrowed from Node: Volta solved this nicely in the Node world - you pin a tool version per project, and the right one is used automatically because your node/npm are actually lightweight shims that resolve the correct version at call time. No cd hook, no "did I remember to switch" ritual. Jolta applies that model to the JDK.

cd my-service
jolta pin 21
java -version # โ†’ OpenJDK 21, automatically

jolta pin 21 writes a .java-version file to the project. From then on, when you invoke java, javac, jar, jshell, and friends, Jolta's shims walk up the directory tree, find the nearest .java-version, and dispatch to the right JDK. Overhead is sub-5ms per call. If the pinned JDK isn't installed yet, Jolta downloads it on first use.

How resolution works

Jolta resolves the version to use in this order:

  1. An explicit environment variable, if set
  2. The project's .java-version file (found by walking up the tree)
  3. Your global default (jolta default 21)
  4. The system JDK

You can also pin a specific distribution when you care:

jolta pin corretto@21
jolta default 21

Why you might pick it (and why you might not)

Where Jolta tries to be different:

  • Automatic, not manual. The whole point is that you don't run a switch command per directory - the shims handle it.
  • Compatible on day one. It reads the same .java-version files as jenv and asdf, and recognizes SDKMAN!'s .sdkmanrc. Migration is mostly "install Jolta and keep your existing files."
  • Single dependency-free Rust binary. One static binary is both the CLI and the shims. Nothing to install into your shell beyond running jolta setup.
  • Windows is first-class, alongside macOS (Apple Silicon + Intel) and Linux (x86_64 + arm64).
  • Multiple distributions: Temurin, Corretto, GraalVM, Oracle, Zulu.

Where the incumbents may still win for you:

  • SDKMAN! manages far more than JDKs (Gradle, Maven, Kotlin, Scala, โ€ฆ). If you want one tool for your whole JVM toolchain, that breadth is real.
  • mise / asdf are polyglot across many languages, so if you also manage Node/Python/Ruby versions you may prefer one manager for everything.
  • Those projects are mature and battle-tested; Jolta is young.

Installation

macOS (Homebrew):

brew install OneAppPlatform/tap/jolta && jolta setup

Linux/macOS (one-liner):

curl -fsSL https://raw.githubusercontent.com/OneAppPlatform/jolta/main/install.sh | sh

Windows: download the executable from the releases page and run jolta setup.

Then:

jolta setup   # install shims + shell config
jolta catalog # browse available versions
jolta pin 21  # pin this project
jolta doctor  # diagnose config issues

Feedback wanted

It's early, and the most useful thing right now is critical feedback from people with real multi-JDK setups. If you try it, I'd love to hear where the resolution logic surprised you, which vendors/distributions you need, and what would have to be true for you to switch from your current setup.

Issues and comments both welcome: github.com/OneAppPlatform/jolta.

Comments

No comments yet. Start the discussion.