Open Liberty and Java 11
The Java community threw application servers quite the curve-ball with Java 9, 10, and 11 updates, but we did it! Open Liberty is now supported on Java 11.
Java 11 was officially released in September 2018, which is the first LTS (long term support) major release since Java 8. Java 9 and 10 were released a while back, but these were not LTS releases. Now that there is a new version of Java that has a decent lifespan, let’s look at the new functionality since Java 8, and what it takes to run Open Liberty apps on Java 11.
New functionality in Java 9/10/11
There is a ton of new stuff in the JDK since Java 8, but here is the stuff that I think is the most exciting from the perspective of an application running on Open Liberty:
-
Flow API (Reactive Streams)
-
Standard HTTP Client
-
Transport Layer Security (TLS) 1.3
-
var for local variables
-
jshell
-
process API enhancements
-
immutable set builders
Flow API (Reactive Streams) - JEP 266
Reactive programming has gained a lot of popularity, especially when used in distributed systems. Libraries like RxJava make this possible in older Java versions, and now the JDK has officially defined a set of interfaces. Specifically, Publisher<T>
, Subscriber<T>
, and Processor<T,R>
.
Here is a great article from Baeldung giving an intro to the Flow API.
Standard HTTP Client - JEP 321
Finally Java has a much needed improvement above the old HttpURLConnection
with a standardized HTTP Client API. You can now invoke HTTP requests without packaging extra libraries such as Apache HTTP Client. Invoking a simple GET request looks like this:
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://openliberty.io"))
.build();
HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
System.out.println(response.body());
Here is a nice blog on DZone that goes into more examples.
Transport Layer Security (TLS) 1.3 - JEP 332
The new TLSv1.3 protocol is a major overhaul of TLS and offers significant security improvements. The Open Liberty security team found a few important compatibility bugs that we were able to get fixed in OpenJ9 JDK 11.0.2, and is the reason why we don’t support 11.0.0 or 11.0.1. Shout out to the OpenJ9 team for getting these important fixes into a release for us!
var for local variables - JEP 286
Java has gotten some flak over the years for being a verbose language. Being type-safe is nice, but if you’re writing some short-lived variables in private helper methods, the repeated declaration of types can get quite tedious. With local variable type inference, a lot of the ceremony of type declaration gets removed.
// Java 6
HashMap<Foo,List<Bar>> barMap = new HashMap<Foo,List<Bar>>();
// Java 7
HashMap<Foo,List<Bar>> barMap = new HashMap<>();
// Java 10
var barMap = new HashMap<Foo,List<Bar>>();
The Java Shell - JEP 222
The Java Shell (a.k.a. jshell
) is a super handy command-line utility that offers a REPL shell with full-blown Java language features and tab completion.
$ jshell jshell> System.out.println("Hello world!") Hello world! jshell> System.getProperty("java.vendor") $1 ==> "Eclipse OpenJ9"
Also, if you’re a Java whiz, it can be used for conference talks in place of slides!
Process API enhancements - JEP 102
The java.lang.Process
API has been enhanced so you can do some really convenient things without dipping into native code such as:
-
Getting detailed process info (pid, start cmd, cpu time, user)
-
List all child processes on the system
-
List child processes of the current process
Immutable Set builders - JEP 269
Initializing a simple fixed collection is something we do very frequently, but it has always been a bit clunky. Now we finally have a nice and concise way to do so.
// Pre-JDK 9
List<String> list = Collections.unmodifiableList(Arrays.asList("one", "two", "three"));
// JDK 9
var list = List.of("one", "two", "three"); // List<String>
var set = Set.of("one", "two", "three"); // Set<String>
var map = Map.of("one", 1, // Map<String,Integer>
"two", 2,
"three", 3);
How to run Open Liberty on Java 11
First of all, if you don’t have a copy of it already, you will need to download Java 11.
Currently Open Liberty supports Java 11.0.2 (or newer) on the OpenJ9 VM. Other Java VMs, such as Hotspot, are not supported due to some outstanding VM bugs. |
Next, you will need Open Liberty 19.0.0.1 or newer.
If you are using Maven, here are the coordinates:
<dependency>
<groupId>io.openliberty</groupId>
<artifactId>openliberty-runtime</artifactId>
<version>19.0.0.1</version>
<type>zip</type>
</dependency>
Or for Gradle:
dependencies {
libertyRuntime group: 'io.openliberty', name: 'openliberty-runtime', version: '[19.0.0.1,)'
}
Or you can download OpenLiberty directly here.
There are a number of ways to control the Java executable used by the Open Liberty server. The following list describes the order of precedence:
-
JAVA_HOME
in${server.config.dir}/server.env
-
JAVA_HOME
in${wlp.install.dir}/etc/server.env
-
JAVA_HOME
set in the environment -
searching the
PATH
in the environment
To confirm that Open Liberty is using the intended Java level, you can check the first line of console output:
$ export JAVA_HOME=/path/to/java11 $ export PATH=$JAVA_HOME/bin:$PATH $ bin/server run myServer Launching myServer (WebSphere Application Server 19.0.0.1/wlp-1.0.24.201901191232) on Eclipse OpenJ9 VM, version 11.0.2 (en_US) [AUDIT ] CWWKE0001I: The server myServer has been launched. ...
Run a simple REST application on Java 11
To run an application on Open Liberty that uses Java 11, we can start by defining the new compiler compliance level in your pom.xml
file if using Maven:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>11</release>
</configuration>
</plugin>
Or, if you are using Gradle, you can set it in the build.gradle
file like so:
sourceCompatibility = 11
targetCompatibility = 11
It is not required to set a compiler compliance level of Java 11 in order to run an existing application on Java 11, but in order to leverage any of the new language features you will need to do so. |
Clone this sample project and run it using Gradle:
$ git clone [email protected]:aguibert/basic-liberty.git $ git checkout java11-blog $ ./gradlew libertyStart
Then open a browser to http://localhost:8080/basic-liberty-1.0-SNAPSHOT/ and you should see the following output:
Hello world The java runtime is version: 11
To stop the server, run the command: ./gradlew libertyStop
Java 11 changes that impact Open Liberty
There have been a lot of good enhancements to Java between Java 8 and 11, such as local variables and the Flow API, but there are a few changes that are likely to cause problems for existing applications.
Removal of Java EE and CORBA APIs from the JDK
In Java 11, several modules (groupings of Java packages) were removed from the JDK. Your applications may have been relying on some of these removed packages, but the good news is that all of the removed packages are provided by existing Open Liberty features. Here is a table that shows what Open Liberty features to enable for each removed package:
Removed Module | Removed Package(s) | Equivalent Open Liberty Feature |
---|---|---|
java.activation |
javax.activation |
jaxb-2.2 |
java.corba |
javax.activity javax.rmi javax.rmi.CORBA org.omg.* |
ejbRemote-3.2 |
java.transaction |
javax.transaction |
jdbc-4.X features |
java.xml.bind |
javax.xml.bind.* |
jaxb-2.2 |
java.xml.ws.annotation |
javax.annotation |
Most Java EE 7 or 8 features |
java.xml.ws |
javax.jws.* javax.xml.soap javax.xml.ws.* |
jaxws-2.2 |
Introduction of the Java Platform Module System (JPMS)
In Java 9, one of the biggest changes to Java was delivered, under JEP 261: The Module System. This introduces a new Java construct called a "module" which is essentially an archive that is a collection of Java packages, with a module-info.class
descriptor at the root of the archive. Using the module-info
descriptor, a module can define what packages it exposes, what services it provides, what services it requires, and what modules it requires.
Here is an example of what a module-info.java
might look like:
module com.foo {
// Declare what packages are exported from this module
exports com.foo.api;
exports com.foo.spi to com.foo.othermodule;
// Declare a package as "open" so that other modules can perform deep reflection into it
// This is useful for CDI injection or JPA entity weaving
opens com.foo.internal.jpa.entities;
// Declare what modules this module requires
// By default, every module automatically requires the 'java.base' module
requires java.sql;
requires java.xml;
// Declare used and provided services
uses java.sql.Driver;
provides com.foo.api.FooService
with com.foo.internal.FooServiceImpl;
}
The JDK itself is now organized into Java modules, and it looks something like this:
The intent is for the entire Java ecosystem to eventually organize itself into Java modules, but for now Java modules are not defined in the Java EE/Jakarta EE/Eclipse MicroProfile space. While you can technically include a Java module in your Open Liberty application, the module-info descriptor will have no effect at the moment.
JPMS Access Restrictions
While JPMS does introduce some interesting architectural capabilities and brings a long-needed modularity system into the JDK, it does so at the expense of several popular Java technologies, namely CDI and JPA. The reason for this is that Java modules cannot access members of non-exported packages from other Java modules, and Java code may no longer perform deep reflection (i.e. things that require setAccessible(true)
) on members of Java modules. If either of these rules are violated, an IllegalAccessException
is thrown.
Since this is such a major compatibility issue for most of the Java ecosystem, a "kill switch" (formally known as the --illegal-access=permit
JVM option) has been introduced. It is enabled by default and allows code in unnamed modules to access and perform deep reflection on all named modules. It is important to note that eventually the kill switch will not be enabled by default, and later it will be removed entirely, so it is best to stop writing code that uses deep reflection and start migrating to alternative approaches.
Useful references for Java 11 migration
While this blog post focused mainly on Java 11 migration within the context of Liberty, there are many great resources out there for generic Java 11 migration, as well as Java 11 feature highlights. To name a few: