37
Migrating to Java 9 Modules | Paul Bakker

Migrating to Java 9 modules by Paul Bakker - RainFocus · Migrating to Java 9 Modules ... java --add-opens java.base/java.lang=ALL-UNNAMED Main. Automatic Modules. But we’re still

Embed Size (px)

Citation preview

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 easytext.cli { requires easytext.analysis;

}

module easytext.analysis { exports analysis.api; opens impl;

}

easytext.cli easytext.analysis

other

analysis.api

implreflection only!

Module primer.

module easytext.cli { requires easytext.analysis;

}

module easytext.analysis { exports analysis.api; opens impl;

}

easytext.cli easytext.analysis

other

analysis.api

implreflection only!

Module primer.

Modules define dependencies explicitly

module easytext.cli { requires easytext.analysis;

}

module easytext.analysis { exports analysis.api; opens impl;

}

easytext.cli easytext.analysis

other

analysis.api

implreflection only!

Module primer.

Packages are encapsulated by default

module easytext.cli { requires easytext.analysis;

}

module easytext.analysis { exports analysis.api; opens impl;

}

easytext.cli easytext.analysis

other

analysis.api

implreflection only!

Module primer.

Packages can be “opened” for deep reflection at run-time

Migrating to Java 9.Java 8

Java 9

java -cp … -jar MyApp.jar

java -cp … -jar MyApp.jar

Our journey to modules.Running on Java 9Using existing librariesAllowing reflectionSpring / Hibernate case studyRefactoring to increase modularityMigrating 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 $   #"" impl $   #"" service $   #"" BookstoreServiceImpl.java !"" log4j2.xml !"" main $   #"" Main.java #"" main.xml

javac -cp [list of JARs in lib] -d out -sourcepath src $(find src -name '*.java')

cp $(find src -name '*.xml') out

java -cp [list of JARs in lib]:out main.Main

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

AutomaticModules

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 pathExports all packagesRequires all resolved modulesCan read the class path (the unnamed module)

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

module books { requires jackson.databind;}

Case study

Sample app after migration.

Step 1 - Project structure.!"" lib!"" mods!"" run.sh#"" src #"" bookapp !"" books $   !"" api $   $   !"" entities $   $   $   #"" Book.java $   $   #"" service $   $   #"" BooksService.java $   #"" impl $   !"" entities $   $   #"" BookEntity.java $   #"" service $   #"" HibernateBooksService.java !"" bookstore $   !"" api $   $   #"" service $   $   #"" BookstoreService.java $   #"" impl $   #"" service $   #"" BookstoreServiceImpl.java !"" log4j2.xml !"" main $   #"" Main.java !"" main.xml #"" module-info.java

“mods” dir for automatic modulesSingle module dir (for now)Add module-info.java

javac -cp $CLASSPATH \ --module-path mods \ -d out \ --module-source-path src \ -m bookapp

Step 2 - Add requires.

BookstoreServiceImpl.java:7: error: package org.springframework.stereotype is not visible import org.springframework.stereotype.Component;

Compilation error

Our module needs explicit dependencies!

Step 3 - Add missing compile time platform modules.

HibernateBooksService.java:19: error: cannot access Referenceable return sessionFactory.getCurrentSession().get(BookEntity.class, id); ^ class file for javax.naming.Referenceable not found

Compilation error

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 tomultiple modules

Multi module structure

main

books.api books.impl

bookstore

requires

requires

requires

Spring

main

books.api books.impl

bookstore.api

requires

requires

requires

Module Systemuses provides

bookstore.impl

provides

Build tools.<artifactId>bookstore</artifactId><build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>9</source> <target>9</target> </configuration> </plugin> </plugins></build><dependencies> <dependency> <groupId>javamodularity</groupId> <artifactId>books</artifactId> <version>1.0-SNAPSHOT</version> </dependency></dependencies>

module bookstore { requires books;

exports bookstore.service.api;

uses books.api.service.BooksService;

provides bookstore.service.api.BookstoreService with bookstore.service.impl.BookstoreServiceImpl;}

Thank you.Paul Bakker@pbakker