Meet Java Modules
As a warm up let's see how JDK itself was modularised.
List module files
New folder with JDK modules. jmod is currently internal jdk format for module file (jar on steroids)
ls -al  /usr/lib/jvm/jdk-9/jmods/
total 134544
drwxr-xr-x 2 root root     4096 wrz  2 15:17 .
drwxr-xr-x 8 root root     4096 wrz 24 16:35 ..
-rw-r--r-- 1 uucp  143    60992 sie  3 06:24 java.activation.jmod
-rw-r--r-- 1 uucp  143 19179351 sie  3 06:24 java.base.jmod
-rw-r--r-- 1 uucp  143   111302 sie  3 06:24 java.compiler.jmod
-rw-r--r-- 1 uucp  143  2680154 sie  3 06:24 java.corba.jmod
-rw-r--r-- 1 uucp  143    51795 sie  3 06:24 java.datatransfer.jmod
-rw-r--r-- 1 uucp  143 15766124 sie  3 06:24 java.desktop.jmod
-rw-r--r-- 1 uucp  143   765862 sie  3 06:24 javafx.base.jmod
-rw-r--r-- 1 uucp  143  2511426 sie  3 06:24 javafx.controls.jmod
-rw-r--r-- 1 uucp  143   238532 sie  3 06:24 javafx.deploy.jmod
(...)
-rw-r--r-- 1 uucp  143   119985 sie  3 06:24 java.logging.jmod
-rw-r--r-- 1 uucp  143   880650 sie  3 06:24 java.management.jmod
-rw-r--r-- 1 uucp  143    90550 sie  3 06:24 java.management.rmi.jmod
-rw-r--r-- 1 uucp  143   443764 sie  3 06:24 java.naming.jmod
-rw-r--r-- 1 uucp  143    62499 sie  3 06:24 java.prefs.jmod
jmod
With java 9 a new option to describe module was added.
- try also: java -d jdk.unsupported
 
jmod describe  /usr/lib/jvm/jdk-9/jmods/java.base.jmod 
java.base@9
exports java.io
exports java.lang
exports java.lang.annotation
exports java.lang.invoke
exports java.lang.module
exports java.lang.ref
exports java.lang.reflect
exports java.math
...
list modules
java --list-modules | column
java -d java.sql
java.sql@9
exports java.sql
exports javax.sql
exports javax.transaction.xa
requires java.base mandated
requires java.logging transitive
requires java.xml transitive
uses java.sql.Driver
module system
Intro
We are going to use recent version of Intellij (2018 or newer) . In this workshop perform actions step by step.
Create New Project
File -> New -> Project -> Empty Project
Name : modulesjug
Create New Java Module
Name : com.jug.modules.intro.math
Press F4 and in project settings choose language level Java 9
Alt + Insert on src -> new module.info.java
Create New Package
Name : com.jug.modules.intro.math
Create New Class MathLib.java
package com.jug.modules.intro.math;
import java.util.Optional;
public class MathLib {
    public static Optional<Integer> add(Integer i1, Integer i2){
        return Optional.of(i1+i2);
    }
}
- Create Main class in math package
 
package com.jug.modules.intro.math;
public class MathMain {
    public static void main(String[] args) {
        System.out.println("adding in math lib : "+MathLib.add(1,2));
    }
}
create empty folder mods in the project folder on modules level
mkdir mods >ls com.jug.modules.intro.math modscompile module
you can create bash file -> gedit compilemod.sh &
#!/bin/bash
chmod 700 compilemod.sh
javac -d mods/com.jug.modules.intro.math  com.jug.modules.intro.math/src/module-info.java /
com.jug.modules.intro.math/src/com/jug/modules/intro/math/MathLib.java /
com.jug.modules.intro.math/src/com/jug/modules/intro/math/MathMain.java
tree mods
mods
└── com.jug.modules.intro.math
    ├── com
    │   └── jug
    │       └── modules
    │           └── intro
    │               └── math
    │                   ├── MathLib.class
    │                   └── MathMain.class
    └── module-info.class
- Run Module
 
java --module-path mods -m com.jug.modules.intro.math/com.jug.modules.intro.math.MathMain
adding in math lib : Optional[3]
Module Encapsulation
- add internal package
 
package com.jug.modules.intro.math.internal
- Put there utility class OverflowDetector
 
public class OverflowDetector {
    public static Optional<Integer> add(int a, int b){
        try{
            return Optional.of(java.lang.Math.addExact(a,b));
        }catch(Exception e){
            return Optional.empty();
        }
    }
}
- export main package
 
module com.jug.modules.intro.math {
    exports com.jug.modules.intro.math;
}
use OverflowDetector
public class MathLib { public static Optional<Integer> add(Integer i1, Integer i2){ return OverflowDetector.add(i1,i2); } }
compile full module
javac -d mods/com.jug.modules.intro.math $(find com.jug.modules.intro.math/src/ -name *.java)
- show modules description
 
