Java Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

1. Introduction

In this tutorial, we’re going to talk about an exception that Maven throws when misconfigured: JAVA_HOME should point to a JDK, not a JRE.

Maven is a powerful tool for building code. We’ll look under the hood to understand why this error happens and will see how to solve it.

2. The JAVA_HOME Problem

After installing Maven, we have to set the JAVA_HOME environment variable, so the tool knows where to find the JDK commands to execute. Maven goals run the appropriate Java commands against the project’s source code.

For example, the most common scenario is to compile the code by executing the javac command.

If JAVA_HOME is not pointing to a valid JDK installation, Maven will throw an error at every execution:

mvn -version

# Output... 
The JAVA_HOME environment variable is not defined correctly
This environment variable is needed to run this program
NB: JAVA_HOME should point to a JDK, not a JRE

3. JDK or JRE

How does Maven verify the JAVA_HOME path?

Before running any goals, Maven checks for the existence of the java command in the path specified by JAVA_HOME or by asking the OS for a default JDK installation. If the executable is not found, Maven terminates with the error.

Here is the mvn executable check for Linux (Apache Maven v3.5.4):

if [ -z "$JAVA_HOME" ] ; then
    JAVACMD=`which java`
else
    JAVACMD="$JAVA_HOME/bin/java"
fi
 
if [ ! -x "$JAVACMD" ] ; then
    echo "The JAVA_HOME environment variable is not defined correctly" >&2
    echo "This environment variable is needed to run this program" >&2
    echo "NB: JAVA_HOME should point to a JDK not a JRE" >&2
    exit 1
fi

This check may seem reasonable at first glance, but we have to consider that both JDK and JRE have a bin folder and both contain an executable java file.

Therefore, it’s possible to configure JAVA_HOME to point to a JRE installation, hiding this particular error and causing problems at a later stage. While the JRE’s main purpose is to run Java code, the JDK can also compile and debug among other important differences.

For this reason, an mvn compile command would fail – as we’ll see in the next subsection.

3.1. Compilation Failure because of JRE instead of JDK

As usual with default settings, they’re very helpful if we have a “standard” configuration.

For example, if we install Java 11 on an Ubuntu 18.04 system and don’t set the JAVA_HOME environment variable, Maven will still happily find our JDK and use it for the different goals, including compiling.

But if we’ve managed to set up a non-standard configuration (let alone made a mess) on our system, Maven’s helpfulness doesn’t suffice anymore. It can be even misleading.

Let’s assume we have the following configuration on Ubuntu 18.04:

  • JDK 11
  • JRE 8
  • JAVA_HOME set to the path of the JRE 8 installation

If we do our basic check:

mvn --version

We’ll get a meaningful output like this:

Apache Maven 3.6.0 (97c98ec64a1fdfee7767ce5ffb20918da4f719f3; 2018-10-24T18:41:47Z)
Maven home: /opt/apache-maven-3.6.0
Java version: 1.8.0_191, vendor: Oracle Corporation, runtime: /usr/lib/jvm/java-8-openjdk-amd64/jre
Default locale: en, platform encoding: UTF-8
OS name: "linux", version: "4.15.0-42-generic", arch: "amd64", family: "unix"

Let’s see what happens if we try to compile a project:

mvn clean compile

Now, we get an error:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.7.0:compile (default-compile) on project my-app: Compilation failure
[ERROR] No compiler is provided in this environment. Perhaps you are running on a JRE rather than a JDK?

3.2. Fixing the Compilation Error on the Project Level

As in many other cases with Maven, it’s recommended to set up meaningful system-wide settings – in this case, we’ll change the value of the JAVA_HOME variable as described in section 5 to point to the JDK instead of the JRE.

However, if we can’t set up defaults for whatever reason, we can still override the settings on the project level. Let’s see how to do that.

First, we’ll open the pom.xml of our project, go to the section build / pluginManagement / plugins and have a look at the entry for the maven-compiler-plugin:

<plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.7.0</version>
</plugin>

Then, we’ll add a configuration to it so that it uses a custom executable and skip searching for javac in the JAVA_HOME/bin directory:

<plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.7.0</version>
    <configuration>
        <fork>true</fork>
        <executable>/usr/lib/jvm/java-11-openjdk-amd64/bin/javac</executable>
    </configuration>
</plugin>

On Unix systems, this executable should be a script with adequate permission. On Windows systems, it should be an .exe file.

Next, we’ll try to compile the project again:

mvn clean compile

Now, the build – including the compile phase with the maven-compiler-plugin – is successful.

4. Checking the JAVA_HOME Configuration

It’s pretty simple to check if JAVA_HOME points to an actual JDK. We can either print its content in a terminal, or we can run one of the following shell commands:

4.1. Checking JAVA_HOME on Linux

Just open a terminal and type:

> $JAVA_HOME/bin/javac -version

If JAVA_HOME points to a JDK, the output should look like:

> javac 1.X.0_XX

If JAVA_HOME doesn’t point to a JDK, the OS will throw an error message:

> bash: /bin/javac: No such file or directory

4.2. Checking JAVA_HOME on Windows

Open a command prompt and type:

%JAVA_HOME%\bin\javac -version

If JAVA_HOME points to a JDK, the output should look like:

> javac 1.X.0_XX

If JAVA_HOME doesn’t point to a JDK, the OS will throw an error message:

> The system cannot find the path specified.

5. How to Solve the Issue

First of all, we need to know where to find our JDK:

  • If we installed our JDK distribution using a package installer, we should be able to find the path using the OS search utility
  • If the distribution was a portable one, let’s check the folder where we extracted it

Once we know the path to the JDK, we can set our JAVA_HOME environment variable, using the appropriate results for our particular OS.

6. Conclusion

In this brief tutorial, we’ve discussed the “JAVA_HOME should point to a JDK not a JRE” Maven error and examined its root cause.

Finally, we discussed how to check your JAVA_HOME environment variable, and how to make sure it points to a JDK.

Java bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE