JAVA - JENV VS SDKMAN!

GESTIONAR VARIAS VERSIONES DE JAVA SIN VOLVERSE LOCO

Si trabajas con Java el tiempo suficiente, llega un momento en el que un proyecto necesita Java 8, otro Java 17, el nuevo Java 21 y, ya puestos, alguien del equipo quiere probar la última versión. Instalar y desinstalar JDKs a mano, o ir cambiando el JAVA_HOME en el .bashrc cada vez que cambias de proyecto, es de esas cosas que haces dos veces y a la tercera te buscas una herramienta.

Las dos opciones más conocidas son jEnv y SDKMAN!. Yo usé SDKMAN! durante años sin pensarlo demasiado, y en mi última empresa me tocó jEnv, porque era la norma del equipo. Y ya te adelanto que nunca me terminó de gustar. Te cuento las dos, y por qué.

La diferencia de fondo

Aunque parezca que hacen lo mismo, tienen un enfoque distinto:

  • jEnv solo gestiona versiones de Java. No instala nada. Tú instalas los JDKs por tu cuenta (con brew, el gestor de paquetes de tu sistema o descargándolos) y luego le dices a jEnv dónde están.
  • SDKMAN! instala y gestiona. Le pides una versión de Java, se la descarga, la instala y la deja lista para usar. Y no solo Java: también Maven, Gradle, Kotlin, Groovy, Scala, JBang y un buen puñado de herramientas más del mundo JVM.

Esa diferencia, que sobre el papel parece pequeña, es la que marca el día a día.

jEnv

La instalación en macOS, que es donde más se usa, sería algo así:

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

Y ahora viene la parte que menos me gusta. Como jEnv no instala Java, primero lo instalas por otro lado, y después lo registras a mano:

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

Y así con cada versión que quieras usar. Luego, para elegir la versión de un proyecto, te vas a su carpeta y:

jenv local 21

Eso crea un fichero .java-version en el directorio, que puedes subir al repositorio, y a partir de ahí, cada vez que entras en esa carpeta, jEnv usa esa versión. Esto está bien pensado, la verdad.

Lo que no está tan bien pensado es lo de JAVA_HOME. Por defecto, jEnv cambia el java que ejecutas, pero no la variable JAVA_HOME, que es precisamente la que usan Maven, Gradle, el IDE y medio ecosistema para saber qué Java usar. Para eso hay que activar un plugin:

jenv enable-plugin export

Si se te olvida, te encuentras con que java -version te dice una cosa y Maven compila con otra. Y te puedo asegurar que no soy el único que ha perdido un rato con esto: es de lejos la queja más repetida en las incidencias del proyecto.

Súmale que, al registrar un JDK, jEnv te crea varios alias para la misma versión (21, 21.0, openjdk64-21.0.x...), y que cada JDK nuevo que instalas es un paso manual más, y entenderás por qué, en un equipo donde cada uno tenía su máquina montada a su manera, lo de "a mí me funciona" salía más de lo que me gustaría.

SDKMAN!

La instalación es un script:

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

Y a partir de ahí, todo es el comando sdk. Para ver qué versiones de Java hay disponibles, de todos los proveedores (Temurin, Corretto, Zulu, GraalVM...):

sdk list java

Para instalar una, usas el identificador que te aparece en esa lista (los números de versión de este post son de ejemplo, usa los que te salgan a ti):

sdk install java 21.0.8-tem

Y para cambiar de versión, tienes dos opciones:

sdk use java 17.0.16-tem       # solo en la terminal actual
sdk default java 21.0.8-tem    # por defecto para todas

Aquí JAVA_HOME se actualiza solo, sin plugins ni nada. Cambias la versión y todo lo demás se entera.

Para fijar la versión de un proyecto, el equivalente al .java-version de jEnv es el fichero .sdkmanrc:

sdk env init

Eso genera el fichero con la versión que estés usando, y cualquiera que clone el repo solo tiene que hacer sdk env install para que SDKMAN! le instale exactamente lo que el proyecto necesita. Y si quieres que cambie de versión automáticamente al entrar en la carpeta, basta con activar sdkman_auto_env=true en ~/.sdkman/etc/config.

Lo mejor, para mí, es que el mismo fichero puede fijar también la versión de Maven o Gradle:

java=21.0.8-tem
maven=3.9.11

Un único fichero en el repo, un único comando, y todo el equipo trabaja con exactamente las mismas versiones.

¿Siguen vigentes?

Los dos siguen vivos, pero con ritmos muy distintos. SDKMAN! está muy activo, y de hecho está reescribiendo poco a poco sus comandos en Rust para que sean más rápidos. jEnv sigue funcionando, pero a mí me da la impresión de que su mantenimiento es bastante más lento.

Y si trabajas con varios lenguajes a la vez (Java, Node, Python...), hoy también merece la pena echar un ojo a herramientas como mise o asdf, que gestionan versiones de todo con una sola herramienta. Pero si tu mundo es la JVM, SDKMAN! sigue siendo, en mi opinión, la opción más cómoda.

Para cerrar

jEnv no es una mala herramienta: es ligera, hace lo que promete, y si ya tienes los JDKs instalados con brew, encaja bien. Pero cuando la comparas con SDKMAN!, que instala, gestiona, se ocupa del JAVA_HOME y además te trae Maven y Gradle en el mismo paquete, cuesta encontrarle ventajas. Lo que me molestaba no era tanto la herramienta en sí, sino todos los pasos manuales que dejaba en manos de cada uno. Y en un equipo, cada paso manual es una oportunidad para que la máquina de alguien se comporte distinto a la del resto.