Getting Started With a Java Environment on macOS
Tags: beginners, java
Published: 2019-06-28 16:32:33

---
title: Getting Started With a Java Environment on macOS
published: true
description: Is it wrong to admit I have no idea how Java is really installed on my system? 
tags: beginner, java, oop
cover_image: https://images.unsplash.com/photo-1503676260728-1c00da094a0b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1309&q=80
---


Alright, real talk: I finished my first Java project as a like, *totally* professhional developer without having much of a clue about, well, anything to do with the Java build/tooling ecosystem. I pressed enough buttons in IntelliJ for things to run, and (thankfully) they ran, and I spent the rest of the project perpetually swaddled within IntelliJ's gentle embrace. 

Winging it? Nah, I call it *falling with style*. 

Basically, I found comprehending Java tooling seriously perplexing. So before kicking off my second Java project I wanted to forge a better understanding of just what the heck was going on.

Over the course of a few posts we're going to learn a little more about how Java is installed on our local machine before building a straightforward (and only somewhat contrived) `Hello World` application, making use of external libraries and multiple files so we can make some sense of a standard Java directory structure and how to build our application from the command line. Then we'll move on to incorporating the same app with help from Gradle, a popular build tool, and we'll finish by incorporating our project into the IntelliJ IDE. At the end of this series we should all have a much more comfortable knowledge of what goes on under the hood of all our tooling, and way more confidence to tinker with our project builds. 

First steps: the absolute basics. Time for a forensic journey. The first time I quickly fudged my way through installing OpenJDK 12 and I can't even tell you how I did it. Downloading [OpenJDK](https://openjdk.java.net/), as far as I can tell, gives you a directory of unloved binaries which virtually zero guidance on what you're supposed to do with them. I felt like ~~Ted Danson~~ Steve Guttenberg in *Three Men and a Baby*. 

***tl;dr:** copy the uncompressed OpenJDK directory - e.g. `jdk-12.jdk` - into `/Library/Java/JavaVirtualMachines/` and you'll be good.* 

But whyyyy? Let's unmangle that. 

*Caveat*: this is specific to macOS right now. Linux and Windows will inevitably be different, and likely have a swathe of gremlins of their very own. 

### Where is Java actually installed?

Strap in, because descending through an installation of Java is like staring Cthulhu in the face for a fortnight. Descending into its heart of darkness is a guarantee that you're going to end up as Kurtz. You've been warned. 

Let's start at the top: 
```bash
$ java --version
openjdk 12 2019-03-19
OpenJDK Runtime Environment (build 12+33)
OpenJDK 64-Bit Server VM (build 12+33, mixed mode, sharing)
```