java --module-path mods  --describe-module com.jug.modules.intro.math 
file:///home/pawel/projects/workshops/modulesjug/mods/com.jug.modules.intro.math/
exports com.jug.modules.intro.math
- show cyclic dependency by requiring itself
 
Second Module - Displayer
create new module com.jug.modules.intro.displayer in the same parent folder as math module.
add module-info.java
- requires com.jug.modules.intro.math;
 
add Displayer class
package com.jug.modules.intro.displayer;
import com.jug.modules.intro.math.MathLib;
import java.util.Optional;
import java.util.Scanner;
import java.util.function.Consumer;
public class Displayer {
    private static Consumer<Integer> printResult=(i) -> System.out.println("result is : "+i);
    private static Runnable errorProcedure = () -> System.out.println("unable to add integeres, overflow");
    public static void displayMath(){
        Scanner scanner = new Scanner(System.in);
        System.out.print("i1 : ");
        Integer i1=scanner.nextInt();
        System.out.print("i2 : ");
        Integer i2=scanner.nextInt();
        Optional<Integer> result = MathLib.add(i1, i2);
        result.ifPresentOrElse(printResult,errorProcedure);
    }
    public static void main(String[] args) {
        displayMath();
    }
}
- try to compile
 
javac -d mods/com.jug.modules.intro.displayer $(find com.jug.modules.intro.displayer/src/ -name *.java)
com.jug.modules.intro.displayer/src/module-info.java:2: error: module not found: com.jug.modules.intro.math
    requires com.jug.modules.intro.math;
- add module path
 
javac -d mods/com.jug.modules.intro.displayer --module-path mods  $(find com.jug.modules.intro.displayer/src/ -name *.java)
- test displayer with input : MAX_INT = 2147483647
 
java --module-path mods -m com.jug.modules.intro.displayer/com.jug.modules.intro.displayer.Displayer
i1 : 2147483647
i2 : 2147483647
unable to add integeres, overflow
- try to access internal math package
 
import com.jug.modules.intro.math.MathLib.internal;
com.jug.modules.intro.displayer/src/com/jug/modules/intro/displayer/Displayer.java:4: error: cannot find symbol
import com.jug.modules.intro.math.MathLib.internal;
- remove math module and try to compile displayer
 
java --module-path mods -m com.jug.modules.intro.displayer/com.jug.modules.intro.displayer.Displayer
Error occurred during initialization of boot layer
java.lang.module.FindException: Module com.jug.modules.intro.math not found, required by com.jug.modules.intro.displayer
- compile math once again
 
javac -d mods/com.jug.modules.intro.math  $(find com.jug.modules.intro.math/src/ -name *.java)
- java --show-module-resolution
 
java --show-module-resolution --limit-modules com.jug.modules.intro.math  --module-path mods -m com.jug.modules.intro.displayer
root com.jug.modules.intro.displayer file:///home/pawel/projects/workshops/modulesjug/mods/com.jug.modules.intro.displayer/
com.jug.modules.intro.displayer requires com.jug.modules.intro.math file:///home/pawel/projects/workshops/modulesjug/mods/com.jug.modules.intro.math/
module com.jug.modules.intro.displayer does not have a MainClass attribute, use -m <module>/<main-class>
- decompile
 
Compiled from "module-info.java"
module com.jug.modules.intro.displayer {
  requires com.jug.modules.intro.math;
  requires java.base;
}
Packaging to jars
jar --create --file modsjars/modules.math.jar -C mods/com.jug.modules.intro.math/ .
- try to recompile displayer. Because we have math packed in jar so technically we have two modules with the same name
 
javac -d mods/com.jug.modules.intro.displayer --module-path mods  $(find com.jug.modules.intro.displayer/src/ -name *.java)
error: duplicate module on application module path
  module in com.jug.modules.intro.math
1 error
- move Jar to different folder
 
javac -d mods/com.jug.modules.intro.displayer --module-path modsjars  $(find com.jug.modules.intro.displayer/src/ -name *.java)
jar --create --file modsjars/modules.displayer.jar --main-class com.jug.modules.intro.displayer.Displayer -C mods/com.jug.modules.intro.displayer/ .
java --module-path modsjars --module com.jug.modules.intro.displayer
i1 : 4
i2 : 7
result is : 11
//  --show-module-resultion
//  jar --list --file mods/modules.displayer.jar
//  jar --describe-module --file mods/monitor.observer.jar
// -Xlog:module*=debug:stdout:tid
- Keep jar and folder to show ambigous modules
 
