Migrating to Java 9
Modules
| Paul Bakker
Why care about modules?
lib/nebula-4.0.12.jar:lib/netflix-gradle-lint-8.6.1.jar:lib/gretty-2.0.0.jar:lib/gradle-infamous-plugin-1.28.jar:lib/java-semver-0.9.0.jar:lib/guava-20.0.jar:lib/
nebula-core-4.0.1.jar:lib/commons-lang-2.6.jar:lib/joda-time-2.9.9.jar:lib/kotlin-reflect-1.1.4-3.jar:lib/nebula-common-4.0.12.jar:lib/nebula-repos-4.0.12.jar:lib/
nebula-publish-4.0.12.jar:lib/httpclient-4.5.3.jar:lib/jgrapht-core-0.9.2.jar:lib/build-info-extractor-gradle-4.1.1.jar:lib/ant-jsch-1.9.9.jar:lib/rest-api-client-
core-1.9.0.jar:lib/rest-api-client-httpclient-1.9.0.jar:lib/bakery-api-client-1.8.0.jar:lib/odin-2.0.0.jar:lib/artifactory-java-client-services-1.2.2.jar:lib/
kotlin-stdlib-jre8-1.1.4-3.jar:lib/gradle-contacts-plugin-3.0.1.jar:lib/gradle-dependency-lock-plugin-4.9.4.jar:lib/gradle-extra-configurations-
plugin-3.2.0.jar:lib/gradle-git-scm-plugin-3.0.1.jar:lib/gradle-info-plugin-3.6.0.jar:lib/gradle-java-cross-compile-plugin-0.9.0.jar:lib/gradle-lint-
plugin-8.3.1.jar:lib/gradle-metrics-plugin-6.0.0.jar:lib/gradle-ospackage-plugin-4.4.0.jar:lib/gradle-override-plugin-3.0.2.jar:lib/gradle-resolution-rules-
plugin-5.0.2.jar:lib/gradle-scm-plugin-3.0.1.jar:lib/gradle-stash-plugin-4.0.1.jar:lib/nebula-dependency-base-plugin-0.3.0.jar:lib/nebula-dependency-
recommender-5.0.0.jar:lib/nebula-grails-plugin-1.0.0.jar:lib/nebula-project-plugin-3.4.0.jar:lib/nebula-release-plugin-6.0.1.jar:lib/gradle-release-1.2.jar:lib/
gradle-cobertura-plugin-2.5.0.jar:lib/gretty-core-2.0.0.jar:lib/spring-boot-loader-tools-1.5.4.RELEASE.jar:lib/jetty-util-8.1.8.v20121106.jar:lib/httpclient-
cache-4.5.2.jar:lib/kotlin-stdlib-1.1.4-3.jar:lib/metatron-decrypt-1.108.0.jar:lib/nebula-publishing-plugin-5.1.3.jar:lib/httpcore-4.4.6.jar:lib/commons-
logging-1.2.jar:lib/commons-codec-1.9.jar:lib/ivy-2.2.0.jar:lib/build-info-extractor-2.5.5.jar:lib/ant-1.9.9.jar:lib/mail-1.4.4.jar:lib/frigga-0.13.jar:lib/
nekohtml-1.9.17.jar:lib/artifactory-java-client-api-1.2.2.jar:lib/groovy-xml-2.3.2.jar:lib/jcl-over-slf4j-1.7.2.jar:lib/kotlin-stdlib-jre7-1.1.4-3.jar:lib/
svnkit-1.8.12.jar:lib/asm-5.2.jar:lib/gpars-1.2.1.jar:lib/junit-4.12.jar:lib/jackson-datatype-joda-2.3.2.jar:lib/logstash-logback-encoder-4.5.1.jar:lib/
jest-0.1.7.jar:lib/fluent-hc-4.5.1.jar:lib/redline-1.2.5.jar:lib/jdeb-1.4.jar:lib/gradle-docker-plugin-3.0.1.jar:lib/commons-beanutils-core-1.8.3.jar:lib/jackson-
module-kotlin-2.7.5.jar:lib/maven-model-builder-3.5.0.jar:lib/grails-launcher-1.1.jar:lib/groovy-2.4.11.jar:lib/commons-cli-1.2.jar:lib/commons-
configuration-1.10.jar:lib/org.apache.servicemix.bundles.bcprov-jdk16-1.46_3.jar:lib/spring-boot-devtools-1.3.3.RELEASE.jar:lib/spring-core-4.3.9.RELEASE.jar:lib/
annotations-13.0.jar:lib/gson-2.8.0.jar:lib/protobuf-java-util-3.2.0.jar:lib/protobuf-java-3.2.0.jar:lib/grpc-core-1.3.0.jar:lib/javax.inject-1.jar:lib/metatron-
common-1.108.0.jar:lib/metatron-ipc-common-1.108.0.jar:lib/build-info-client-2.5.5.jar:lib/xstream-1.3.1.jar:lib/ant-launcher-1.9.9.jar:lib/multiverse-
core-0.7.0.jar:lib/jsr166y-1.7.0.jar:lib/hamcrest-core-1.3.jar:lib/logback-core-1.1.3.jar:lib/jest-common-0.1.7.jar:lib/httpcore-nio-4.4.1.jar:lib/
httpasyncclient-4.1.jar:lib/plexus-utils-3.0.24.jar:lib/plexus-interpolation-1.24.jar:lib/plexus-component-annotations-1.7.1.jar:lib/maven-model-3.5.0.jar:lib/
maven-artifact-3.5.0.jar:lib/maven-builder-support-3.5.0.jar:lib/spring-boot-1.3.3.RELEASE.jar:lib/spring-boot-autoconfigure-1.3.3.RELEASE.jar:lib/
error_prone_annotations-2.0.19.jar:lib/jsr305-3.0.0.jar:lib/grpc-context-1.3.0.jar:lib/instrumentation-api-0.3.0.jar:lib/grpc-protobuf-1.3.0.jar:lib/grpc-
stub-1.3.0.jar:lib/jackson-mapper-asl-1.9.12.jar:lib/build-info-api-2.5.5.jar:lib/xpp3_min-1.1.4c.jar:lib/spring-context-4.2.5.RELEASE.jar:lib/grpc-google-common-
protos-0.1.6.jar:lib/grpc-protobuf-lite-1.3.0.jar:lib/jackson-core-asl-1.9.12.jar:lib/spring-aop-4.2.5.RELEASE.jar:lib/spring-beans-4.2.5.RELEASE.jar:lib/spring-
expression-4.2.5.RELEASE.jar:lib/aopalliance-1.0.jar:lib/http-builder-0.7.1.jar:lib/json-lib-2.3-jdk15.jar:lib/xml-resolver-1.2.jar:lib/commons-
beanutils-1.8.0.jar:lib/commons-collections-3.2.1.jar:lib/ezmorph-1.0.6.jar:lib/slf4j-api-1.7.25.jar:lib/commons-io-2.5.jar:lib/jackson-annotations-2.7.5.jar:lib/
jackson-databind-2.7.5.jar:lib/nebula-gradle-interop-0.5.0.jar:lib/org.eclipse.jgit.ui-4.6.1.201703071140-r.jar:lib/jsch.agentproxy.jsch-0.0.9.jar:lib/
jsch.agentproxy.pageant-0.0.9.jar:lib/jsch.agentproxy.sshagent-0.0.9.jar:lib/jsch.agentproxy.usocket-jna-0.0.9.jar:lib/jsch.agentproxy.usocket-nc-0.0.9.jar:lib/
jsch.agentproxy.core-0.0.9.jar:lib/jna-4.1.0.jar:lib/jna-platform-4.1.0.jar:lib/p4java-2015.2.1365273.jar:lib/jzlib-1.1.2.jar:lib/jsch.agentproxy.svnkit-trilead-
ssh2-0.0.7.jar:lib/trilead-ssh2-1.0.0-build220.jar:lib/jsch.agentproxy.connector-factory-0.0.7.jar:lib/sequence-library-1.0.3.jar:lib/sqljet-1.1.10.jar:lib/antlr-
runtime-3.4.jar:lib/commons-lang3-3.5.jar:lib/gradle-git-1.7.2.jar:lib/groovy-json-2.4.11.jar:lib/commons-compress-1.8.jar:lib/bcpg-jdk15on-1.51.jar:lib/bcprov-
jdk15on-1.51.jar:lib/xercesImpl-2.9.1.jar:lib/jackson-core-2.7.5.jar:lib/org.eclipse.jgit-4.6.1.201703071140-r.jar:lib/JavaEWAH-1.1.6.jar:lib/jsch-0.1.54.jar:lib/
grgit-1.9.3.jar:lib/xz-1.5.jar
Module primer.
module easytext.analysis {
module easytext.cli { exports analysis.api;
requires easytext.analysis; opens impl;
} }
easytext.cli easytext.analysis
analysis.api
impl
reflection only!
other
Module primer.
module easytext.analysis {
module easytext.cli { exports
Modules defineanalysis.api;
dependencies
requires easytext.analysis; opensexplicitly
impl;
} }
easytext.cli easytext.analysis
analysis.api
impl
reflection only!
other
Module primer.
module easytext.analysis {
module easytext.cli { exports analysis.api;
requires easytext.analysis; opens impl;
Packages are encapsulated by
} }
default
easytext.cli easytext.analysis
analysis.api
impl
reflection only!
other
Module primer.
module easytext.analysis {
module easytext.cli { exports analysis.api;
requires Packages
easytext.analysis;
can be “opened” for opens impl;
} deep reflection at run-time }
easytext.cli easytext.analysis
analysis.api
impl
reflection only!
other
Migrating to Java 9.
Java 8
java -cp … -jar
MyApp.jar
Java 9
java -cp … -jar
MyApp.jar
Our journey to modules.
Running on Java 9
Using existing libraries
Allowing reflection
Spring / Hibernate case study
Refactoring to increase modularity
Migrating builds
!"" lib
!"" run.sh
#"" src
!"" books
$ !"" api
$ $ !"" entities
$ $ $ #"" Book.java
$ $ #"" service
$ $ #"" BooksService.java
$ #"" impl
$ !"" entities
$ $ #"" BookEntity.java
$ #"" service
$ #"" HibernateBooksService.java
!"" bookstore
$ !"" api
$ $ #"" service
$ $ #"" BookstoreService.java
javac -cp [list of JARs in lib]
$ #"" impl -d out -sourcepath src $(find src -name '*.java')
$ #"" service
$ #"" BookstoreServiceImpl.java cp $(find src -name '*.xml') out
!"" log4j2.xml
!"" main java -cp [list of JARs in lib]:out main.Main
$ #"" Main.java
#"" main.xml
Missing platform libraries.
import javax.xml.bind.DatatypeConverter;
public class Main {
public static void main(String... args) {
DatatypeConverter.parseBase64Binary("SGVsbG8gd29ybGQh");
}
}
java.lang.ClassNotFoundException: javax.xml.bind.JAXBException
The platform module graph.
java.se vs java.se.ee
Resolving JAXB.
java --add-modules java.xml.bind …
Illegal deep reflection.
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by
javassist.util.proxy.SecurityActions …
WARNING: Please consider reporting this to the maintainers of
javassist.util.proxy.SecurityActions
WARNING: Use --illegal-access=warn to enable warnings of further
illegal reflective access operations
WARNING: All illegal access operations will be denied in a future
release
Type Compile time Reflection on public Deep reflection
Exports
✓ ✓ ✘
Open
✘ ✓ ✓
Exports + Open
✓ ✓ ✓
Fixing illegal deep reflection.
Use a library that doesn’t do this!
do you really need deep reflection on JDK types!?
Use command line flags to open packages
java --add-opens java.base/java.lang=ALL-UNNAMED Main
Automatic
Modules
But we’re still on the classpath!
Let’s migrate our code to
modules
But how do we deal with
libraries?
package demo;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Main {
public static void main(String... args) throws Exception {
Book modularityBook =
new Book("Java 9 Modularity", "Modularize all the things!");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(modularityBook);
System.out.println(json);
}
}
module books {
}
Automatic modules - A non-
modular JAR on the module
path
Exports all packages
Requires all resolved modules
Can read the class path (the unnamed module)
module books {
requires jackson.databind;
}
CP=lib/jackson-annotations-2.8.8.jar:
CP+=lib/jackson-core-2.8.8.jar
javac -cp $CP --module-path mods -d out --module-source-path src -m books
Case study
Sample app after migration.
!"" lib
!""
!""
mods
run.sh
Step 1 - Project structure.
#"" src
#"" bookapp “mods” dir for automatic modules
!"" books
$ !"" api
Single module dir (for now)
$ $ !"" entities Add module-info.java
$ $ $ #"" Book.java
$ $ #"" service
$ $ #"" BooksService.java
$ #"" impl
$ !"" entities
javac -cp $CLASSPATH \
$
$
$ #"" BookEntity.java
#"" service
--module-path mods \
$ #"" HibernateBooksService.java -d out \
!"" bookstore
$ !"" api --module-source-path src \
$ $ #"" service
$ $ #"" BookstoreService.java -m bookapp
$ #"" impl
$ #"" service
$ #"" BookstoreServiceImpl.java
!"" log4j2.xml
!"" main
$ #"" Main.java
!"" main.xml
#"" module-info.java
Step 2 - Add requires.
Compilation error
BookstoreServiceImpl.java:7: error: package
org.springframework.stereotype is not visible
import org.springframework.stereotype.Component;
Our module needs explicit
dependencies!
Step 3 - Add missing compile time platform
modules.
Compilation error
HibernateBooksService.java:19: error: cannot access Referenceable
return sessionFactory.getCurrentSession().get(BookEntity.class, id);
^
class file for javax.naming.Referenceable not found
Hibernate should declare
requires transitive java.naming
Running.
java -cp $CLASSPATH
--module-path mods:out
-m bookapp/main.Main
Step 4 - Add missing platform modules.
java.lang.ClassNotFoundException: java.sql.SQLException
java -cp $CLASSPATH
--add-modules java.sql,java.xml.bind
--module-path mods:out
-m bookapp/main.Main
Step 5 - Open packages for reflection.
java.lang.IllegalAccessException: class
org.springframework.beans.BeanUtils cannot access class
books.impl.service.HibernateBooksService (in module bookapp)
because module bookapp does not export books.impl.service to
unnamed module @5c45d770
opens books.impl.entities;
opens books.impl.service;
opens bookstore.impl.service;
Step 6 - Illegal deep reflection.
java.lang.reflect.InaccessibleObjectException: Unable to make
protected final java.lang.Class
java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int
,java.security.ProtectionDomain) throws
java.lang.ClassFormatError accessible: module java.base does not
"opens java.lang" to module javassist
java -cp $CLASSPATH
--add-modules java.sql,java.xml.bind
--add-opens java.base/java.lang=javassist
--module-path mods:out
-m bookapp/main.Main
Refactor to
multiple modules
Multi module structure
requires
books.api books.impl
main requires
bookstore
requires Spring
requires
books.api books.impl
main requires
bookstore.api bookstore.impl
requires
provides
uses provides
Module System
Build tools. <artifactId>bookstore</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
module bookstore {
<artifactId>maven-compiler-plugin</artifactId>
requires books;
<configuration>
<source>9</source>
exports bookstore.service.api;
<target>9</target>
</configuration>
uses books.api.service.BooksService;
</plugin>
</plugins>
provides bookstore.service.api.BookstoreService with
</build>
bookstore.service.impl.BookstoreServiceImpl;
<dependencies>
}
<dependency>
<groupId>javamodularity</groupId>
<artifactId>books</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
Thank you.
Paul Bakker
@pbakker