Wednesday, 18 January 2012

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

Wednesday, 1 December 2010

Devoxx 2010

Devoxx 2010

Monday 15th

9:30 - 12:30
Developing Substantial Java Desktop Applications
with Geertjan Wielenga and Anton Epple
http://www.devoxx.com/display/Devoxx2K10/Developing+Substantial+Java+Desktop+Applications
Only 5 attending
The lab started with a presentation by about what is a Rich Client Platform, what are its advantages,
what are its alternatives, What are the key features of the Netbeans platform and development process.
Then moved onto a short exercise of porting the NetBeans Anagram sample application to the NetBeans RCP.
The lab finished with a overview of the new features in NB RCP 6.9 and of some advanced use of the platform.
Was nice to meet Geertjan Wielenga.

13:30 - 16:30
Groovy update, ecosystem, and skyrocketing to the cloud with App Engine and Gaelyk!
by Guillaume Laforge
http://www.devoxx.com/display/Devoxx2K10/Groovy+update%2C+ecosystem%2C+and+skyrocketing+to+the+cloud+with+App+Engine+and+Gaelyk%21
The talk only had 3 parts because Guillaume Laforge did not have enough valid fingers for more ;)
The first part was about Groovy.
After a brief remainder of what is Groovy and how it removes boiler plates from Java,
the talk moved into a past, present, future slicing.
The past the a reminder of the features introduced with Groovy 1.6 (JMXBuilder, out of the box OSGi, multiple assignements,
Grape, Runtime Mixins).
The present with Groovy 1.7 (annonymous inner classes for better Java support, @Grab, power asserts, improved SQL support)
The future with Groovy 1.8 (performances, native JSON, new AST, Graddle build, modularisation of the groovy deliverables, project coin/JDK7 alignments, enhanced DSL support), expected for January 2011.
The second part was about the Groovy ecosystem with a brief introduction/mention of Grails, Griffon, GPars (concurrency),
easyB (testing), spock (testing), Gmock (testing), SoapUI (testing).
The third and last part was a presentation of Gaelyk.
Overall a synthetic view of Groovy today.



16:45-17:15
Increasing developer productivity with Mylyn
by Oliver Gierke
http://www.devoxx.com/display/Devoxx2K10/Increasing+developer+productivity+with+Mylyn
An introduction to Mylyn, a plugin that filter Eclipse UI according to the current task.
The talk finished with a mention of Code2Cloud, a product from SpringSource/VMWare for which the speaker works.

17:25 - 17:55
VisualVM - multiplatform versatile monitoring solution
by Jaroslav Bachorik
http://www.devoxx.com/display/Devoxx2K10/VisualVM+-+multiplatform+versatile+monitoring+solution
Starting with an overview of VisualVM then onto its extensibility point before finishing with a case study of
a CPU Frequency Monitor extension. (Btw there are wizards for NetBeans to create extensions for VisualVM)

Tuesday 16th

09:30 - 12:30
Dive into Android
by Romain Guy and Chet Haase
http://www.devoxx.com/display/Devoxx2K10/Dive+into+Android
Presentation of some Android graphics API (layouts) and tips, agremented with some demos.

13:30 - 16:30
Hesitated between 'What's new in Hibernate: a JPA 2 perspective' and 'JavaFX 101'. I chose the former to start
as I had managed not to see it yet and because I wasn't sure the emphasis of the later was on JavaFX 2.

What's new in Hibernate: a JPA 2 perspective
by Emmanuel Bernard
http://www.devoxx.com/display/Devoxx2K10/What%27s+new+in+Hibernate++a+JPA+2+perspective
Introduced the Criteria API, which makes it easier to create complex queries (also has the ability to allow for IDE refactoring),
also talked about JPA2 locking modes (standardisation of optimistic/pessimistic lock modes).
Then the talk moved to Hibernate Search which I had already heard about so I went to the JavaFX talk.

