TL;DR -- Different shells store environment variables in different locations. Set environment variables using your shell's recommended location.
There are plenty of resources out there for installing Java on Ubuntu, but I wanted to write one that covers the exact issue I ran into when installing Java with zsh
.
This post focuses on the process for installing Java with zsh
as compared to installing Java with bash
. If you've no interest in zsh
and just want to know how to bang it out with bash
, I suggest reading this Digital Ocean post instead.
Update Package Manager
Before doing anything, it's a good idea to update our package manager:
sudo apt update
sudo apt upgrade
With that out of the way, the first thing I like to do is clear out any existing Java installations before making any other changes.
Check the existing Java installation
We can start by seeing what our existing Java setup looks like by issuing a command:
# WSL
java -version # press enter
# output
openjdk version "17.0.5" 2022-10-18
OpenJDK Runtime Environment (build 17.0.5+8-Ubuntu-2ubuntu122.04)
OpenJDK 64-Bit Server VM (build 17.0.5+8-Ubuntu-2ubuntu122.04, mixed mode, sharing)
Remove existing Java installation
You may or may not want to remove an existing Java installation. I prefer to start fresh, just in case.
In the previous step, I saw that I had the OpenJDK packages installed for Java, so I proceed with removing them.
Note: the
comments
add context (actions such as tab completions, saving files, re-opening the terminal)
# WSL
sudo apt remove --autoremove openjdk- # press tab repeteadly
# installed packages should be listed with tab completion
# after pressing tab, it autocompletes to:
sudo apt remove --autoremove openjdk-17-jre-headless:amd64 # press enter
# if more than one package appeared in the previous step, make sure to remove all of them
# source the shell (or close and re-open your terminal application)
source ~/.zshrc
# confirm Java is no longer installed
java -version # press enter
# output
zsh: command not found: java
Install desired Java version
Now we can install the appropriate version of Java (more specifically, the JDK). In my case, it's version 17
.
So, we can just add 17
to openjdk-
in the command below (replace 17
with whatever version you need to install):
sudo apt install openjdk-17 # press tab to see packages listed
# output
openjdk-17-dbg openjdk-17-jdk openjdk-17-jre-headless
openjdk-17-demo openjdk-17-jdk-headless openjdk-17-jre-zero
openjdk-17-doc openjdk-17-jre openjdk-17-source
# we want a headless JDK, we so end up with:
sudo apt install openjdk-17-jdk-headless
# source the shell or restart the terminal
source ~/.zshrc
# confirm Java installation
java -version # press enter
# output
openjdk version "17.0.7" 2023-04-18
OpenJDK Runtime Environment (build 17.0.7+7-Ubuntu-0ubuntu122.04.2)
OpenJDK 64-Bit Server VM (build 17.0.7+7-Ubuntu-0ubuntu122.04.2, mixed mode, sharing)
Set the $JAVA_HOME
environment variable
We're not quite done yet, though. We need to set the $JAVA_HOME
environment variable.
I'll first describe the way I initially tried it so that I can illustrate how much more straightforward the correct way is.
The wrong way
This is where I got thrown off by zsh
. I didn't realize that zsh
handles environment variables differently than bash
.
I made the mistake of adding JAVA_HOME
to my /etc/environment
file (the bash
way). The result went something like this:
# /etc/environment
JAVA_HOME="/path/to/java/home"
# save and exit /etc/environment
# source the file
source /etc/environment
echo $JAVA_HOME # outputs /path/to/java/home, yay!
# close terminal
# open new terminal
echo $JAVA_HOME # no output, uhh.. wtf?
At this point, I thought I would get clever and source /etc/environment
within my .zshrc
It almost worked:
# .zshrc
source /etc/environment
# save and exit .zshrc
# close and re-open terminal
echo $JAVA_HOME # success!
So far so good, but when I try to open a project with VS Code from the terminal...
code . # presss enter
zsh: command not found: code
GRRR!
I decide to get clever once again, and add my entire $PATH
variable to my /etc/environment
file:
# /etc/environment
PATH="/home/this/path:/home/that/path:/another/path:/vs/code/path"
JAVA_HOME="/java/home/path"
This solution actually worked, but it felt so hacky. I just knew there had to be a better way...
The right way
Before we jump in, note that the configuration has been reset from the previous section, (titledThe wrong way):
source /etc/environment
removed from.zshrc
JAVA_HOME
variable deleted from/etc/environment
PATH
deleted from/etc/environment
the terminal was session closed and re-opened
Now we can go about setting JAVA_HOME
properly:
echo $JAVA_HOME # press enter
# no output
# find the Java install path
sudo update-alternatives --config java # press enter
# output
There is only one alternative in link group java (providing /usr/bin/java):
/usr/lib/jvm/java-17-openjdk-amd64/bin/java
Nothing to configure.
From here we can copy the path that was output: /usr/lib/jvm/java-17-openjdk-amd64/bin/java
The place you put that path will depend on a few factors:
which shell you're using
whether this Java configuration is being set at the profile level or the system level
bash | zsh | |
profile-level | ~/.bashrc | ~/.zshenv |
system-level | /etc/environment | /etc/zsh/zshenv |
Luckily, what we put in the file is the same, regardless of scope or shell:
# at the bottom of the appropriate file from the table above
JAVA_HOME="/usr/lib/jvm/java-17-openjdk-amd64/bin/java"
Save and exit the file, then close and re-open the terminal.
Now we can see that JAVA_HOME
is set if we echo it out:
echo $JAVA_HOME
# output
/usr/lib/jvm/java-17-openjdk-amd64/bin/java
That's it! Your Java configuration is ready to go, and your shell commands remain intact.