Automatic Modules
jar --file=/home/pawel/.m2/repository/commons-lang/commons-lang/2.5/commons-lang-2.5.jar --describe-module
No module descriptor found. Derived automatic module.
[email protected] automatic
requires java.base mandated
contains org.apache.commons.lang
contains org.apache.commons.lang.builder
contains org.apache.commons.lang.enums
contains org.apache.commons.lang.exception
contains org.apache.commons.lang.math
contains org.apache.commons.lang.mutable
contains org.apache.commons.lang.reflect
contains org.apache.commons.lang.text
contains org.apache.commons.lang.time
JLink
jlink --module-path mods:/usr/lib/jvm/jdk-9/jmods --add-modules com.jug.modules.intro.displayer --output hellojre
modulesjug$ hellojre/bin/java --list-modules 
com.jug.modules.intro.displayer
com.jug.modules.intro.math
java.base@9
Run :
hellojre/bin/java  -m com.jug.modules.intro.displayer/com.jug.modules.intro.displayer.Displayer
EXERCISE PART 1
- Create new module - com.exercise.consumer
 - Set Java level in IDE to Java 9
 - Create - empty for now - module-info.java
 - Create new module - com.exercise.producer
 - Set Java Level to Java 9
 - Create module-info.java
 - In producer module create package : com.exercise.producer
 In com.exercise.producer create new class which produces List of Strings according to
class Producer{ public static List<String> produce(){....} } Producer.produce mustBe List("one","two","three") //test assertion in pseudo codeAdd proper export to module.info
Add dependency to producer in consumer
- you may need to add dependency in IDE properties
 
Create packages the same as module name
Add Consumer class + imports
    public class Consumer { 
        public static void main(String[] args) { 
            String result=Producer.produce().stream().collect(Collectors.joining("|")); 
            System.out.println(result); 
        } 
    }
When you run it the result should be
one|two|three
Create another module com.exercise.Printer
create module
create package
create module-info.java
in com.exercise.Printer add an interface
public interface Printer { void print(String s); }create another package which will be hidden in the module. - com.exercise.printer.console
create printer implementation
package com.exercise.printer.console; import com.exercise.printer.Printer; public class ConsolePrinter implements Printer { @Override public void print(String s) { System.out.println("PRINTING: "+s); } }create provider in the exported package com.exercise.printer
package com.exercise.printer; import com.exercise.printer.console.ConsolePrinter; public abstract class AbstractFactoryPrinterProviderBean { public static Printer providePrinter(){ return new ConsolePrinter(); } }
Use Printer in consumer module
update module-info
package com.exercise.consumer; import com.exercise.printer.AbstractFactoryPrinterProviderBean; import com.exercise.printer.Printer; import com.exercise.producer.Producer; import java.util.stream.Collectors; public class Consumer { public static void main(String[] args) { String result=Producer.produce().stream().collect(Collectors.joining("|")); Printer printer=AbstractFactoryPrinterProviderBean.providePrinter(); printer.print(result); } }Result should be : PRINTING: one|two|three
It should be forbidden to access ConsolePrinter directly
//THIS IS FORBIDDEN!! import com.exercise.printer.console.ConsolePrinter; Printer printer=new ConsolePrinter();
Compile everything into mods folder
First displayer
javac --module-path mods -d mods/com.exercise.printer $(find com.exercise.printer/src/ -name *.java)Then producer
Finally consumer
Run Consumer
java --module-path mods -m com.exercise.consumer/com.exercise.consumer.Consumer
Create Consumer Jar
Create Custom JRE for our Jar with JLink
Run Consumer on Customer JRE
Maven
First clone repo : https://github.com/PawelWlodarski/mvnmodules
parent pom
- configure Java9 in the main pom.xml
 
<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.target>1.9</maven.compiler.target>
        <maven.compiler.source>1.9</maven.compiler.source>
</properties>
- configure modules in the main pom.xml.
 
<modules>
        <module>math</module>
        <module>calculator</module>
        <module>gui</module>
    </modules>
- configure build plugin in the main pom.xml
 
  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.7.0</version>
    <configuration>
        <source>9</source>
        <target>9</target>
        <showWarnings>true</showWarnings>
        <showDeprecation>true</showDeprecation>
    </configuration>
</plugin>
module pom
- gui modules pom.xml and check dependency on other module
 
  <dependencies>
        <dependency>
            <groupId>lodz.jug</groupId>
            <artifactId>calculator</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
   </dependencies>
- module-info.java in gui/src/main/java
 
module com.jug.modules.intro.gui {
    requires com.jug.modules.intro.calculator;
}
Decoupling
Show EngineProvider. How it connects with services?
Compile Modules
mvn clean install
all jars will be saved in mods folder because of setting in the main pom.xml
<outputDirectory>../mods</outputDirectory>
Now try to run gui.
java --module-path mods --module com.jug.modules.intro.gui/com.jug.modules.intro.gui.GUI
Services
Services allows you to decouple provider of a given service implementation from its consumer