JavaFX 101
by Richard Bair
and Jasper Potts
http://www.devoxx.com/display/Devoxx2K10/JavaFX+101
The talk focused on JavaFX 2 (at least it was talking about it from the moment I joined on)
though it managed not to show much code, concentrating on the challenges posed by the switch
to 'plain' java (object litterals, need for 'better' JavaBean, observable objects).
JavaFX 2 should also be heavy on its CSS(3) skinning (with some adjustment due to the non use of HTML),
have a WebView (embedded web browser), have convenient and complete (well except for a date picker) out of the box,
have Swing integration, use the Prism rendering pipe (no more AWT), improve media handling (much more stable).
JavaFX 2 is scheduled for 11Q1 EA, 11Q2 Beta and 11Q3 for release (GA).
A pre-alpha of Java Web Toolkit was shown. It is an alternative to Prism on HTML5, however the GA won't be before H2 2012.





16:45 - 17:15
Java EE 6: Tooling Status: what am I missing?
by Ludovic Champenois
http://www.devoxx.com/display/Devoxx2K10/Java+EE+6++Tooling+Status++what+am+I+missing
A look at the state of IDEs with regards to Java EE6, guess which one does not yet support Java EE6...



17:25 - 17:55
IzPack: because you and your end users have installation issues
by Julien Ponge
http://www.devoxx.com/display/Devoxx2K10/IzPack++because+you+and+your+end+users+have+installation+issues
Slides: http://www.slideshare.net/julien.ponge/izpack-at-devoxx-2010
Introduction to IzPack by its creator.

17:26
http://twitter.com/nmartignole/status/4570895762857984
"RDV centre ville devant l'hotel Park Inn en face Hotel Agora à 21h00 si vs voulez diner en ville #Devoxx #fun #beer faite suivre"
and meet 40ish French speaking people.





Wednesday 17th

09:30 - 10:00
Welcome and Intro
by Stephan Janssen
http://www.devoxx.com/display/Devoxx2K10/Welcome+and+Intro
A few stats and awesome demos of Parleys by Stephan Janssen. [Orange when do you upgrade my Desire to Froyo? -- 2010-12-02: they listened and did it ;)]

10:00 - 10:50
Java SE: The Road Ahead
by Mark Reinhold
http://www.devoxx.com/display/Devoxx2K10/Java+SE++The+Road+Ahead
Mark Reinhold announced the timeline to Java SE7:
2010-12-16: Feature Complete
2011-05-11: First RC
2011-07-28: General Availability (Release)
He also announced the filling of the JSRs for Project Coin (334), Project Lambda (335), Java 7 (336), and Java 8 (337)
[would have been 'sweet' to have 337 as Java 7 and 338 as Java 8]
And reminded us of the themes for upcoming versions: productivity, performance, universality, serviceability, modularity and integration.

10:50 - 11:40
The State of the Web
by Dion Almaer and Ben Galbraith
http://www.devoxx.com/display/Devoxx2K10/The+State+of+the+Web

12:00 - 13:00
From Dev/Ops to DevOps. Amazing the difference one character can make.
by Patrick Debois and Kris Buytaert
http://www.devoxx.com/display/Devoxx2K10/From+Dev+Ops+to+DevOps.+Amazing+the+difference+one+character+can+make.
Talked about Vampires and Werewolves (devs and ops), with that message:
we should talk together. To aim for continuous delivery.



13:30
Photo Frenchies:





14:00 - 15:00
From Shabby to Chic
by Richard Bair and Jasper Potts
http://www.devoxx.com/display/Devoxx2K10/From+Shabby+to+Chic
Richard and Jasper showed how to make a chic JIRA client using JavaFX and CSS.




15:10 - 16:10
JavaFX Your Way: Building JavaFX Applications with Alternative Languages
by Stephen Chin
http://www.devoxx.com/display/Devoxx2K10/JavaFX+Your+Way++Building+JavaFX+Applications+with+Alternative+Languages
Slides: http://www.slideshare.net/steveonjava/javafx-your-way-devoxx-version
Interesting to see how a bit of JavaFX code looks like in popular alternative languages.

16:40 - 17:40
HK2: Oracle WebLogic Server, Oracle GlassFish Server, and Beyond
by Jerome Dochez
http://www.devoxx.com/display/Devoxx2K10/HK2++Oracle+WebLogic+Server%2C+Oracle+GlassFish+Server%2C+and+Beyond
The presentation started with an introduction to HK2 (somehow slides are more expressive than the JavaDoc),
then showed a/the migration path for WebLogic

