Sunday, 25 November 2012

Worldclock on Jigsaw and HK2

In the previous post I 'ported' HK2 to work on Jigsaw, in this post I adapt Worldclock to use it.

Retrieving the existing code:

svn checkout https://svn.java.net/svn/worldclock-application~svn/tags/application6-jigsaw-maven ~/dev/worldclock-hk2

And the new pom and module-info:

svn checkout https://svn.kenai.com/svn/lh-playground~svn/jigsaw/worldclock-hk2 ~/dev/worldclock-hk2-downloads

Panel

Overwrite the existing pom and module info for the panel:
cp ~/dev/worldclock-hk2-downloads/panel/pom.xml ~/dev/worldclock-hk2/panel/
(update the libraryDirectory path for your set up / username, gedit  ~/dev/worldclock-hk2/panel/pom.xml & )
cp  ~/dev/worldclock-hk2-downloads/panel/src/main/java/lh.worldclock.panel/module-info.java ~/dev/worldclock-hk2/panel/src/main/java/lh.worldclock.panel/
Add the sample HK2 dependency injection contract:
mkdir -p ~/dev/worldclock-hk2/panel/src/main/java/lh.worldclock.panel/lh/worldclock/hk2/
gedit ~/dev/worldclock-hk2/panel/src/main/java/lh.worldclock.panel/lh/worldclock/hk2/PanelProvider.java &
then add the content
package lh.worldclock.hk2;

import lh.worldclock.core.WorldClockBoard;
import org.jvnet.hk2.annotations.Contract;

@Contract
public interface PanelProvider
{
  WorldClockBoard getPanel();
}

Add the sample HK2 dependency injection service:
gedit ~/dev/worldclock-hk2/panel/src/main/java/lh.worldclock.panel/lh/worldclock/hk2/PanelProviderImpl.java &
then add the content
package lh.worldclock.hk2;

import lh.worldclock.core.WorldClockBoard;
import org.jvnet.hk2.annotations.Service;

@Service
public class PanelProviderImpl implements PanelProvider
{
  @Override
  public WorldClockBoard getPanel()
  {
    return new WorldClockBoard();
  }
}
build
cd ~/dev/worldclock-hk2/panel/
mvn -Dmaven.test.skip=true clean install
(if the build fail due to  invalid characters, edit the file and remove the comments
gedit ~/dev/worldclock-hk2/panel/src/main/java/lh.worldclock.panel/lh/worldclock/core/WorldClockBoard.java & )


Application


Overwrite the existing pom and module info for the application:
cp ~/dev/worldclock-hk2-downloads/application/pom.xml ~/dev/worldclock-hk2/application/
(update the libraryDirectory path for your set up / username, gedit  ~/dev/worldclock-hk2/application/pom.xml & )
cp  ~/dev/worldclock-hk2-downloads/application/src/main/java/lh.worldclock.application/module-info.java ~/dev/worldclock-hk2/application/src/main/java/lh.worlclock.application/

Adjust WorldClockPanel:
gedit ~/dev/worldclock-hk2/application/src/main/java/lh.worlclock.application/lh/worldclock/WorldClockPanel.java &

