# Installing Java on Ubuntu (without breaking your shell)

> 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](https://www.digitalocean.com/community/tutorials/how-to-install-java-with-apt-on-ubuntu-22-04) instead.

## Update Package Manager

Before doing anything, it's a good idea to update our package manager:

```bash
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:

```bash
# 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)

```bash
# 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):

```bash
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:

```bash
# /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:

```bash
# .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...

```bash
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:

```bash
# /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, (titled*The 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:

```bash
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`

<a id="shell-environment-table"></a>

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:

```bash
# 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:

```bash
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.