17:50 - 18:50
Android Graphics and Animations
by Romain Guy and Chet Haase
http://www.devoxx.com/display/Devoxx2K10/Android+Graphics+and+Animations
What I needed, schemas of the drawing architecture in Android, plus a few tips.



19:00 - 20:00
The state of Java SE 7
by Mark Reinhold, Joe Darcy and Brian Goetz
http://www.devoxx.com/display/Devoxx2K10/The+state+of+Java+SE+7
Too many people and no mic, I left pretty quickly.

20:00 - 21:00
JPA Futures
by Linda DeMichiel
http://www.devoxx.com/display/Devoxx2K10/JPA+Futures
Some slides with the candidate features for JPA 2.1, the audience was for each slide asked to comment / ask questions.
Although I don't know enough yet of JPA this proved interresting.

21:00 - 22:00
Visage Android Workshop
by Stephen Chin
http://www.devoxx.com/display/Devoxx2K10/Visage+Android+Workshop
The BOF I was looking forward to. Visage (more or less JavaFX script) on Android!
Stephen, now I want to see the first drop of the SDK.


Thursday 18th

09:30 - 10:40
The Future Roadmap of Java EE
by Jerome Dochez, Linda DeMichiel and Paul Sandoz
http://www.devoxx.com/display/Devoxx2K10/The+Future+Roadmap+of+Java+EE
The cloud, modularity based on Java SE7 modularity, HTML5 and JSON support, JPA 2.1 and JAX-RS future.

10:50 - 11:50
Getting Things Done for Programmers
by Kito Mann
http://www.devoxx.com/display/Devoxx2K10/Getting+Things+Done+for+Programmers
Based on the book "Getting Things Done" by David Allen.
The main question still remains how does one get only one inbox?
Or how do you tell somebody coming to you that your putting his request in your inbox
and will handle it once you'll have processed the preceeding items?
On the plus side, the talk listed a few tools to investigate.



12:00 - 13:00
OpenJDK
by Dalibor Topic
http://www.devoxx.com/display/Devoxx2K10/OpenJDK
An introduction to OpenJDK with pointers for building it and to further information about it.
The main take away for me: I should try the ALLOW_DOWNLOADS=true on my make line next time that way
I may have 2 less files to edit once the java.net URLs will have been updated.
I disagree with Dalibor when it comes to the build duration, it is closer to the content of a home coffee machine...
(and you can turn the heater down too)

14:00 - 15:00
Extending VisualVM
by Kirk Pepperdine
http://www.devoxx.com/display/Devoxx2K10/Extending+VisualVM
I missed the beginning (well half) of it, but this seemed to have a few code slides that could come handy.

15:10 - 16:10
The JavaPosse Live
by Dick Wall, Carl Quinn and Joe Nuxoll
http://www.devoxx.com/display/Devoxx2K10/JavaPosse+Live
Audio: http://javaposse.com/java-posse-330-devoxx-2010
'nough said.






16:40 - 17:40
The Modular Java Platform
by Mark Reinhold
http://www.devoxx.com/display/Devoxx2K10/The+Modular+Java+Platform
Mark Reinhold: "I only came here so that I could have a seat for the next session"
Either he was not the only one thinking that way or Modularity/Jigsaw is getting some interest
as the room was far from empty. Also new was the emphase on Maven (good).
And the big question:

Answers: OSGi strongly discourage bundle dependencies (for OSGi packages dependencies should be the norm),
which disjoint the runtime boundary from the packaging and mapping to native packages and Maven.
Sums up as: "The unit of reuse is the unit of release" Robert "Uncle Bob" Martin

17:50 - 18:50
Java Puzzlers - Scraping the Bottom of the Barrel
by Joshua Bloch and William Pugh
http://www.devoxx.com/display/Devoxx2K10/Java+Puzzlers+-+Scraping+the+Bottom+of+the+Barrel
The famous Java Puzzlers by one brother who created a few and the other who busted a few with his not known tool.
Good show. A few things to look at in the code...



