0% found this document useful (0 votes)
132 views

Migrating To Java 9 Modules

1. Modules define explicit dependencies and encapsulate packages by default. 2. Packages can be "opened" to allow deep reflection at runtime via the "opens" directive. 3. By default, only reflection on public APIs is allowed; deep reflection is denied to prevent security issues.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
132 views

Migrating To Java 9 Modules

1. Modules define explicit dependencies and encapsulate packages by default. 2. Packages can be "opened" to allow deep reflection at runtime via the "opens" directive. 3. By default, only reflection on public APIs is allowed; deep reflection is denied to prevent security issues.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 37

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

You might also like