replace constructor start with:
   public WorldClockPanel(final WorldClockBoard board)
  {
//    board = new WorldClockBoard();
    this.board = board;
Adjust WorldClockFrame:
gedit ~/dev/worldclock-hk2/application/src/main/java/lh.worlclock.application/lh/worldclock/WorldClockFrame.java &

add import
 import lh.worldclock.core.WorldClockBoard;

remove the initialisation of pane
 WorldClockPanel pane;

replace constructor start with:
   public WorldClockFrame(ImageIcon icon, final WorldClockBoard board)
  {
    pane = new WorldClockPanel(board);
Adjust WorldClock
gedit ~/dev/worldclock-hk2/application/src/main/java/lh.worlclock.application/lh/worldclock/WorldClock.java &

add imports
import com.sun.enterprise.module.bootstrap.ModuleStartup;
import com.sun.enterprise.module.bootstrap.StartupContext;
import org.jvnet.hk2.annotations.Inject;
import org.jvnet.hk2.annotations.Service;
import lh.worldclock.core.WorldClockBoard;

replace class declaration
    @Service
    public class WorldClock implements ModuleStartup

make the frame non static
/*static*/ WorldClockFrame frame = null;
 add the injection
   @Inject
  WorldClockBoard board;
make main method non static

make void showWindow() non static and change
frame = new WorldClockFrame(icon, board);

to add the board

make   private PopupMenu createPopup() non static

add the following new methods
  public void setStartupContext(StartupContext context)
  {
  }

  public void start()

  {
    main(new String[]{});
  }

  public void stop()
  {
  }
build
cd ~/dev/worldclock-hk2/application/
mvn -Dmaven.test.skip=true clean install

Time to run:
cd ~/dev/hk2-jigsaw/jigsaw-adapter/
mvn lh.jigsaw:jigsaw-maven-plugin:run

HK2 and Jigsaw


In 2007, Jerôme Dochez presented HK2 as a module system loosely based on JSR277, I tend to see Jigsaw as the continuation of JSR277, so I wanted to see how HK2 could interface with Jigsaw.

Setting up the environement:

1. Build Jigsaw
2. Build the Maven plugin for Jigsaw
3. Download and extract Maven 3 in ~/dev
4. Export the variables:
export M2_HOME=~/dev/apache-maven-3.0.4/
export MAVEN_HOME=~/dev/apache-maven-3.0.4/
export JAVA_HOME='/usr/lib/jvm/java-7-openjdk-i386'
export PATH=$JAVA_HOME/bin:$M2_HOME/bin:$PATH
give some extra room to Maven:
export MAVEN_OPTS="-XX:PermSize=64M -XX:MaxPermSize=128M"

Get and build HK2:

svn checkout https://svn.java.net/svn/hk2~svn/tags/hk2-parent-1.6.32 ~/dev/hk2-1.6.32
cd ~/dev/hk2-1.6.32
mvn -DskipTests=true -Prelease-phase1 clean install
mvn -DskipTests=true clean install

Get the additions/replacements:

In order to reduce the amount of typing/copying some of the added/modified files are downloadable.
svn checkout https://svn.kenai.com/svn/lh-playground~svn/jigsaw/hk2 ~/dev/hk2-jigsaw-downloads

Patch HK2:

HK2 1.6.32 still uses the APT to build, this is removed from Java8/Jigsaw, so some adjustments are needed. These replace APT API with JSR269 API.
cd ~/dev/hk2-1.6.32/
patch -p0 < ~/dev/hk2-jigsaw-downloads/hk2-1.6.32-java7-maven-plugin.diff
as a result of the patch some of the tests in HK2 are failing, so disabling them for now
gedit ~/dev/hk2-1.6.32/pom.xml &
then comment line  307 (<module>auto-depends-tests</module>).
Build the patched version:
mvn -DskipTests=true clean install

Clean up no longer needed classes from the Maven plugin:

rm -f ~/dev/hk2-1.6.32/hk2-maven-plugin/src/main/java/com/sun/enterprise/module/maven/CompositeAnnotationProcessorFactory.java
rm -f ~/dev/hk2-1.6.32/hk2-maven-plugin/src/main/java/com/sun/enterprise/module/maven/AptInvoker.java
rm -f ~/dev/hk2-1.6.32/hk2-maven-plugin/src/main/java/com/sun/enterprise/module/maven/AptCompiler.java

Adjust hk2-maven-plugin for Jigsaw:

Replace components.xml with a version that defines the packaging type to hk2-jmod rather than hk2-jar, and that also adds the Jigsaw extensions.
 cp ~/dev/hk2-jigsaw-downloads/hk2-1.6.32/hk2-maven-plugin/src/main/resources/META-INF/plexus/components.xml ~/dev/hk2-1.6.32/hk2-maven-plugin/src/main/resources/META-INF/plexus/components.xml
Adjust  PackageMojo:
gedit ~/dev/hk2-1.6.32/hk2-maven-plugin/src/main/java/com/sun/enterprise/module/maven/PackageMojo.java &
replace line 109:
@parameter expression="${component.org.codehaus.plexus.archiver.Archiver#jar}"
with
@component role="org.codehaus.plexus.archiver.Archiver" roleHint="jar"

Adjust RunMojo:
gedit ~/dev/hk2-1.6.32/hk2-maven-plugin/src/main/java/com/sun/enterprise/module/maven/RunMojo.java &

set the goal to hk2-run on line 65 so that the default run is the Jigsaw run

Add the non compile parts from the Jigsaw Maven plugin and override the compile part:
cp -r ~/dev/jigsaw-maven-plugin/src/main/java/lh ~/dev/hk2-1.6.32/hk2-maven-plugin/src/main/java/
mv  ~/dev/hk2-1.6.32/hk2-maven-plugin/src/main/java/lh/jigsaw/plugin/AbstractCompilerMojo.java ~/dev/hk2-1.6.32/hk2-maven-plugin/src/main/java/com/sun/enterprise/module/maven/
mv  ~/dev/hk2-1.6.32/hk2-maven-plugin/src/main/java/lh/jigsaw/plugin/CompilerMojo.java ~/dev/hk2-1.6.32/hk2-maven-plugin/src/main/java/com/sun/enterprise/module/maven/
Adjust  AbstractCompilerMojo:
gedit  ~/dev/hk2-1.6.32/hk2-maven-plugin/src/main/java/com/sun/enterprise/module/maven/AbstractCompilerMojo.java &

replace the package
package lh.jigsaw.plugin;
with
package com.sun.enterprise.module.maven;


Adjust CompilerMojo:
gedit  ~/dev/hk2-1.6.32/hk2-maven-plugin/src/main/java/com/sun/enterprise/module/maven/CompilerMojo.java &

replace the package
package lh.jigsaw.plugin;
with
package com.sun.enterprise.module.maven;

Adjust the pom:
gedit ~/dev/hk2-1.6.32/hk2-maven-plugin/pom.xml &
Add to the build:
         <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
                <dependencies>
                   <dependency>
                     <groupId>org.codehaus.plexus</groupId>
                     <artifactId>plexus-compiler-javac</artifactId>
                     <version>1.8.1-jigsaw</version>
                     <exclusions>
                        <exclusion>
                          <groupId>org.codehaus.plexus</groupId>
                          <artifactId>plexus-component-api</artifactId>
                        </exclusion>
                     </exclusions>
                  </dependency>
                </dependencies>
            </plugin>
        </plugins>
Replace the auto-depends-plugin dependency with:
         <dependency>
            <groupId>org.glassfish.hk2</groupId>
            <artifactId>auto-depends-plugin</artifactId>
            <version>${project.version}</version>
            <exclusions>
              <exclusion>
                <groupId>com.sun</groupId>
                <artifactId>tools</artifactId>
              </exclusion>
            </exclusions>
        </dependency>
Replace the plexus-compiler-javac dependency with:
     <dependency>
      <groupId>org.codehaus.plexus</groupId>
      <artifactId>plexus-compiler-javac</artifactId>
      <version>1.8.1-jigsaw</version>
      <exclusions>
        <exclusion>
          <groupId>org.codehaus.plexus</groupId>
          <artifactId>plexus-component-api</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
Build the modified plugin with Jigsaw:
export JAVA_HOME=~/dev/jigsaw/build/linux-i586/jdk-module-image
cd ~/dev/hk2-1.6.32/hk2-maven-plugin/
mvn clean install

Make (parts of) hk2 Jigsaw friendly (ie Jigsaw projects):


JSR330 support
mkdir ~/dev/hk2-jigsaw
cp -r ~/dev/hk2-jigsaw-downloads/hk2-jigsaw/hk2jsr330/ ~/dev/hk2-jigsaw/
cd ~/dev/hk2-jigsaw/hk2jsr330/
mvn -Dmaven.test.skip=true clean install
(update the libraryDirectory path for your set up / username, gedit ~/dev/hk2-jigsaw/hk2jsr330/pom.xml & )
 
HK2-API
cp  ~/dev/hk2-jigsaw-downloads/hk2-1.6.32/hk2-api/pom.xml ~/dev/hk2-1.6.32/hk2-api/
(update the libraryDirectory path for your set up / username, gedit  ~/dev/hk2-1.6.32/hk2-api/pom.xml & )

mkdir ~/dev/hk2-1.6.32/hk2-api/src/main/java/lh.jigsaw.hk2.api
mv  ~/dev/hk2-1.6.32/hk2-api/src/main/java/org/  ~/dev/hk2-1.6.32/hk2-api/src/main/java/lh.jigsaw.hk2.api/
cp  ~/dev/hk2-jigsaw-downloads/hk2-1.6.32/hk2-api/src/main/java/lh.jigsaw.hk2.api/module-info.java ~/dev/hk2-1.6.32/hk2-api/src/main/java/lh.jigsaw.hk2.api/
build
cd ~/dev/hk2-1.6.32/hk2-api/
mvn -Dmaven.test.skip=true clean install

Auto-depends
cp  ~/dev/hk2-jigsaw-downloads/hk2-1.6.32/auto-depends/pom.xml ~/dev/hk2-1.6.32/auto-depends/
(update the libraryDirectory path for your set up / username, gedit  ~/dev/hk2-1.6.32/auto-depends/pom.xml & )
mkdir ~/dev/hk2-1.6.32/auto-depends/src/main/java/lh.jigsaw.hk2.autodepends
mv ~/dev/hk2-1.6.32/auto-depends/src/main/java/org/ ~/dev/hk2-1.6.32/auto-depends/src/main/java/lh.jigsaw.hk2.autodepends
mv ~/dev/hk2-1.6.32/auto-depends/src/main/java/com/ ~/dev/hk2-1.6.32/auto-depends/src/main/java/lh.jigsaw.hk2.autodepends
cp  ~/dev/hk2-jigsaw-downloads/hk2-1.6.32/auto-depends/src/main/java/lh.jigsaw.hk2.autodepends/module-info.java ~/dev/hk2-1.6.32/auto-depends/src/main/java/lh.jigsaw.hk2.autodepends/
build
cd ~/dev/hk2-1.6.32/auto-depends/
mvn -Dmaven.test.skip=true clean install
HK2-Core
cp  ~/dev/hk2-jigsaw-downloads/hk2-1.6.32/core/pom.xml ~/dev/hk2-1.6.32/core/
(update the libraryDirectory path for your set up / username, gedit  ~/dev/hk2-1.6.32/core/pom.xml & )


mkdir ~/dev/hk2-1.6.32/core/src/main/
mv  ~/dev/hk2-1.6.32/core/src/java ~/dev/hk2-1.6.32/core/src/main/
mkdir ~/dev/hk2-1.6.32/core/src/main/java/lh.jigsaw.hk2.core
mv  ~/dev/hk2-1.6.32/core/src/main/java/com ~/dev/hk2-1.6.32/core/src/main/java/lh.jigsaw.hk2.core/
cp  ~/dev/hk2-jigsaw-downloads/hk2-1.6.32/core/src/main/java/lh.jigsaw.hk2.core/module-info.java ~/dev/hk2-1.6.32/core/src/main/java/lh.jigsaw.hk2.core/
build
cd ~/dev/hk2-1.6.32/core/
mvn -Dmaven.test.skip=true clean install 

Add the adapter for Jigsaw:

The adapter only implements the minimum required to start HK2 over Jigsaw and inject a dependency in a program. It inspired by both the initial HK2 launcher and the OSGi adapter. The Jigsaw interaction bits are in JigsawFactory (obtain a library instance), JigsawModuleDefinition (obtain the inhabitants file content from the Jisaw library for a module), JigsawModulesRegistry (obtain a class loader for a module).
cp -r  ~/dev/hk2-jigsaw-downloads/hk2-jigsaw/jigsaw-adapter ~/dev/hk2-jigsaw/
cd ~/dev/hk2-jigsaw/jigsaw-adapter
mvn -Dmaven.test.skip=true clean install

Let's us it:

See  Worldclock on Jigsaw and HK2

Wednesday, 18 January 2012

Jigsaw: Worldclock with the Maven plugin

Last updated: 2012-02-02

In the previous post I created a Maven plugin for Jigsaw, now I'll adapt Worldclock to use it.
The transformed source code is also available at: https://svn.java.net/svn/worldclock-application~svn/tags/application6-jigsaw-maven 


1. Create the directory
mkdir -p ~/dev/worldclock
2. Get the source
cd ~/dev/worldclock
svn checkout https://svn.java.net/svn/worldclock-application~svn/tags/application6-jigsaw ~/dev/worldclock/application

3. Cleanup
Remove the files that are not needed.
rm -rf ~/dev/worldclock/application/*.*
rm -rf ~/dev/worldclock/application/src/META-INF

4. Code adjustment
The current combinaison of Jigsaw/JDK8/Ubuntu does not support the sytem tray properly, so adjust the Worldclock.java to bypass it
gedit ~/dev/worldclock/application/src/lh/worldclock/WorldClock.java &

on line 106 replace:
if (SystemTray.isSupported())
with:
if (false)

on line 135 replace:
if (showWindowOnStart)
with:
if (true)

5. Move the source
Move the files to create the java sources for the application:
mkdir -p ~/dev/worldclock/application/src/main/java/lh.worlclock.application/lh/worldclock
mv ~/dev/worldclock/application/src/lh/worldclock/*.java ~/dev/worldclock/application/src/main/java/lh.worlclock.application/lh/worldclock
Move the application resources:
mkdir -p ~/dev/worldclock/application/src/main/resources/lh/worldclock
mv ~/dev/worldclock/application/src/lh/worldclock/*.* ~/dev/worldclock/application/src/main/resources/lh/worldclock
Move the panel source:
mkdir -p ~/dev/worldclock/panel/src/main/java/lh.worldclock.panel/lh/worldclock/core
mv ~/dev/worldclock/application/src/lh/worldclock/core/*.java ~/dev/worldclock/panel/src/main/java/lh.worldclock.panel/lh/worldclock/core
Move the panel resources:
mkdir -p ~/dev/worldclock/panel/src/main/resources/lh/worldclock/core
mv ~/dev/worldclock/application/src/lh/worldclock/core/*.jpg ~/dev/worldclock/panel/src/main/resources/lh/worldclock/core
More cleanup:
rm -rf ~/dev/worldclock/application/src/lh/worldclock/core
6. Add module-info for Panel
gedit  ~/dev/worldclock/panel/src/main/java/lh.worldclock.panel/module-info.java &

module lh.worldclock.panel @ 0.7
{
  requires jdk.base;
  requires jdk.desktop;
  exports lh.worldclock.core.*;
}

7. Add module-info for Application
gedit ~/dev/worldclock/application/src/main/java/lh.worlclock.application/module-info.java &

module lh.worldclock.application @ 0.7
{
  requires jdk.base;
  requires jdk.desktop;
  requires lh.worldclock.panel @ 0.7;
  class lh.worldclock.WorldClock;
}

8. Add the pom for Panel
gedit ~/dev/worldclock/panel/pom.xml &

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>lh.worldclock</groupId>
  <artifactId>panel</artifactId>
  <packaging>jmod</packaging>
  <version>0.7-SNAPSHOT</version>
  <name>Worldclock Panel</name>
  <url>http://worldclock-application.java.net/</url>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>lh.jigsaw</groupId>
                <artifactId>jigsaw-maven-plugin</artifactId>
                <version>0.1-SNAPSHOT</version>
                <extensions>true</extensions>
                <configuration>
                  <libraryDirectory>../modules</libraryDirectory>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

9. Add the pom for Application
gedit ~/dev/worldclock/application/pom.xml &

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>lh.worldclock</groupId>
  <artifactId>application</artifactId>
  <packaging>jmod</packaging>
  <version>0.7-SNAPSHOT</version>
  <name>Worldclock Application</name>
  <url>http://worldclock-application.java.net/</url>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>
   <build>
        <plugins>
            <plugin>
                <groupId>lh.jigsaw</groupId>
                <artifactId>jigsaw-maven-plugin</artifactId>
                <version>0.1-SNAPSHOT</version>
                <extensions>true</extensions>
                <configuration>
                  <libraryDirectory>../modules</libraryDirectory>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

10. Install Panel
cd ~/dev/worldclock/panel/
mvn install

11. Install Application
cd ~/dev/worldclock/application/
mvn install

12. Run
 cd ~/dev/worldclock/application/
mvn lh.jigsaw:jigsaw-maven-plugin:run
(use ctrl+c on the console to exit)



Jigsaw and Maven: Take 2, a Maven plugin

Last updated: 2012-11-01

This post will create a Maven plugin for Jigsaw. This plugin is based on HK2 Maven plugin.
The post includes all the code needed to build the plugin.
(the resulting source code, except for changes in Jigsaw's source, is available at: https://svn.kenai.com/svn/lh-playground~svn/jigsaw )

Setting up the environment:
1. Build Jigsaw
2. Get Subversion
sudo apt-get install subversion
3. Get Maven

sudo apt-get install maven2
4. Set JAVA_HOME
export JAVA_HOME='/usr/lib/jvm/java-7-openjdk-i386'

Custom Plexus Javac Component:
1. Get the code
cd ~/dev
svn checkout https://svn.codehaus.org/plexus/plexus-components/tags/1.8.1/plexus-compilers/plexus-compiler-javac ~/dev/plexus-compiler-javac
2. Compile without modification
cd ~/dev/plexus-compiler-javac
mvn install
3. Adjust the pom
gedit ~/dev/plexus-compiler-javac/pom.xml &
replace the existing content with

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>org.codehaus.plexus</groupId>
    <artifactId>plexus-compilers</artifactId>
    <version>1.8.1</version>
  </parent>
 
  <artifactId>plexus-compiler-javac</artifactId>

  <name>Jigsaw Plexus Javac Component</name>
  <version>1.8.1-jigsaw</version>
 
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3.2</version>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
        <dependencies>
          <dependency>
            <groupId>org.codehaus.plexus</groupId>
            <artifactId>plexus-compiler-javac</artifactId>
            <version>1.8.1</version>
            <exclusions>
              <exclusion>
                <groupId>org.codehaus.plexus</groupId>
                <artifactId>plexus-component-api</artifactId>
              </exclusion>
            </exclusions>
          </dependency>
        </dependencies>
      </plugin>
    </plugins>
  </build>
  <dependencies>
    <dependency>
      <groupId>org.codehaus.plexus</groupId>
      <artifactId>plexus-utils</artifactId>
    </dependency>
    <dependency>
      <groupId>org.codehaus.plexus</groupId>
      <artifactId>plexus-compiler-api</artifactId>
      <version>1.8.1</version>
    </dependency>
    <dependency>
      <groupId>org.codehaus.plexus</groupId>
      <artifactId>plexus-compiler-test</artifactId>
      <version>1.8.1</version>
    </dependency>
  </dependencies>
 
</project>
 4. Adjust the code
gedit ~/dev/plexus-compiler-javac/src/main/java/org/codehaus/plexus/compiler/javac/JavacCompiler.java &

add the imports:
import java.util.Locale;
import javax.tools.*;
import javax.tools.Diagnostic.Kind;



locate the call to compileInProcess (line 165)
messages = compileInProcess( args );
replace it with:
messages = compileInProcessJSR199( buildCompilerArgumentsJSR199( config ), sourceFiles );

add the method:
        public static List<String> buildCompilerArgumentsJSR199( CompilerConfiguration config )
    {
        List<String> args = new ArrayList<String>();

        // ----------------------------------------------------------------------
        // Set output
        // ----------------------------------------------------------------------

        File destinationDir = new File( config.getOutputLocation() );

        args.add( "-d" );

        args.add( destinationDir.getAbsolutePath() );

        // ----------------------------------------------------------------------
        // Set the class and source paths
        // ----------------------------------------------------------------------

        List classpathEntries = config.getClasspathEntries();
        if ( classpathEntries != null && !classpathEntries.isEmpty())
        {
            args.add( "-classpath" );

            args.add( getPathString( classpathEntries ) );
        }

        List sourceLocations = config.getSourceLocations();
        if ( sourceLocations != null && !sourceLocations.isEmpty() )
        {
            //always pass source path, even if sourceFiles are declared,
            //needed for jsr269 annotation processing, see MCOMPILER-98
            args.add( "-sourcepath" );

            args.add( getPathString( sourceLocations ) );
        }

        //now add jdk 1.6 annotation processing related parameters

        if ( config.getGeneratedSourcesDirectory() != null )
        {
            config.getGeneratedSourcesDirectory().mkdirs();

            args.add( "-s" );
            args.add( config.getGeneratedSourcesDirectory().getAbsolutePath() );
        }
        if ( config.getProc() != null )
        {
            args.add( "-proc:" + config.getProc() );
        }
        if ( config.getAnnotationProcessors() != null )
        {
            args.add("-processor");
            String[] procs = config.getAnnotationProcessors();
            StringBuilder buffer = new StringBuilder();
            for (int i = 0; i < procs.length; i++)
            {
                if (i > 0)
                {
                    buffer.append( "," );
                }

                buffer.append( procs[i] );
            }
            args.add( buffer.toString() );
        }

        if ( config.isOptimize() )
        {
            args.add( "-O" );
        }

        if ( config.isDebug() )
        {
            if ( StringUtils.isNotEmpty( config.getDebugLevel() ) )
            {
                args.add( "-g:" + config.getDebugLevel() );
            }
            else
            {
                args.add( "-g" );
            }
        }

        if ( config.isVerbose() )
        {
            args.add( "-verbose" );
        }

        if ( config.isShowDeprecation() )
        {
            args.add( "-deprecation" );

            // This is required to actually display the deprecation messages
            config.setShowWarnings( true );
        }

        if ( !config.isShowWarnings() )
        {
            args.add( "-nowarn" );
        }

        // TODO: this could be much improved
        if ( StringUtils.isEmpty( config.getTargetVersion() ) )
        {
            // Required, or it defaults to the target of your JDK (eg 1.5)
            args.add( "-target" );
            args.add( "1.1" );
        }
        else
        {
            args.add( "-target" );
            args.add( config.getTargetVersion() );
        }

        if ( !suppressSource( config ) && StringUtils.isEmpty( config.getSourceVersion() ) )
        {
            // If omitted, later JDKs complain about a 1.1 target
            args.add( "-source" );
            args.add( "1.3" );
        }
        else if ( !suppressSource( config ) )
        {
            args.add( "-source" );
            args.add( config.getSourceVersion() );
        }

        if ( !suppressEncoding( config ) && !StringUtils.isEmpty( config.getSourceEncoding() ) )
        {
            args.add( "-encoding" );
            args.add( config.getSourceEncoding() );
        }

        for ( Iterator it = config.getCustomCompilerArguments().entrySet().iterator(); it.hasNext(); )
        {
            Map.Entry entry = (Map.Entry) it.next();

            String key = (String) entry.getKey();

            if ( StringUtils.isEmpty( key ) )
            {
                continue;
            }

            args.add( key );

            String value = (String) entry.getValue();

            if ( StringUtils.isEmpty( value ) )
            {
                continue;
            }

            args.add( value );
        }

        return args;
    }

add the method:
    List compileInProcessJSR199(List<String> args, String[] sources)
    {
      
        List<CompilerError> messages = new ArrayList<CompilerError>();
              
        JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
        getLogger().info("javac =" + javac);
              
        getLogger().info("args = " + args);

        DiagnosticCollector<JavaFileObject> filesDiagnostics = new DiagnosticCollector<JavaFileObject>();
      
        StandardJavaFileManager filesManager = javac.getStandardFileManager(filesDiagnostics, null, null);
      
        Iterable<? extends JavaFileObject> compilationUnit = filesManager.getJavaFileObjects(sources);
                      
        javac.getTask(null, null, filesDiagnostics, args, null, compilationUnit).call();
      
        for (Diagnostic<? extends JavaFileObject> diag: filesDiagnostics.getDiagnostics())
        {
          messages.add(new CompilerError(diag.toString(), diag.getKind() == Kind.ERROR));
        }
      
        try
        {
          filesManager.close();
        }
        catch (IOException ex)
        {
          messages.add(new CompilerError("ex closing file manager: " + ex.getMessage(), false));
        }              
      
        return messages;
    }

5. Compile the modifed code
skip the tests as some are now failing due to the different way to handle errors/warnings
 cd ~/dev/plexus-compiler-javac
mvn -DskipTests=true  install
6. Set JAVA_HOME
 export JAVA_HOME=~/dev/jigsaw/build/linux-i586/jdk-module-image
Custom Maven plugin:
1. Create the directories
mkdir -p ~/dev/jigsaw-maven-plugin/src/main/java/lh/jigsaw/plugin
mkdir -p ~/dev/jigsaw-maven-plugin/src/main/resources/META-INF/plexus
2. Add the pom
gedit ~/dev/jigsaw-maven-plugin/pom.xml &

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>lh.jigsaw</groupId>
  <artifactId>jigsaw-maven-plugin</artifactId>
  <packaging>maven-plugin</packaging>
  <version>0.1-SNAPSHOT</version>

  <name>Jigsaw Maven Plugin</name>
  <description>Maven2 plugin for creating Jigsaw modules</description>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3.2</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
        <dependencies>            <dependency>              <groupId>org.codehaus.plexus</groupId>              <artifactId>plexus-compiler-javac</artifactId>              <version>1.8.1-jigsaw</version>              <exclusions>                <exclusion>                  <groupId>org.codehaus.plexus</groupId>                  <artifactId>plexus-component-api</artifactId>                </exclusion>              </exclusions>            </dependency>
        </dependencies>
      </plugin>
    </plugins>
  </build>
  <dependencies>
    <dependency>
      <groupId>org.apache.maven</groupId>
      <artifactId>maven-plugin-api</artifactId>
      <version>2.0.4</version>
    </dependency>
    <dependency>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>2.3.2</version>
    </dependency>
   
    <dependency>
      <groupId>org.codehaus.plexus</groupId>
      <artifactId>plexus-compiler-api</artifactId>
      <version>1.8.1</version>
      <exclusions>
        <exclusion>
          <groupId>org.codehaus.plexus</groupId>
          <artifactId>plexus-component-api</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.codehaus.plexus</groupId>
      <artifactId>plexus-compiler-manager</artifactId>
      <version>1.8.1</version>
      <exclusions>
        <exclusion>
          <groupId>org.codehaus.plexus</groupId>
          <artifactId>plexus-component-api</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.codehaus.plexus</groupId>
      <artifactId>plexus-compiler-javac</artifactId>
      <version>1.8.1-jigsaw</version>
      <exclusions>
        <exclusion>
          <groupId>org.codehaus.plexus</groupId>
          <artifactId>plexus-component-api</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.codehaus.plexus</groupId>
      <artifactId>plexus-container-default</artifactId>
      <version>1.0-alpha-9-stable-1</version>
    </dependency>

    <dependency>
      <groupId>org.codehaus.plexus</groupId>
      <artifactId>plexus-utils</artifactId>
      <version>2.0.5</version>
    </dependency>

    <dependency>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-resources-plugin</artifactId>
      <version>2.5</version>
    </dependency>
       
    <dependency>
      <groupId>org.apache.maven</groupId>
      <artifactId>maven-toolchain</artifactId>
      <version>1.0</version>
    </dependency>
       
  </dependencies>
</project>
3. Add the components.xml
gedit ~/dev/jigsaw-maven-plugin/src/main/resources/META-INF/plexus/components.xml &

<component-set>
  <!-- this defines a custom life cycle for .jmod -->
  <components>
    <component>
      <role>org.apache.maven.lifecycle.mapping.LifecycleMapping</role>
      <role-hint>jmod</role-hint>
      <implementation>org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping</implementation>
      <configuration>
        <lifecycles>
          <lifecycle>
            <id>default</id>
            <phases>
              <initialize>lh.jigsaw:jigsaw-maven-plugin:initialise</initialize>
              <process-resources>org.apache.maven.plugins:maven-resources-plugin:resources</process-resources>
              <compile>lh.jigsaw:jigsaw-maven-plugin:lh-compile</compile>
              <process-test-resources>org.apache.maven.plugins:maven-resources-plugin:testResources</process-test-resources>
              <test-compile>org.apache.maven.plugins:maven-compiler-plugin:testCompile</test-compile>
              <test>org.apache.maven.plugins:maven-surefire-plugin:test</test>
              <package>lh.jigsaw:jigsaw-maven-plugin:jpkg,org.apache.maven.plugins:maven-jar-plugin:jar</package>
              <install>lh.jigsaw:jigsaw-maven-plugin:jmod,org.apache.maven.plugins:maven-install-plugin:install</install>
              <deploy>org.apache.maven.plugins:maven-deploy-plugin:deploy</deploy>
            </phases>
          </lifecycle>
        </lifecycles>
      </configuration>
    </component>
    <component>
      <role>org.apache.maven.artifact.handler.ArtifactHandler</role>
      <role-hint>jmod</role-hint>
      <implementation>org.apache.maven.artifact.handler.DefaultArtifactHandler</implementation>
      <configuration>
        <extension>jmod</extension>
        <type>jmod</type>
        <packaging>jmod</packaging>
        <language>java</language>
        <addedToClasspath>true</addedToClasspath>
      </configuration>
    </component>
  </components>
</component-set>
 4. Add InitialiseMojo:
gedit ~/dev/jigsaw-maven-plugin/src/main/java/lh/jigsaw/plugin/InitialiseMojo.java &

package lh.jigsaw.plugin;

import java.io.File;
import java.io.IOException;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.openjdk.jigsaw.SimpleLibrary;

/**
 * Initialise the library if it doesn't exist, will fail the build if the directory is not a valid library<br>
 * It will use ${project.build.directory}/library as the default location for the project library, the JDK library
 * as its parent.
 *
 * @requiresProject
 * @goal initialise
 * @requiresDependencyResolution runtime
 * @phase initialize
 *
 * @author ludovic
 */
public class InitialiseMojo extends AbstractMojo
{
  /**
    * library directory
    *
    * @parameter default-value="${project.build.directory}/library"
    * @required
    */
  private File libraryDirectory; 

  @Override
  public void execute() throws MojoExecutionException, MojoFailureException
  {
    getLog().info("InitialiseMojo");
    try
    {     
      if (libraryDirectory.exists())
      {
        SimpleLibrary.open(libraryDirectory);
      }
      else
      {
        File homeLibrary = new File(System.getProperty("java.home"),
                                    "lib/modules");
        SimpleLibrary.create(libraryDirectory, homeLibrary);
      }
     
    }
    catch (IOException ex)
    {
      throw new MojoExecutionException("Library '" + libraryDirectory + "' is not valid.", ex);
    }
  } 
}
5. Add  AbstractCompilerMojo:
This is needed so that the CompilerMojo subclass parent get its dependencies properly injected. The sole change from the original from the Maven compiler plugin is the package name.
gedit ~/dev/jigsaw-maven-plugin/src/main/java/lh/jigsaw/plugin/AbstractCompilerMojo.java &

package lh.jigsaw.plugin;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.CompilationFailureException;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.toolchain.Toolchain;
import org.apache.maven.toolchain.ToolchainManager;
import org.codehaus.plexus.compiler.Compiler;
import org.codehaus.plexus.compiler.CompilerConfiguration;
import org.codehaus.plexus.compiler.CompilerError;
import org.codehaus.plexus.compiler.CompilerException;
import org.codehaus.plexus.compiler.CompilerOutputStyle;
import org.codehaus.plexus.compiler.manager.CompilerManager;
import org.codehaus.plexus.compiler.manager.NoSuchCompilerException;
import org.codehaus.plexus.compiler.util.scan.InclusionScanException;
import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner;
import org.codehaus.plexus.compiler.util.scan.mapping.SingleTargetSourceMapping;
import org.codehaus.plexus.compiler.util.scan.mapping.SourceMapping;
import org.codehaus.plexus.compiler.util.scan.mapping.SuffixMapping;
import org.codehaus.plexus.util.ReaderFactory;
import org.codehaus.plexus.util.StringUtils;

/**
 * TODO: At least one step could be optimized, currently the plugin will do two
 * scans of all the source code if the compiler has to have the entire set of
 * sources. This is currently the case for at least the C# compiler and most
 * likely all the other .NET compilers too.
 *
 * @author others
 * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
 * @version $Id: AbstractCompilerMojo.java 990573 2010-08-29 12:12:21Z bentmann $
 * @since 2.0
 */
public abstract class AbstractCompilerMojo
    extends AbstractMojo
{
    // ----------------------------------------------------------------------
    // Configurables
    // ----------------------------------------------------------------------

    /**
     * Indicates whether the build will continue even if there are compilation errors; defaults to true.
     *
     * @parameter expression="${maven.compiler.failOnError}" default-value="true"
     * @since 2.0.2
     */
    private boolean failOnError = true;

    /**
     * Set to true to include debugging information in the compiled class files.
     *
     * @parameter expression="${maven.compiler.debug}" default-value="true"
     */
    private boolean debug = true;

    /**
     * Set to true to show messages about what the compiler is doing.
     *
     * @parameter expression="${maven.compiler.verbose}" default-value="false"
     */
    private boolean verbose;

    /**
     * Sets whether to show source locations where deprecated APIs are used.
     *
     * @parameter expression="${maven.compiler.showDeprecation}" default-value="false"
     */
    private boolean showDeprecation;

    /**
     * Set to true to optimize the compiled code using the compiler's optimization methods.
     *
     * @parameter expression="${maven.compiler.optimize}" default-value="false"
     */
    private boolean optimize;

    /**
     * Set to true to show compilation warnings.
     *
     * @parameter expression="${maven.compiler.showWarnings}" default-value="false"
     */
    private boolean showWarnings;

    /**
     * The -source argument for the Java compiler.
     *
     * @parameter expression="${maven.compiler.source}" default-value="1.5"
     */
    protected String source;

    /**
     * The -target argument for the Java compiler.
     *
     * @parameter expression="${maven.compiler.target}" default-value="1.5"
     */
    protected String target;

    /**
     * The -encoding argument for the Java compiler.
     *
     * @parameter expression="${encoding}" default-value="${project.build.sourceEncoding}"
     */
    private String encoding;

    /**
     * Sets the granularity in milliseconds of the last modification
     * date for testing whether a source needs recompilation.
     *
     * @parameter expression="${lastModGranularityMs}" default-value="0"
     */
    private int staleMillis;

    /**
     * The compiler id of the compiler to use. See this
     * <a href="non-javac-compilers.html">guide</a> for more information.
     *
     * @parameter expression="${maven.compiler.compilerId}" default-value="javac"
     */
    private String compilerId;

    /**
     * Version of the compiler to use, ex. "1.3", "1.5", if fork is set to true.
     *
     * @parameter expression="${maven.compiler.compilerVersion}"
     */
    private String compilerVersion;

    /**
     * Allows running the compiler in a separate process.
     * If "false" it uses the built in compiler, while if "true" it will use an executable.
     *
     * @parameter expression="${maven.compiler.fork}" default-value="false"
     */
    private boolean fork;

    /**
     * Initial size, in megabytes, of the memory allocation pool, ex. "64", "64m"
     * if fork is set to true.
     *
     * @parameter expression="${maven.compiler.meminitial}"
     * @since 2.0.1
     */
    private String meminitial;

    /**
     * Sets the maximum size, in megabytes, of the memory allocation pool, ex. "128", "128m"
     * if fork is set to true.
     *
     * @parameter expression="${maven.compiler.maxmem}"
     * @since 2.0.1
     */
    private String maxmem;

    /**
     * Sets the executable of the compiler to use when fork is true.
     *
     * @parameter expression="${maven.compiler.executable}"
     */
    private String executable;

    /**
     * <p>
     * Sets whether annotation processing is performed or not. Only applies to JDK 1.6+
     * If not set, both compilation and annotation processing are performed at the same time.
     * </p>
     * <p>
     * Allowed values are:
     *    none - no annotation processing is performed.
     *    only - only annotation processing is done, no compilation.
     * </p>
     *
     * @parameter
     * @since 2.2
     */
    private String proc;

    /**
     * <p>
     *  Names of annotation processors to run. Only applies to JDK 1.6+
     * If not set, the default annotation processors discovery process applies.
     * </p>
     *
     * @parameter
     * @since 2.2
     */
    private String[] annotationProcessors;

    /**
     * <p>
     * Sets the arguments to be passed to the compiler (prepending a dash) if fork is set to true.
     * </p>
     * <p>
     * This is because the list of valid arguments passed to a Java compiler
     * varies based on the compiler version.
     * </p>
     *
     * @parameter
     * @since 2.0.1
     */
    protected Map<String, String> compilerArguments;

    /**
     * <p>
     * Sets the unformatted argument string to be passed to the compiler if fork is set to true.
     * </p>
     * <p>
     * This is because the list of valid arguments passed to a Java compiler
     * varies based on the compiler version.
     * </p>
     *
     * @parameter
     */
    protected String compilerArgument;

    /**
     * Sets the name of the output file when compiling a set of
     * sources to a single file.
     *
     * @parameter expression="${project.build.finalName}"
     */
    private String outputFileName;
   
    /**
     * Keyword list to be appended to the -g  command-line switch. Legal values are none or a comma-separated list of the following keywords: lines, vars, and source.
     * If debuglevel is not specified, by default, nothing will be appended to -g. If debug is not turned on, this attribute will be ignored.
     *
     * @parameter expression="${maven.compiler.debuglevel}"
     * @since 2.1
     */
    private String debuglevel;   

    /** @component */
    private ToolchainManager toolchainManager;
   
    // ----------------------------------------------------------------------
    // Read-only parameters
    // ----------------------------------------------------------------------

    /**
     * The directory to run the compiler from if fork is true.
     *
     * @parameter default-value="${basedir}"
     * @required
     * @readonly
     */
    private File basedir;

    /**
     * The target directory of the compiler if fork is true.
     *
     * @parameter default-value="${project.build.directory}"
     * @required
     * @readonly
     */
    private File buildDirectory;

    /**
     * Plexus compiler manager.
     *
     * @component
     */
    private CompilerManager compilerManager;
   
    /**
     * The current build session instance. This is used for
     * toolchain manager API calls.
     *
     * @parameter default-value="${session}"
     * @required
     * @readonly
     */
    private MavenSession session;

    protected abstract SourceInclusionScanner getSourceInclusionScanner( int staleMillis );

    protected abstract SourceInclusionScanner getSourceInclusionScanner( String inputFileEnding );

    protected abstract List<String> getClasspathElements();

    protected abstract List<String> getCompileSourceRoots();

    protected abstract File getOutputDirectory();
   
    protected abstract String getSource();
   
    protected abstract String getTarget();
   
    protected abstract String getCompilerArgument();
   
    protected abstract Map<String, String> getCompilerArguments();

    protected abstract File getGeneratedSourcesDirectory();

    @SuppressWarnings( "unchecked" )
    public void execute()
        throws MojoExecutionException, CompilationFailureException
    {
        // ----------------------------------------------------------------------
        // Look up the compiler. This is done before other code than can
        // cause the mojo to return before the lookup is done possibly resulting
        // in misconfigured POMs still building.
        // ----------------------------------------------------------------------

        Compiler compiler;

        getLog().debug( "Using compiler '" + compilerId + "'." );

        try
        {
            compiler = compilerManager.getCompiler( compilerId );
        }
        catch ( NoSuchCompilerException e )
        {
            throw new MojoExecutionException( "No such compiler '" + e.getCompilerId() + "'." );
        }
       
        //-----------toolchains start here ----------------------------------
        //use the compilerId as identifier for toolchains as well.
        Toolchain tc = getToolchain();
        if ( tc != null )
        {
            getLog().info( "Toolchain in compiler-plugin: " + tc );
            if ( executable  != null )
            {
                getLog().warn( "Toolchains are ignored, 'executable' parameter is set to " + executable );
            }
            else
            {
                fork = true;
                //TODO somehow shaky dependency between compilerId and tool executable.
                executable = tc.findTool( compilerId );
            }
        }
        // ----------------------------------------------------------------------
        //
        // ----------------------------------------------------------------------

        List<String> compileSourceRoots = removeEmptyCompileSourceRoots( getCompileSourceRoots() );

        if ( compileSourceRoots.isEmpty() )
        {
            getLog().info( "No sources to compile" );

            return;
        }

        if ( getLog().isDebugEnabled() )
        {
            getLog().debug( "Source directories: " + compileSourceRoots.toString().replace( ',', '\n' ) );
            getLog().debug( "Classpath: " + getClasspathElements().toString().replace( ',', '\n' ) );
            getLog().debug( "Output directory: " + getOutputDirectory() );
        }

        // ----------------------------------------------------------------------
        // Create the compiler configuration
        // ----------------------------------------------------------------------

        CompilerConfiguration compilerConfiguration = new CompilerConfiguration();

        compilerConfiguration.setOutputLocation( getOutputDirectory().getAbsolutePath() );

        compilerConfiguration.setClasspathEntries( getClasspathElements() );

        compilerConfiguration.setSourceLocations( compileSourceRoots );

        compilerConfiguration.setOptimize( optimize );

        compilerConfiguration.setDebug( debug );

        if ( debug && StringUtils.isNotEmpty( debuglevel ) )
        {
            String[] split = StringUtils.split( debuglevel, "," );
            for ( int i = 0; i < split.length; i++ )
            {
                if ( !( split[i].equalsIgnoreCase( "none" ) || split[i].equalsIgnoreCase( "lines" )
                    || split[i].equalsIgnoreCase( "vars" ) || split[i].equalsIgnoreCase( "source" ) ) )
                {
                    throw new IllegalArgumentException( "The specified debug level: '" + split[i]
                        + "' is unsupported. " + "Legal values are 'none', 'lines', 'vars', and 'source'." );
                }
            }
            compilerConfiguration.setDebugLevel( debuglevel );
        }       
       
        compilerConfiguration.setVerbose( verbose );

        compilerConfiguration.setShowWarnings( showWarnings );

        compilerConfiguration.setShowDeprecation( showDeprecation );

        compilerConfiguration.setSourceVersion( getSource() );

        compilerConfiguration.setTargetVersion( getTarget() );

        compilerConfiguration.setProc( proc );

        compilerConfiguration.setGeneratedSourcesDirectory( getGeneratedSourcesDirectory() );

        compilerConfiguration.setAnnotationProcessors( annotationProcessors );

        compilerConfiguration.setSourceEncoding( encoding );
       
        Map<String, String> effectiveCompilerArguments = getCompilerArguments();

        String effectiveCompilerArgument = getCompilerArgument();

        if ( ( effectiveCompilerArguments != null ) || ( effectiveCompilerArgument != null ) )
        {
            LinkedHashMap<String, String> cplrArgsCopy = new LinkedHashMap<String, String>();
            if ( effectiveCompilerArguments != null )
            {
                for ( Map.Entry<String, String> me : effectiveCompilerArguments.entrySet() )
                {
                    String key = (String) me.getKey();
                    String value = (String) me.getValue();
                    if ( !key.startsWith( "-" ) )
                    {
                        key = "-" + key;
                    }
                    cplrArgsCopy.put( key, value );
                }
            }
            if ( !StringUtils.isEmpty( effectiveCompilerArgument ) )
            {
                cplrArgsCopy.put( effectiveCompilerArgument, null );
            }
            compilerConfiguration.setCustomCompilerArguments( cplrArgsCopy );
        }

        compilerConfiguration.setFork( fork );

        if ( fork )
        {
            if ( !StringUtils.isEmpty( meminitial ) )
            {
                String value = getMemoryValue( meminitial );

                if ( value != null )
                {
                    compilerConfiguration.setMeminitial( value );
                }
                else
                {
                    getLog().info( "Invalid value for meminitial '" + meminitial + "'. Ignoring this option." );
                }
            }

            if ( !StringUtils.isEmpty( maxmem ) )
            {
                String value = getMemoryValue( maxmem );

                if ( value != null )
                {
                    compilerConfiguration.setMaxmem( value );
                }
                else
                {
                    getLog().info( "Invalid value for maxmem '" + maxmem + "'. Ignoring this option." );
                }
            }
        }

        compilerConfiguration.setExecutable( executable );

        compilerConfiguration.setWorkingDirectory( basedir );

        compilerConfiguration.setCompilerVersion( compilerVersion );

        compilerConfiguration.setBuildDirectory( buildDirectory );

        compilerConfiguration.setOutputFileName( outputFileName );

        // TODO: have an option to always compile (without need to clean)
        Set<File> staleSources;

        boolean canUpdateTarget;

        try
        {
            staleSources =
                computeStaleSources( compilerConfiguration, compiler, getSourceInclusionScanner( staleMillis ) );

            canUpdateTarget = compiler.canUpdateTarget( compilerConfiguration );

            if ( compiler.getCompilerOutputStyle().equals( CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES ) &&
                !canUpdateTarget )
            {
                getLog().info( "RESCANNING!" );
                // TODO: This second scan for source files is sub-optimal
                String inputFileEnding = compiler.getInputFileEnding( compilerConfiguration );

                Set<File> sources = computeStaleSources( compilerConfiguration, compiler,
                                                   getSourceInclusionScanner( inputFileEnding ) );

                compilerConfiguration.setSourceFiles( sources );
            }
            else
            {
                compilerConfiguration.setSourceFiles( staleSources );
            }
        }
        catch ( CompilerException e )
        {
            throw new MojoExecutionException( "Error while computing stale sources.", e );
        }

        if ( staleSources.isEmpty() )
        {
            getLog().info( "Nothing to compile - all classes are up to date" );

            return;
        }

        // ----------------------------------------------------------------------
        // Dump configuration
        // ----------------------------------------------------------------------

        if ( getLog().isDebugEnabled() )
        {
            getLog().debug( "Classpath:" );

            for ( String s : getClasspathElements() )
            {
                getLog().debug( " " + s );
            }

            getLog().debug( "Source roots:" );

            for ( String root : getCompileSourceRoots() )
            {
                getLog().debug( " " + root );
            }

            try
            {
                if ( fork )
                {
                    if ( compilerConfiguration.getExecutable() != null )
                    {
                        getLog().debug( "Excutable: " );
                        getLog().debug( " " + compilerConfiguration.getExecutable() );
                    }
                }

                String[] cl = compiler.createCommandLine( compilerConfiguration );
                if ( cl != null && cl.length > 0 )
                {
                    StringBuffer sb = new StringBuffer();
                    sb.append( cl[0] );
                    for ( int i = 1; i < cl.length; i++ )
                    {
                        sb.append( " " );
                        sb.append( cl[i] );
                    }
                    getLog().debug( "Command line options:" );
                    getLog().debug( sb );
                }
            }
            catch ( CompilerException ce )
            {
                getLog().debug( ce );
            }
        }

        // ----------------------------------------------------------------------
        // Compile!
        // ----------------------------------------------------------------------

        if ( StringUtils.isEmpty( compilerConfiguration.getSourceEncoding() ) )
        {
            getLog().warn(
                           "File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING
                               + ", i.e. build is platform dependent!" );
        }

        List<CompilerError> messages;

        try
        {
            messages = compiler.compile( compilerConfiguration );
        }
        catch ( Exception e )
        {
            // TODO: don't catch Exception
            throw new MojoExecutionException( "Fatal error compiling", e );
        }

        List<CompilerError> warnings = new ArrayList<CompilerError>();
        List<CompilerError> errors = new ArrayList<CompilerError>();
        if ( messages != null )
        {
            for ( CompilerError message : messages )
            {
                if ( message.isError() )
                {
                    errors.add( message );
                }
                else
                {
                    warnings.add( message );
                }
            }
        }

        if ( failOnError && !errors.isEmpty() )
        {
            if ( !warnings.isEmpty() )
            {
                getLog().info( "-------------------------------------------------------------" );
                getLog().warn( "COMPILATION WARNING : " );
                getLog().info( "-------------------------------------------------------------" );
                for ( CompilerError warning : warnings )
                {
                    getLog().warn( warning.toString() );
                }
                getLog().info( warnings.size() + ( ( warnings.size() > 1 ) ? " warnings " : " warning" ) );
                getLog().info( "-------------------------------------------------------------" );
            }
           
            getLog().info( "-------------------------------------------------------------" );
            getLog().error( "COMPILATION ERROR : " );
            getLog().info( "-------------------------------------------------------------" );
           
            for ( CompilerError error : errors )
            {
                    getLog().error( error.toString() );
            }
            getLog().info( errors.size() + ( ( errors.size() > 1 ) ? " errors " : " error" ) );
            getLog().info( "-------------------------------------------------------------" );
           
            throw new CompilationFailureException( errors );
        }
        else
        {
            for ( CompilerError message : messages )
            {
                getLog().warn( message.toString() );
            }
        }
    }

    private String getMemoryValue( String setting )
    {
        String value = null;

        // Allow '128' or '128m'
        if ( isDigits( setting ) )
        {
            value = setting + "m";
        }
        else
        {
            if ( ( isDigits( setting.substring( 0, setting.length() - 1 ) ) ) &&
                ( setting.toLowerCase().endsWith( "m" ) ) )
            {
                value = setting;
            }
        }
        return value;
    }

    //TODO remove the part with ToolchainManager lookup once we depend on
    //3.0.9 (have it as prerequisite). Define as regular component field then.
    private Toolchain getToolchain()
    {
        Toolchain tc = null;
        if ( toolchainManager != null )
        {
            tc = toolchainManager.getToolchainFromBuildContext( "jdk", session );
        }
        return tc;
    }

    private boolean isDigits( String string )
    {
        for ( int i = 0; i < string.length(); i++ )
        {
            if ( !Character.isDigit( string.charAt( i ) ) )
            {
                return false;
            }
        }
        return true;
    }

    @SuppressWarnings( "unchecked" )
    private Set<File> computeStaleSources( CompilerConfiguration compilerConfiguration, Compiler compiler,
                                     SourceInclusionScanner scanner )
        throws MojoExecutionException, CompilerException
    {
        CompilerOutputStyle outputStyle = compiler.getCompilerOutputStyle();

        SourceMapping mapping;

        File outputDirectory;

        if ( outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_PER_INPUT_FILE )
        {
            mapping = new SuffixMapping( compiler.getInputFileEnding( compilerConfiguration ), compiler
                .getOutputFileEnding( compilerConfiguration ) );

            outputDirectory = getOutputDirectory();
        }
        else if ( outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES )
        {
            mapping = new SingleTargetSourceMapping( compiler.getInputFileEnding( compilerConfiguration ), compiler
                .getOutputFile( compilerConfiguration ) );

            outputDirectory = buildDirectory;
        }
        else
        {
            throw new MojoExecutionException( "Unknown compiler output style: '" + outputStyle + "'." );
        }

        scanner.addSourceMapping( mapping );

        Set<File> staleSources = new HashSet<File>();

        for ( String sourceRoot : getCompileSourceRoots() )
        {
            File rootFile = new File( sourceRoot );

            if ( !rootFile.isDirectory() )
            {
                continue;
            }

            try
            {
                staleSources.addAll( scanner.getIncludedSources( rootFile, outputDirectory ) );
            }
            catch ( InclusionScanException e )
            {
                throw new MojoExecutionException(
                    "Error scanning source root: \'" + sourceRoot + "\' " + "for stale files to recompile.", e );
            }
        }

        return staleSources;
    }

    /**
     * @todo also in ant plugin. This should be resolved at some point so that it does not need to
     * be calculated continuously - or should the plugins accept empty source roots as is?
     */
    private static List<String> removeEmptyCompileSourceRoots( List<String> compileSourceRootsList )
    {
        List<String> newCompileSourceRootsList = new ArrayList<String>();
        if ( compileSourceRootsList != null )
        {
            // copy as I may be modifying it
            for ( String srcDir : compileSourceRootsList )
            {
                if ( !newCompileSourceRootsList.contains( srcDir ) && new File( srcDir ).exists() )
                {
                    newCompileSourceRootsList.add( srcDir );
                }
            }
        }
        return newCompileSourceRootsList;
    }
}
6. Add  CompilerMojo:
Jigsaw adjustments to the compiler mojo.
gedit ~/dev/jigsaw-maven-plugin/src/main/java/lh/jigsaw/plugin/CompilerMojo.java &

package lh.jigsaw.plugin;

import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.CompilationFailureException;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.compiler.util.scan.SimpleSourceInclusionScanner;
import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner;
import org.codehaus.plexus.compiler.util.scan.StaleSourceScanner;

/**
 * Jigsaw adjustments to the compiler mojo
 *
 * @since 2.0
 * @goal lh-compile
 * @phase compile
 * @threadSafe
 * @requiresDependencyResolution compile
 */
public class CompilerMojo extends AbstractCompilerMojo
{

  /**
   * @parameter expression="${project}"
   * @required
   * @readonly
   */
  private MavenProject project;

  /**
   * The source directories containing the sources to be compiled.
   *
   * @parameter default-value="${project.compileSourceRoots}"
   * @required
   * @readonly
   */
  private List<String> compileSourceRoots;

  /**
   * <p>
   * Specify where to place generated source files created by annotation processing.
   * Only applies to JDK 1.6+
   * </p>
   * @parameter default-value="${project.build.directory}/generated-sources/annotations"
   * @since 2.2
   */
  private File generatedSourcesDirectory;

  /**
   * Project classpath.
   *
   * @parameter default-value="${project.compileClasspathElements}"
   * @required
   * @readonly
   */
  private List<String> classpathElements;

  /**
   * The directory for compiled classes.
   *
   * @parameter default-value="${project.build.outputDirectory}"
   * @required
   * @readonly
   */
  private File outputDirectory;

  /**
   * Project artifacts.
   *
   * @parameter default-value="${project.artifact}"
   * @required
   * @readonly
   * @todo this is an export variable, really
   */
  private Artifact projectArtifact;

  /**
   * A list of inclusion filters for the compiler.
   *
   * @parameter
   */
  private Set<String> includes = new HashSet<>();

  /**
   * A list of exclusion filters for the compiler.
   *
   * @parameter
   */
  private Set<String> excludes = new HashSet<>();

  /**
   * library directory
   *
   * @parameter default-value="${project.build.directory}/library"
   * @required
   */
  private File libraryDirectory;

  @Override
  protected List<String> getCompileSourceRoots()
  {
    return compileSourceRoots;
  }

  @Override
  protected List<String> getClasspathElements()
  {
    return classpathElements;
  }

  @Override
  protected File getOutputDirectory()
  {
    return outputDirectory;
  }

  private void lhJigsawCompilerAdjustments()
  {
    compilerArguments = new HashMap<>(2);

    compilerArguments.put("modulepath", outputDirectory.getAbsolutePath());
    compilerArguments.put("L", libraryDirectory.getAbsolutePath());
  }

  @Override
  public void execute() throws MojoExecutionException, CompilationFailureException
  {
    lhJigsawCompilerAdjustments();

    super.execute();

    projectArtifact.setFile(outputDirectory);
  }

  @Override
  protected SourceInclusionScanner getSourceInclusionScanner(int staleMillis)
  {
    SourceInclusionScanner scanner;

    if (includes.isEmpty() && excludes.isEmpty())
    {
      scanner = new StaleSourceScanner(staleMillis);
    }
    else
    {
      if (includes.isEmpty())
      {
        includes.add("**/*.java");
      }
      scanner = new StaleSourceScanner(staleMillis, includes, excludes);
    }

    return scanner;
  }

  @Override
  protected SourceInclusionScanner getSourceInclusionScanner(String inputFileEnding)
  {
    SourceInclusionScanner scanner;

    if (includes.isEmpty() && excludes.isEmpty())
    {
      includes = Collections.singleton("**/*." + inputFileEnding);
      scanner = new SimpleSourceInclusionScanner(includes, Collections.EMPTY_SET);
    }
    else
    {
      if (includes.isEmpty())
      {
        includes.add("**/*." + inputFileEnding);
      }
      scanner = new SimpleSourceInclusionScanner(includes, excludes);
    }

    return scanner;
  }

  @Override
  protected String getSource()
  {
    return source;
  }

  @Override
  protected String getTarget()
  {
    return target;
  }

  @Override
  protected String getCompilerArgument()
  {
    return compilerArgument;
  }

  @Override
  protected Map<String, String> getCompilerArguments()
  {
    return compilerArguments;
  }

  @Override
  protected File getGeneratedSourcesDirectory()
  {
    return generatedSourcesDirectory;
  }
}
7. Add JPkgMojo:
Package the module into a .jmod file.
gedit ~/dev/jigsaw-maven-plugin/src/main/java/lh/jigsaw/plugin/JPkgMojo.java &

package lh.jigsaw.plugin;

import java.io.File;
import java.io.IOException;
import java.lang.module.ModuleInfo;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.openjdk.jigsaw.JigsawModuleSystem;
import org.openjdk.jigsaw.cli.*;

/**
 * Package the module into a jmod file.<br>
 * Drops the file into ${project.build.directory}/module
 *
 * @requiresProject
 * @goal jpkg
 * @requiresDependencyResolution runtime
 * @phase package
 *
 * @author ludovic
 */
public class JPkgMojo extends AbstractMojo
{

  /**
   * Library directory
   *
   * @parameter default-value="${project.build.directory}/library"
   * @required
   */
  private File libraryDirectory;

  /**
   * Classes directory.
   *
   * @parameter expression="${project.build.outputDirectory}"
   * @required
   * @readonly
   */
  private File classesDir;

  /**
   * Output directory.
   *
   * @parameter expression="${project.build.directory}/module"
   * @required
   * @readonly
   */
  private File outputDir;

  /**
   * Module name
   *
   * @parameter expression="${moduleName}"
   * @readonly
   */
  private String moduleName;

  @Override
  public void execute() throws MojoExecutionException, MojoFailureException
  {
    getLog().info("JPkgMojo");
    try
    {
      if (outputDir.exists())
      {
        deleteDir(outputDir.toPath());
      }

      outputDir.mkdirs();

      String[] params = new String[]
      {
        "-L",
        libraryDirectory.getAbsolutePath(),
        "-m",
        classesDir.getAbsolutePath(),
        "-d",
        outputDir.getAbsolutePath(),
        "jmod",
        getModuleName()
      };
     
      getLog().info(Arrays.toString(params));
     
      Packager.run(params);
    }
    catch (MojoExecutionException ex)
    {
      throw ex;
    }
    catch (Exception ex)
    {
      throw new MojoExecutionException("Could not package '" + moduleName + "'.", ex);
    }

  }

  private String getModuleName() throws MojoExecutionException
  {
    if (moduleName == null)
    {
      try
      {
        Path path = classesDir.toPath().resolve("module-info.class");
        byte[] bytes = java.nio.file.Files.readAllBytes(path);
        JigsawModuleSystem jms = JigsawModuleSystem.instance();
        ModuleInfo mif = jms.parseModuleInfo(bytes);
        moduleName = mif.id().name();
        getLog().info("module name read from module-info = " + moduleName);

      }
      catch (IOException ex)
      {
        throw new MojoExecutionException("Could not read the module name, check that module-info.class exist.", ex);
      }
    }
    return moduleName;
  }

  private static void deleteDir(Path dir) throws IOException
  {
    try (DirectoryStream<Path> s = Files.newDirectoryStream(dir))
    {
      for (Path p : s)
      {
        if (Files.isDirectory(p))
        {
          deleteDir(p);
        }
        Files.delete(p);
      }
    }
  }
}
8 Add JModMojo:
Installs the jmod file into the library.
gedit ~/dev/jigsaw-maven-plugin/src/main/java/lh/jigsaw/plugin/JModMojo.java &

package lh.jigsaw.plugin;

import java.io.File;
import java.io.IOException;
import java.lang.module.ModuleId;
import java.lang.module.ModuleInfo;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.openjdk.jigsaw.JigsawModuleSystem;
import org.openjdk.jigsaw.cli.Librarian;

/**
 * Installs the jmod file into the library.<br>
 * Remove the module from that library if it already exist before adding the new content.
 *
 * @requiresProject
 * @goal jmod
 * @requiresDependencyResolution runtime
 * @phase install
 *
 * @author ludovic
 */
public class JModMojo extends AbstractMojo
{

  /**
   * library directory
   *
   * @parameter default-value="${project.build.directory}/library"
   * @required
   */
  private File libraryDirectory;

  /**
   * Classes directory.
   *
   * @parameter expression="${project.build.outputDirectory}"
   * @required
   * @readonly
   */
  private File classesDir;

  /**
   * module dir
   *
   * @parameter expression="${project.build.directory}/module/"
   * @required
   * @readonly
   */
  private File moduleDir;

  /**
   * Module name
   *
   * @parameter expression="${moduleName}""
   * @readonly
   */
  private String moduleName;

  @Override
  public void execute() throws MojoExecutionException, MojoFailureException
  {
    getLog().info("JModMojo");
    try
    {
      File[] files = moduleDir.listFiles();
      if (files == null)
      {
        throw new MojoExecutionException("no files in <target>/module directory");
      }
      File moduleFile = null;
      for (File f : files)
      {
        if (f.getName().endsWith("jmod"))
        {
          moduleFile = f;
          break;
        }
      }
      if (moduleFile == null)
      {
        throw new MojoExecutionException("no jmod file in <target>/module directory");
      }

      // allow to update the module by removing it, may have to reindex?
      ModuleId moduleId = getModuleId();
      Path installedModule = libraryDirectory.toPath().resolve(moduleId.name()).resolve(moduleId.version().toString());
      if (Files.exists(installedModule))
      {
        String[] params = new String[]
        {
          "-L",
          libraryDirectory.getAbsolutePath(),
          "remove",
          "-f",
          moduleId.toString()
        };

        getLog().info(Arrays.toString(params));

        Librarian.run(params);

      }

      String[] params = new String[]
      {
        "-L",
        libraryDirectory.getAbsolutePath(),
        "install",
        moduleFile.getAbsolutePath()
      };
     
      getLog().info(Arrays.toString(params));
     
      Librarian.run(params);
    }
    catch (MojoExecutionException ex)
    {
      throw ex;
    }
    catch (Exception ex)
    {
      throw new MojoExecutionException("Could not install '" + moduleName + "' into the '" + libraryDirectory + "' library.", ex);
    }

  }

  private String getModuleName() throws MojoExecutionException
  {
    if (moduleName == null)
    {
      try
      {
        Path path = classesDir.toPath().resolve("module-info.class");
        byte[] bytes = java.nio.file.Files.readAllBytes(path);
        JigsawModuleSystem jms = JigsawModuleSystem.instance();
        ModuleInfo mif = jms.parseModuleInfo(bytes);
        moduleName = mif.id().name();
        getLog().info("module name read from module-info = " + moduleName);

      }
      catch (IOException ex)
      {
        throw new MojoExecutionException("Could not read the module name, check that module-info.class exist.", ex);
      }
    }
    return moduleName;
  }

  private ModuleId getModuleId() throws MojoExecutionException
  {
    ModuleId id;
    try
    {
      Path path = classesDir.toPath().resolve("module-info.class");
      byte[] bytes = java.nio.file.Files.readAllBytes(path);
      JigsawModuleSystem jms = JigsawModuleSystem.instance();
      ModuleInfo mif = jms.parseModuleInfo(bytes);
      id = mif.id();
      getLog().info("module name read from module-info = " + moduleName);

    }
    catch (IOException ex)
    {
      throw new MojoExecutionException("Could not read the module name, check that module-info.class exist.", ex);
    }
   
    return id;
  }
}
9. Add RunMojo:
Runs the project's module from the library.
gedit ~/dev/jigsaw-maven-plugin/src/main/java/lh/jigsaw/plugin/RunMojo.java &

package lh.jigsaw.plugin;

import java.io.File;
import java.io.IOException;
import java.lang.module.ModuleInfo;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.openjdk.jigsaw.JigsawModuleSystem;

/**
 * Runs the project's module from the library.
 *
 * @requiresProject
 * @goal run
 * @requiresDependencyResolution runtime
 *
 * @author ludovic
 */
public class RunMojo extends AbstractMojo
{

  /**
   * library directory
   *
   * @parameter default-value="${project.build.directory}/library"
   * @required
   */
  private File libraryDirectory;

  /**
   * Classes directory.
   *
   * @parameter expression="${project.build.outputDirectory}"
   * @required
   * @readonly
   */
  private File classesDir;

  /**
   * Module name
   *
   * @parameter expression="${moduleName}""
   * @readonly
   */
  private String moduleName;

  /**
   * vm args
   * @parameter default-value="${vmargs}"
   */
  String vmargs;

  /**
   * args
   * @parameter default-value="${args}"
   */
  String args;

  @Override
  public void execute() throws MojoExecutionException, MojoFailureException
  {
    getLog().info("RunMojo");
    try
    {
      List<String> cmdline = new ArrayList<>();

      Path javaCmdDirPath = Paths.get(System.getProperty("java.home"));
      Path javaCmdPath = javaCmdDirPath.resolve("bin/java");
      cmdline.add(javaCmdPath.toString());

      cmdline.add("-L");
      cmdline.add(libraryDirectory.toString());

      cmdline.addAll(split(vmargs));

      cmdline.add("-m");
      cmdline.add(getModuleName());

      cmdline.addAll(split(args));

      ProcessBuilder pb = new ProcessBuilder(cmdline);
      System.out.println("cmd: " + pb.command());

      pb = pb.inheritIO();
      Process p = pb.start();
      int ret = p.waitFor();
      System.out.println("ret = " + ret);
    }
    catch (Exception ex)
    {
      getLog().error(ex);
    }

  }

  private String getModuleName()
  {
    if (moduleName == null)
    {
      try
      {
        Path path = classesDir.toPath().resolve("module-info.class");
        byte[] bytes = java.nio.file.Files.readAllBytes(path);
        JigsawModuleSystem jms = JigsawModuleSystem.instance();
        ModuleInfo mif = jms.parseModuleInfo(bytes);
        moduleName = mif.id().name();
        System.out.println("name = " + moduleName);

      }
      catch (IOException ex)
      {
        getLog().error(ex);
      }
    }
    return moduleName;
  }

  private List<String> split(String arg)
  {
    if (arg == null)
    {
      return Collections.emptyList();
    }

    String[] elts = arg.split(" ");
    return Arrays.asList(elts);
  }
}
10. Compile and install the plugin:
cd ~/dev/jigsaw-maven-plugin
mvn install
Using the plugin
See Jigsaw: Worldclock with the Maven plugin