20:00 - 21:00
The annual Java User Group BOF
by Stephan Janssen
http://www.devoxx.com/display/Devoxx2K10/The+annual+Java+User+Group+BOF
Discussion between JUG leaders and community outreach representatives from Oracle about how they can
interract together. Oracle would like to only have a subset of representatives from the JUGs to reduce
the number of people to talk to and improve bi-directional channels (no strings attached though).
For the French JUGs this does not really pose an issue (that is already more or less the case),
but other JUGs didn't quite agree. The BOF continued at the near by Axxes bar.

Friday 19th

09:30 - 10:30
The Future of Java Discussion Panel
by Joshua Bloch, Mark Reinhold, Stephen Colebourne, Juergen Hoeller, Antonio Goncalves and Bill Venners
http://www.devoxx.com/display/Devoxx2K10/The+Future+of+Java+Discussion+Panel
A few questions taken from Google moderator where asked to the experts (quite a few of the question where from some
bloke in California, USofA with a name starting with Joe and finishing with Nuxoll).
Foil hats were a great idea, bat man too.





10:40 - 11:40ish
Les CastCodeurs en direct
http://lescastcodeurs.com/2010/11/les-cast-codeurs-podcast-episode-31-special-devoxx-2010/
Summary of Devoxx in French in front of 600 people according to the organisers and a few less according to the photo.
Apparently there was some NoSQL at Devoxx, ah well.. may watch some pres on Parleys.







 Then a good chat about Jigsaw,
 A photo of the annual whiteboard score taking:

(he still had it when boarding the train to Brussels)

And time to head back...
(well actually I had plenty of time since the Thalys, was, like last year, very late)

Till next year...

Monday, 30 August 2010

Jigsaw: WorldClock with Maven

In the previous post I built WorldClock using just the command line, this time I'll use Maven for compiling.

1. Get Maven
sudo apt-get install maven2
Set JAVA_HOME to Jigsaw, so that Maven uses Jigsaw:
export JAVA_HOME=~/dev/jigsaw/build/linux-i586/jdk-module-image


2. Adjusting the paths for Maven
Create new directories:
mkdir -p ~/dev/worldclock/panel/src/main/java

mkdir -p ~/dev/worldclock/application/src/main/java

Move the files:
mv /home/ludovic/dev/worldclock/panel/src/lh.worldclock.panel ~/dev/worldclock/panel/src/main/java
mv /home/ludovic/dev/worldclock/panel/resources ~/dev/worldclock/panel/src/main/

mv /home/ludovic/dev/worldclock/application/src/lh.worldclock.panel ~/dev/worldclock/application/src/main/java
mv /home/ludovic/dev/worldclock/application/resources ~/dev/worldclock/application/src/main/

3. Add the poms



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>jar</packaging>
  <version>0.7-SNAPSHOT</version>
  <name>Worldclock Panel</name>
  <url>https://worldclock-application.dev.java.net/</url>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
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>jar</packaging>
  <version>0.7-SNAPSHOT</version>
  <name>Worldclock Application</name>
  <url>https://worldclock-application.dev.java.net/</url>
  <dependencies>
    <dependency>
      <groupId>lh.worldclock</groupId>
      <artifactId>panel</artifactId>
      <version>0.7-SNAPSHOT</version>
    </dependency>
  </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <compilerArguments>
                        <version />
                        <L>../modules</L>
                    </compilerArguments>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
(somehow using <compilerArgument> -L ../modules</compilerArgument> does not work)

4. Recreate the library
rm -rf ~/dev/worldclock/modules
$JIG/bin/jmod create -L ~/dev/worldclock/modules
 5. Compile and add to the library
Panel:
cd ~/dev/worldclock/panel

mvn install

$JIG/bin/jmod -L ~/dev/worldclock/modules install ~/dev/worldclock/panel/build -r ~/dev/worldclock/panel/src/main/resources lh.worldclock.panel
Application:
cd ~/dev/worldclock/application

mvn install

$JIG/bin/jmod -L ~/dev/worldclock/modules install ~/dev/worldclock/application/build -r ~/dev/worldclock/application/resources lh.worldclock.application
6. Run
$JIG/bin/java -L ~/dev/worldclock/modules -m lh.worldclock.application