Java 9 – First baby steps with Modules and jlink image 91

Java 9 – First baby steps with Modules and jlink

In a recent article, I created an isolated Docker Container as Java 9 R&D environment: https://technology.amis.nl/2017/10/11/quick-and-clean-start-with-java-9-running-docker-container-in-virtualbox-vm-on-windows-10-courtesy-of-vagrant/. In this article, I will use that environment to take few small steps with Java 9 – in particular with modules. Note:this story does not end well. I wanted to conclude with using jlink to create a stand alone runtime that contained both the required JDK modules and my own module – and demonstrate how small that runtime was. Unfortunately, the Link step failed for me. More news on that in a later article.

Create Custom Module

Start a container based on the openjdk:9 image, exposing its port 80 on the docker host machine and mapping folder /vagrant (mapped from my Windows host to the Docker Host VirtualBox Ubuntu image) to /var/www inside the container:

docker run -it -p 127.0.0.1:8080:80 -v /vagrant:/var/www openjdk:9 /bin/sh

Create Java application with custom module:  I create a single Module (nl.amis.j9demo) and a single class nl.amis.j9demo.MyDemo. The module depends directly on one JDK module (httpserver) and indirectly on several more.

imageThe root directory for the module has the same fully qualified name as the module: nl.amis.j9demo.

This directory contains the module-info.java file. This file specifies:

  • which modules this module depends on
  • which packages it exports (for other modules to create dependencies on)

In my example, the file is very simple – only specifying a dependency on jdk.httpserver:

image

The Java Class MyDemo has a number of imports. Many are for base classes from the java.base module. Note: every Java module has a implicit dependency on java.base, so we do not need to include it in the modue-info.java file.

image

This code create an instance of HttpServer – an object that listens for HTTP Requests at the specified port (80 in this case) and then always returns the same response (the string “This is the response”). As meaningless as that is – the notion of receiving and replying to HTTP Requests in just few lines of Java Code (running in the OpenJDK!) is quite powerful.

package nl.amis.j9demo;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.*;
import com.sun.net.httpserver.*;

import static java.lang.System.out;
import static java.net.HttpURLConnection.*;

public class MyDemo{
  private static final int DEFAULT_PORT = 80;
  private static URI ROOT_PATH = URI.create("/"); 
           

private static class MyHandler implements HttpHandler {
       public void handle(HttpExchange t) throws IOException {
           URI tu = t.getRequestURI();
           InputStream is = t.getRequestBody();
           // .. read the request body
           String response = "This is the response";
           t.sendResponseHeaders(200, response.length());
           OutputStream os = t.getResponseBody();
           os.write(response.getBytes());
           os.close();
       }
   }


  public static void main(String[] args) throws IOException {
    HttpServer server = HttpServer.create(new InetSocketAddress(DEFAULT_PORT), 0);
    server.createContext("/apps ", new MyHandler());
    server.setExecutor(null); // creates a default executor
    server.start();
    out.println("HttpServer is started, listening at port "+DEFAULT_PORT);
  }

}

Compile, Build and Run

Compile the custom module:

javac -d mods –module-source-path src -m nl.amis.j9demo

image

Create destination directory for JAR file

mkdir -p lib

Create the JAR for the module:

jar –create –file lib/nl-amis-j9demo.jar –main-class nl.amis.j9demo.MyDemo -C mods/nl.amis.j9demo .

image

Inspect the JAR file:

jar tvf lib/nl-amis-j9demo.jar

image

To run the Java application- with a reference to the module:

java –p lib/ -m nl.amis.j9demo

image

the traditional equivalent with a classpath for the JAR file(s) would be:

java -classpath lib/nl-amis-j9demo.jar nl.amis.j9demo.MyDemo

Because port 80 in the container was exposed and mapped to port 8080 on the Docker Host, we can access the Java application from the Docker Host, using wget:

wget 127.0.0.1:8080/apps

image

The response from the Java application is hardly meaningful However, the fact that we get a response at all is quite something: the ‘remote’  container based on openjdk:9 has published an HTTP server from our custom module that we can access from the Docker Host with a simple HTTP request.

Jlink

I tried to use jlink – to create a special runtime for my demo app, consisting of required parts of JDK and my own module. I expect this runtime to be really small.

The JVM modules by the way on my Docker Container are in /docker-java-home/jmods

image

The command for this:

jlink –output mydemo-runtime –module-path lib:/docker-java-home/jmods –limit-modules nl.amis.j9demo –add-modules nl.amis.j9demo –launcher demorun=nl.amis.j9demo –compress=2 –no-header-files –strip-debug

Unfortunately, on my OpenJDK:9 Docker Image, linking failed with this error:

image

Error: java.io.UncheckedIOException: java.nio.file.FileSystemException: mydemo-runtime/legal/jdk.httpserver/ASSEMBLY_EXCEPTION: Protocol error

Resources

Documentation for jlink – https://docs.oracle.com/javase/9/tools/jlink.htm

JavaDoc for HttpServer package – https://docs.oracle.com/javase/9/docs/api/com/sun/net/httpserver/package-summary.html#

Java9 Modularity Part 1 (article on Medium by Chandrakala) – https://medium.com/@chandra25ms/java9-modularity-part1-a102d85e9676

JavaOne 2017 Keynote – Mark Reynolds demoing jlink – https://youtu.be/UNg9lmk60sg?t=1h35m43s

Exploring Java 9 Modularity – https://www.polidea.com/blog/Exploring-Java-9-Java-Platform-Module-System/