You can find three modules in the main pom.xml
<module>displayerAPI</module>
<module>displayerProvider1</module>
<module>displayerConsumer</module>
Those three modules use displayer API but consumer and provider are not aware of itself.
First let's look at the API.
package com.jug.modules.displayer.api;
public interface Displayer {
    boolean supports(DisplayerPlatform p);
    void display(String s);
}
It's an normal interface yet purpose of support method may be not clear at first. Generally Each implementation can support different features and because consumer will not be aware of implementation so it can not choose one specific. We will see more in consumer module.
Also nothing specific in module-info
module com.jug.modules.displayer.api{
    exports com.jug.modules.displayer.api;
}
No we will see something new in producer module. Producer has to say that it is able to provides specific implementation of a service
module displayerProvider1 {
    requires com.jug.modules.displayer.api;
    provides com.jug.modules.displayer.api.Displayer
            with com.jug.modules.displayer.console.ConsoleDisplayer;
}
No consumer has to declare usage of this service
module displayerConsumer {
    requires com.jug.modules.displayer.api;
    uses com.jug.modules.displayer.api.Displayer;
}
And finally we have to use service locator to choose proper implementation. Here is when support method may become useful.
ServiceLoader<Displayer> displayers = ServiceLoader.load(Displayer.class);
Stream<ServiceLoader.Provider<Displayer>> providerStream = displayers.stream();
Optional<Displayer> firstPotentialDisplayer = providerStream
        .map(ServiceLoader.Provider::get)
        .filter(d -> d.supports(DisplayerPlatform.LINUX))
        .findFirst();
Displayer displayer = firstPotentialDisplayer.orElseThrow(() -> new RuntimeException("No Displayer her"));
displayer.display("Displayer Found Successfully : "+displayer.getClass());
Services Exercise (Together)
- Create new module in main pom.xml
 - Add another implementation of Displayer
 - Declare in module-info that you are providing implementation
 - in consumer - just for - print all provided implementations
 
Dependencies Configuration
requires transitive
opens
opens <package-name>
opens <package-name> to <package_in_other_module>
- --add-opens
 
qualified exports
* Persistence Framework
exports javamodularity.easytext.gui to javafx.graphics;
unnamed module
- classpath emulation
 - Code in the unnamed module (the class path) can access code in any named
modules using reflection
- --illegal-access flag is set by default for this module
 
 
Compile myUnnamed and runt he following
projects/workshops/mvnmodules$ jar --file mods/myUnnamed-1.0-SNAPSHOT.jar --describe-module
No module descriptor found. Derived automatic module.
[email protected] automatic
requires java.base mandated
contains com.jug.intro.unnamed
jdeps
deps math-1.0-SNAPSHOT.jar 
com.jug.modules.intro.math
 [file:///home/pawel/projects/workshops/mvnmodules/mods/math-1.0-SNAPSHOT.jar]
   requires mandated java.base (@9)
com.jug.modules.intro.math -> java.base
   com.jug.modules.intro.math                         -> com.jug.modules.intro.math.internal                com.jug.modules.intro.math
   com.jug.modules.intro.math                         -> java.io                                            java.base
   com.jug.modules.intro.math                         -> java.lang                                          java.base
   com.jug.modules.intro.math                         -> java.lang.invoke                                   java.base
   com.jug.modules.intro.math                         -> java.util                                          java.base
   com.jug.modules.intro.math.internal                -> java.lang                                          java.base
   com.jug.modules.intro.math.internal                -> java.util                                          java.base
- with modules dependencies
 
jdeps --module-path mods mods/gui-1.0-SNAPSHOT.jar 
com.jug.modules.intro.gui
 [file:///home/pawel/projects/workshops/mvnmodules/mods/gui-1.0-SNAPSHOT.jar]
   requires com.jug.modules.intro.calculator
   requires mandated java.base (@9)
com.jug.modules.intro.gui -> com.jug.modules.intro.calculator
com.jug.modules.intro.gui -> java.base
   com.jug.modules.intro.gui                          -> com.jug.modules.intro.calculator                   com.jug.modules.intro.calculator
list modules + custom modules
java --module-path out --list-modules
Qualified exports
exports    moduleb.privateA    to    A;    //    Exported    only    to    module    A    
exports    moduleb.privateC    to    C;    //    Exported    only    to    module    C
java    -d    java.base    
        module    java.base@9
        ...    
        exports    jdk.internal.ref    to    java.desktop,    javafx.media    
        exports    jdk.internal.math    to    java.desktop    
        exports    sun.net.ext    to    jdk.net    
        exports    jdk.internal.loader    to    java.desktop,    java.logging
Optionality
require static
Links