And then we can peek inside our `java.home` [system property](https://docs.oracle.com/javase/tutorial/essential/environment/sysprop.html) from within `jshell`.
```bash
jshell> System.getProperty("java.home")
$1 ==> "/Library/Java/JavaVirtualMachines/jdk-12.jdk/Contents/Home"
```

Alright, cool. We've got Java installed. It's the latest version, and it's blissfully not the Oracle JDK. Things are looking rosy. So presumably our shell will point to our `java.home` directory? 
```bash
$ which java
/usr/bin/java
```

OK. But that file is 74 bytes, which if you ask me a tell-tale sign a symbolic link. This ain't our first rodeo! 
```bash
$ greadlink -f $(which java)
/System/Library/Frameworks/JavaVM.framework/Versions/A/Commands/java
```

Well, that's new. And if we have a peep within the `JavaVM.framework` directory we can see all our familiar Java files. 
```bash
$ ls -lh /System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/ | grep "java"
-rwxr-xr-x  1 root  wheel    57K 21 Mar 06:07 java
-rwxr-xr-x  1 root  wheel    74K 21 Mar 06:07 java_home
-rwxr-xr-x  1 root  wheel    57K 21 Mar 06:07 javac
-rwxr-xr-x  1 root  wheel    57K 21 Mar 06:07 javadoc
-rwxr-xr-x  1 root  wheel    57K 21 Mar 06:07 javah
-rwxr-xr-x  1 root  wheel    57K 21 Mar 06:07 javap
-rwxr-xr-x  1 root  wheel    57K 21 Mar 06:07 javapackager
-rwxr-xr-x  1 root  wheel    56K 21 Mar 06:07 javaws
```

These aren't symbolic links, so that's good. Things still don't seem to be lining up, mind. But a little more digging on [Stack Overflow suggests these are stub applications] (https://stackoverflow.com/a/15133344) which determine the version of the JVM to use. Have you ever seen anything like that before? Because I haven't. 

Some more internet trawling reveals `java_home`, which according to its `man` page: 

> returns a path suitable for setting the JAVA_HOME environment variable.  It determines this path from the user's enabled and preferred JVMs in the Java Preferences application.  Additional constraints may be provided to filter the list of JVMs available.  By default,  if  no  constraints match the available list of JVMs, the default order is used.  The path is printed to standard output.

Only I'm fairly sure Apple removed the Java Preferences application yonks ago, but no matter. Did they just sort of half-remove the Java support from the OS, keeping us lightly tethered to an unloved, unmaintained ghost in the machine? That's an open question: I have no idea. I only know we wouldn't be Java developers if we cared for such consistencies. Let's just boot it up. 

```bash
$ /usr/libexec/java_home
/Library/Java/JavaVirtualMachines/jdk-12.jdk/Contents/Home
```

Alright! Fantastic. We finally got to `/Library/Java/JavaVirtualMachines`. I've only lost part of my sanity, so I'll take this as a win. `java_home` seems to prioritise more recent versions of the JDK by default, so if you've got 12 installed it will supersede 11 when you run `java`, `javac`, `jshell` and the like. You can look at all your currently installed versions with the `-V` (capitalised) flag. 
```bash
$ /usr/libexec/java_home -V
Matching Java Virtual Machines (2):
    12, x86_64: "OpenJDK 12"    /Library/Java/JavaVirtualMachines/jdk-12.jdk/Contents/Home
    11, x86_64: "OpenJDK 11"    /Library/Java/JavaVirtualMachines/jdk-11.jdk/Contents/Home

/Library/Java/JavaVirtualMachines/jdk-12.jdk/Contents/Home
```

Meanwhile, the `-v` (lower case) flag will return the path to said JVM, which can then be set as the `JAVA_HOME` environment variable. `JAVA_HOME`, when set, takes precedence over the default ordering of `/usr/libexec/java_home`. 
```bash
$ export JAVA_HOME=$(/usr/libexec/java_home -v 11)
$ java --version
openjdk 11 2018-09-25
OpenJDK Runtime Environment 18.9 (build 11+28)
OpenJDK 64-Bit Server VM 18.9 (build 11+28, mixed mode)
```

Want to relive the halcyon days of JDK 1.6? Go right ahead. Have a blast. No, I'm OK - I'll stick around here, cheers. Of course, running the `export` command in your shell will only last the lifetime of that shell instance. For a more permanent solution you'll need to nip into your `.bashrc`/`.zshrc` or equivalent. 

I'm assuming that the default choices of `/usr/libexec/java_home` mirror what happens when you run one of the stub applications in `/System/Library/Frameworks/JavaVM.framework/`. Or, you might have spotted that there was also a `java_home` binary in the `JavaVM.framework` directory earlier. Wait a second...
```bash
$ greadlink -f /usr/libexec/java_home
/System/Library/Frameworks/JavaVM.framework/Versions/A/Commands/java_home
```
Look, I *did* warn you that it would be messy. But at least now we know a lot more about how Java is running on our system!


***

In the next post we'll start looking at the challenges of compiling and running our first Java application. I'll update this post with a link when it's live! 

Has this post been useful for you? I'd really appreciate any comments and feedback on whether there's anything that could be made clearer or explained better. I'd also massively appreciate any corrections!