Migrate Java microservices from Java 8 to Java 11

Introduction

Some people might have struggled to efficiently migrate their Java microservices from 8 to 11+.
There are 2 main reasons you’d want to do that:

  • Java 8 end of line coming soon ;
  • Java 9+ modules that help build “slimmer” JVM.

Use Case

Now, if you simply switch from Java 8 to Java 11 you’ll end up with microservices that will be 50% bigger in size and resource consumption, if you use the Maven plugin to build your microservice, that is. Not really slim…
Why is that? Actually the Maven plugin uses a default Dockerfile that will install the full JDK, whatever the version of Java you’re using. Meaning if you use Java 9+, you’ll end up with a docker image that contains all the java modules, even those that you don’t need…
You’re going to need a custom Dockerfile to take full advantage of the modules system of Java 11 that’ll use jlink to build a custom JVM that will include only the modules you need.
Here’s a sample dockerfile, that should fit for almost everyone (just change the project name in the COPY command):

FROM alpine:3 as base
RUN apk add  --no-cache openjdk@package.java-version@
RUN jlink \
--module-path /opt/java/jmods \
--compress=2 \
--add-modules java.se,jdk.unsupported,jdk.crypto.ec \
--no-header-files \
--no-man-pages \
--output /opt/jdk-mini
FROM alpine:3
COPY --from=base /opt/jdk-mini /opt/jdk-mini
RUN apk add  --no-cache coreutils
ENV JAVA_HOME=/opt/jdk-mini
ENV PATH="$PATH:$JAVA_HOME/bin"
COPY etc/ /etc/@package.directory@/
ADD resources/* /data/
RUN chmod +x /data/entrypoint.sh
ENTRYPOINT /data/entrypoint.sh

Next question is: where to put this Dockerfile so that the Maven plugin will use it instead of the default one?
Pretty easy: just put it in src/main/docker/Dockerfile.
That’s it!
Regarding the dependencies: why java.se and jdk.unsupported?
That’s because of Spring (and Spring Boot) which is a hard dependency of Cumulocity Java SDK.
Spring depends on java.beans, which requires java.desktop, which itself includes… java.awt. And many other dependencies are also required. In the end including java.se is simpler and doesn’t cost much (maybe 1 or 2 MB compared to a better filtered JVM).
jdk.unsupported is also required by Spring.
jdk.crypto.ec is only necessary if your microservice needs to connect to a URL using TLS.

Hope that will help!

Useful resources

If you want more info regarding java modules and how to use them efficiently, here’s a great Devoxx UK session on Youtube (this helped me figure out that I needed jdk.unsupported module):

There’s also an interesting discussion regarding Spring dependencies on java.desktop:

Hopefully Spring 6 might change that…

1 Like