You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by ti...@apache.org on 2017/09/17 20:03:12 UTC

maven-surefire git commit: [SUREFIRE-1262] Add modulepath support

Repository: maven-surefire
Updated Branches:
  refs/heads/SUREFIRE-1262_2 [created] b671a5832


[SUREFIRE-1262] Add modulepath support


Project: http://git-wip-us.apache.org/repos/asf/maven-surefire/repo
Commit: http://git-wip-us.apache.org/repos/asf/maven-surefire/commit/b671a583
Tree: http://git-wip-us.apache.org/repos/asf/maven-surefire/tree/b671a583
Diff: http://git-wip-us.apache.org/repos/asf/maven-surefire/diff/b671a583

Branch: refs/heads/SUREFIRE-1262_2
Commit: b671a5832e77ea89511ce60af4e997c2aa74b2f9
Parents: 5aed843
Author: Tibor17 <ti...@apache.org>
Authored: Sat Sep 16 21:25:37 2017 +0200
Committer: Tibor17 <ti...@apache.org>
Committed: Sun Sep 17 00:09:53 2017 +0200

----------------------------------------------------------------------
 maven-surefire-common/pom.xml                   |  12 +-
 .../plugin/surefire/AbstractSurefireMojo.java   |  80 ++++-
 .../booterclient/ForkConfiguration.java         | 320 +++----------------
 .../surefire/booterclient/ForkStarter.java      |   8 +-
 .../JarClasspathForkConfiguration.java          |  59 ++++
 .../booterclient/JarForkConfiguration.java      | 292 +++++++++++++++++
 .../JarManifestForkConfiguration.java           |  69 ++++
 .../booterclient/JigsawForkConfiguration.java   |  29 ++
 .../plugin/surefire/booterclient/Platform.java  |  29 +-
 .../maven/plugin/surefire/util/Relocator.java   |  39 +--
 .../surefire/providerapi/ServiceLoader.java     |   2 +-
 .../booterclient/ForkConfigurationTest.java     |  40 ++-
 .../maven/surefire/util/RelocatorTest.java      |   6 +-
 .../plugin/surefire/SurefirePluginTest.java     |   3 +-
 pom.xml                                         |  40 +++
 15 files changed, 672 insertions(+), 356 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b671a583/maven-surefire-common/pom.xml
----------------------------------------------------------------------
diff --git a/maven-surefire-common/pom.xml b/maven-surefire-common/pom.xml
index 1bcd7c5..4ae6bb1 100644
--- a/maven-surefire-common/pom.xml
+++ b/maven-surefire-common/pom.xml
@@ -97,20 +97,16 @@
     <dependency>
       <groupId>org.apache.maven.shared</groupId>
       <artifactId>maven-common-artifact-filters</artifactId>
-      <version>1.3</version>
-      <exclusions>
-        <exclusion>
-          <groupId>org.apache.maven.shared</groupId>
-          <artifactId>maven-plugin-testing-harness</artifactId>
-        </exclusion>
-      </exclusions>
     </dependency>
     <dependency>
       <groupId>org.fusesource.jansi</groupId>
       <artifactId>jansi</artifactId>
-      <version>1.13</version>
       <scope>provided</scope>
     </dependency>
+    <!--<dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-java</artifactId>
+    </dependency>-->
     <dependency>
       <groupId>org.mockito</groupId>
       <artifactId>mockito-core</artifactId>

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b671a583/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
index 88b223a..164c203 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
@@ -43,6 +43,8 @@ import org.apache.maven.plugin.descriptor.PluginDescriptor;
 import org.apache.maven.plugin.surefire.booterclient.ChecksumCalculator;
 import org.apache.maven.plugin.surefire.booterclient.ForkConfiguration;
 import org.apache.maven.plugin.surefire.booterclient.ForkStarter;
+import org.apache.maven.plugin.surefire.booterclient.JarClasspathForkConfiguration;
+import org.apache.maven.plugin.surefire.booterclient.JarManifestForkConfiguration;
 import org.apache.maven.plugin.surefire.booterclient.Platform;
 import org.apache.maven.plugin.surefire.booterclient.ProviderDetector;
 import org.apache.maven.plugin.surefire.log.PluginConsoleLogger;
@@ -132,12 +134,13 @@ public abstract class AbstractSurefireMojo
     extends AbstractMojo
     implements SurefireExecutionParameters
 {
+    private static final String FORK_ONCE = "once";
+    private static final String FORK_ALWAYS = "always";
+    private static final String FORK_NEVER = "never";
+    private static final String FORK_PERTHREAD = "perthread";
     private static final Map<String, String> JAVA_9_MATCHER_OLD_NOTATION = singletonMap( "version", "[1.9,)" );
-
     private static final Map<String, String> JAVA_9_MATCHER = singletonMap( "version", "[9,)" );
-
     private static final Platform PLATFORM = new Platform();
-
     private static final File SYSTEM_TMP_DIR = new File( System.getProperty( "java.io.tmpdir" ) );
 
     private final ProviderDetector providerDetector = new ProviderDetector();
@@ -1507,7 +1510,7 @@ public abstract class AbstractSurefireMojo
 
     static boolean isForkModeNever( String forkMode )
     {
-        return ForkConfiguration.FORK_NEVER.equals( forkMode );
+        return FORK_NEVER.equals( forkMode );
     }
 
     protected boolean isForking()
@@ -1521,10 +1524,10 @@ public abstract class AbstractSurefireMojo
 
         if ( toolchain != null && isForkModeNever( forkMode1 ) )
         {
-            return ForkConfiguration.FORK_ONCE;
+            return FORK_ONCE;
         }
 
-        return ForkConfiguration.getEffectiveForkMode( forkMode1 );
+        return getEffectiveForkMode( forkMode1 );
     }
 
     private List<RunOrder> getRunOrders()
@@ -1972,33 +1975,55 @@ public abstract class AbstractSurefireMojo
         final Classpath bootClasspathConfiguration =
             getArtifactClasspath( shadeFire != null ? shadeFire : surefireBooterArtifact );
 
-        return new ForkConfiguration( bootClasspathConfiguration, tmpDir, getEffectiveDebugForkedProcess(),
-                                      getEffectiveJvm(),
-                                      getWorkingDirectory() != null ? getWorkingDirectory() : getBasedir(),
-                                      getProject().getModel().getProperties(),
-                                      getArgLine(), getEnvironmentVariables(), getConsoleLogger().isDebugEnabled(),
-                                      getEffectiveForkCount(), reuseForks, PLATFORM );
+        if ( getClassLoaderConfiguration().isManifestOnlyJarRequestedAndUsable() )
+        {
+            return new JarManifestForkConfiguration( bootClasspathConfiguration,
+                    tmpDir,
+                    getEffectiveDebugForkedProcess(),
+                    getWorkingDirectory() != null ? getWorkingDirectory() : getBasedir(),
+                    getProject().getModel().getProperties(),
+                    getArgLine(),
+                    getEnvironmentVariables(),
+                    getConsoleLogger().isDebugEnabled(),
+                    getEffectiveForkCount(),
+                    reuseForks,
+                    PLATFORM.withJdkExecAttributesForTests( getEffectiveJvm() ) );
+        }
+        else
+        {
+            return new JarClasspathForkConfiguration( bootClasspathConfiguration,
+                    tmpDir,
+                    getEffectiveDebugForkedProcess(),
+                    getWorkingDirectory() != null ? getWorkingDirectory() : getBasedir(),
+                    getProject().getModel().getProperties(),
+                    getArgLine(),
+                    getEnvironmentVariables(),
+                    getConsoleLogger().isDebugEnabled(),
+                    getEffectiveForkCount(),
+                    reuseForks,
+                    PLATFORM.withJdkExecAttributesForTests( getEffectiveJvm() ) );
+        }
     }
 
     private void convertDeprecatedForkMode()
     {
         String effectiveForkMode = getEffectiveForkMode();
         // FORK_ONCE (default) is represented by the default values of forkCount and reuseForks
-        if ( ForkConfiguration.FORK_PERTHREAD.equals( effectiveForkMode ) )
+        if ( FORK_PERTHREAD.equals( effectiveForkMode ) )
         {
             forkCount = String.valueOf( threadCount );
         }
-        else if ( ForkConfiguration.FORK_NEVER.equals( effectiveForkMode ) )
+        else if ( FORK_NEVER.equals( effectiveForkMode ) )
         {
             forkCount = "0";
         }
-        else if ( ForkConfiguration.FORK_ALWAYS.equals( effectiveForkMode ) )
+        else if ( FORK_ALWAYS.equals( effectiveForkMode ) )
         {
             forkCount = "1";
             reuseForks = false;
         }
 
-        if ( !ForkConfiguration.FORK_ONCE.equals( getForkMode() ) )
+        if ( !FORK_ONCE.equals( getForkMode() ) )
         {
             getConsoleLogger().warning( "The parameter forkMode is deprecated since version 2.14. "
                                                 + "Use forkCount and reuseForks instead." );
@@ -2482,7 +2507,7 @@ public abstract class AbstractSurefireMojo
     private void ensureThreadCountWithPerThread()
         throws MojoFailureException
     {
-        if ( ForkConfiguration.FORK_PERTHREAD.equals( getEffectiveForkMode() ) && getThreadCount() < 1 )
+        if ( FORK_PERTHREAD.equals( getEffectiveForkMode() ) && getThreadCount() < 1 )
         {
             throw new MojoFailureException( "Fork mode perthread requires a thread count" );
         }
@@ -3506,4 +3531,25 @@ public abstract class AbstractSurefireMojo
     {
         this.tempDir = tempDir;
     }
+
+    private static String getEffectiveForkMode( String forkMode )
+    {
+        if ( "pertest".equalsIgnoreCase( forkMode ) )
+        {
+            return FORK_ALWAYS;
+        }
+        else if ( "none".equalsIgnoreCase( forkMode ) )
+        {
+            return FORK_NEVER;
+        }
+        else if ( forkMode.equals( FORK_NEVER ) || forkMode.equals( FORK_ONCE )
+                || forkMode.equals( FORK_ALWAYS ) || forkMode.equals( FORK_PERTHREAD ) )
+        {
+            return forkMode;
+        }
+        else
+        {
+            throw new IllegalArgumentException( "Fork mode " + forkMode + " is not a legal value" );
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b671a583/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java
index c962424..fbc4a00 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java
@@ -19,215 +19,66 @@ package org.apache.maven.plugin.surefire.booterclient;
  * under the License.
  */
 
-import org.apache.maven.plugin.surefire.AbstractSurefireMojo;
 import org.apache.maven.plugin.surefire.JdkAttributes;
 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.OutputStreamFlushableCommandline;
-import org.apache.maven.plugin.surefire.util.Relocator;
 import org.apache.maven.surefire.booter.Classpath;
-import org.apache.maven.surefire.booter.ForkedBooter;
 import org.apache.maven.surefire.booter.StartupConfiguration;
 import org.apache.maven.surefire.booter.SurefireBooterForkException;
 import org.apache.maven.surefire.util.internal.ImmutableMap;
 
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
 import java.util.Collections;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
-import java.util.jar.JarEntry;
-import java.util.jar.JarOutputStream;
-import java.util.jar.Manifest;
 
-import static org.apache.maven.plugin.surefire.SurefireHelper.escapeToPlatformPath;
-import static org.apache.maven.shared.utils.StringUtils.join;
+import static org.apache.maven.plugin.surefire.AbstractSurefireMojo.FORK_NUMBER_PLACEHOLDER;
+import static org.apache.maven.plugin.surefire.AbstractSurefireMojo.THREAD_NUMBER_PLACEHOLDER;
 
 /**
  * Configuration for forking tests.
- *
- * @author <a href="mailto:brett@apache.org">Brett Porter</a>
- * @author <a href="mailto:kenney@apache.org">Kenney Westerhof</a>
- * @author <a href="mailto:krosenvold@apache.org">Kristian Rosenvold</a>
  */
-public class ForkConfiguration
+public abstract class ForkConfiguration
 {
-    public static final String FORK_ONCE = "once";
-
-    public static final String FORK_ALWAYS = "always";
-
-    public static final String FORK_NEVER = "never";
-
-    public static final String FORK_PERTHREAD = "perthread";
-
-    private final int forkCount;
-
-    private final boolean reuseForks;
-
-    private final Classpath bootClasspathConfiguration;
-
-    private final JdkAttributes jdk;
-
-    private final Properties modelProperties;
-
-    private final String argLine;
-
-    private final Map<String, String> environmentVariables;
-
-    private final File workingDirectory;
-
-    private final File tempDirectory;
-
-    private final boolean debug;
-
-    private final String debugLine;
-
-    private final Platform pluginPlatform;
-
-    @SuppressWarnings( "checkstyle:parameternumber" )
-    public ForkConfiguration( Classpath bootClasspathConfiguration, File tmpDir, String debugLine,
-                              JdkAttributes jdk, File workingDirectory, Properties modelProperties, String argLine,
-                              Map<String, String> environmentVariables, boolean debugEnabled, int forkCount,
-                              boolean reuseForks, Platform pluginPlatform )
-    {
-        this.bootClasspathConfiguration = bootClasspathConfiguration;
-        this.tempDirectory = tmpDir;
-        this.debugLine = debugLine;
-        this.jdk = jdk;
-        this.workingDirectory = workingDirectory;
-        this.modelProperties = modelProperties;
-        this.argLine = argLine;
-        this.environmentVariables = toImmutable( environmentVariables );
-        this.debug = debugEnabled;
-        this.forkCount = forkCount;
-        this.reuseForks = reuseForks;
-        this.pluginPlatform = pluginPlatform;
-    }
-
-    public Classpath getBootClasspath()
-    {
-        return bootClasspathConfiguration;
-    }
-
-    public static String getEffectiveForkMode( String forkMode )
-    {
-        if ( "pertest".equalsIgnoreCase( forkMode ) )
-        {
-            return FORK_ALWAYS;
-        }
-        else if ( "none".equalsIgnoreCase( forkMode ) )
-        {
-            return FORK_NEVER;
-        }
-        else if ( forkMode.equals( FORK_NEVER ) || forkMode.equals( FORK_ONCE )
-               || forkMode.equals( FORK_ALWAYS ) || forkMode.equals( FORK_PERTHREAD ) )
-        {
-            return forkMode;
-        }
-        else
-        {
-            throw new IllegalArgumentException( "Fork mode " + forkMode + " is not a legal value" );
-        }
-    }
+    @Nonnull public abstract File getTempDirectory();
+    @Nullable protected abstract String getDebugLine();
+    @Nonnull protected abstract File getWorkingDirectory();
+    @Nonnull protected abstract Properties getModelProperties();
+    @Nullable protected abstract String getArgLine();
+    @Nonnull protected abstract Map<String, String> getEnvironmentVariables();
+    protected abstract boolean isDebug();
+    protected abstract int getForkCount();
+    protected abstract boolean isReuseForks();
+    @Nonnull protected abstract Platform getPluginPlatform();
+    @Nonnull protected abstract JdkAttributes getJdkForTests();
+    @Nonnull protected abstract Classpath getBootClasspath();
 
     /**
-     * @param classPath            cli the classpath arguments
+     * @param classPath            the classpath arguments
      * @param config               The startup configuration
-     * @param threadNumber         the thread number, to be the replacement in the argLine   @return A commandline
+     * @param threadNumber         the thread number, to be the replacement in the argLine
      * @return CommandLine able to flush entire command going to be sent to forked JVM
      * @throws org.apache.maven.surefire.booter.SurefireBooterForkException
      *          when unable to perform the fork
      */
-    public OutputStreamFlushableCommandline createCommandLine( List<String> classPath, StartupConfiguration config,
-                                                               int threadNumber )
-            throws SurefireBooterForkException
-    {
-        boolean useJar = config.getClassLoaderConfiguration().isManifestOnlyJarRequestedAndUsable();
-
-        boolean shadefire = config.isShadefire();
+    @Nonnull
+    public abstract OutputStreamFlushableCommandline createCommandLine( List<String> classPath,
+                                                                        StartupConfiguration config, int threadNumber )
+            throws SurefireBooterForkException;
 
-        String providerThatHasMainMethod =
-                config.isProviderMainClass() ? config.getActualClassName() : ForkedBooter.class.getName();
-
-        return createCommandLine( classPath, useJar, shadefire, providerThatHasMainMethod, threadNumber );
-    }
-
-    OutputStreamFlushableCommandline createCommandLine( List<String> classPath, boolean useJar, boolean shadefire,
-                                                        String providerThatHasMainMethod, int threadNumber )
+    @Nonnull
+    protected File getWorkingDirectory( int threadNumber )
         throws SurefireBooterForkException
     {
-        OutputStreamFlushableCommandline cli = new OutputStreamFlushableCommandline();
-
-        cli.setExecutable( jdk.getJvmExecutable() );
-
-        String jvmArgLine =
-                replaceThreadNumberPlaceholder( stripNewLines( replacePropertyExpressions() ), threadNumber );
-
-        if ( jdk.isJava9AtLeast() && !jvmArgLine.contains( "--add-modules" ) )
-        {
-            if ( jvmArgLine.isEmpty() )
-            {
-                jvmArgLine = "--add-modules java.se.ee";
-            }
-            else
-            {
-                jvmArgLine = "--add-modules java.se.ee " + jvmArgLine;
-            }
-        }
-
-        if ( !jvmArgLine.isEmpty() )
-        {
-            cli.createArg().setLine( jvmArgLine );
-        }
-
-        for ( Map.Entry<String, String> entry : environmentVariables.entrySet() )
-        {
-            String value = entry.getValue();
-            cli.addEnvironment( entry.getKey(), value == null ? "" : value );
-        }
-
-        if ( getDebugLine() != null && !getDebugLine().isEmpty() )
-        {
-            cli.createArg().setLine( getDebugLine() );
-        }
+        File cwd = new File( replaceThreadNumberPlaceholder( getWorkingDirectory().getAbsolutePath(), threadNumber ) );
 
-        if ( useJar )
-        {
-            try
-            {
-                File jarFile = createJar( classPath, providerThatHasMainMethod );
-                cli.createArg().setValue( "-jar" );
-                cli.createArg().setValue( escapeToPlatformPath( jarFile.getAbsolutePath() ) );
-            }
-            catch ( IOException e )
-            {
-                throw new SurefireBooterForkException( "Error creating archive file", e );
-            }
-        }
-        else
-        {
-            cli.addEnvironment( "CLASSPATH", join( classPath.iterator(), File.pathSeparator ) );
-
-            final String forkedBooter =
-                providerThatHasMainMethod != null ? providerThatHasMainMethod : ForkedBooter.class.getName();
-
-            cli.createArg().setValue( shadefire ? new Relocator().relocate( forkedBooter ) : forkedBooter );
-        }
-
-        cli.setWorkingDirectory( getWorkingDirectory( threadNumber ).getAbsolutePath() );
-
-        return cli;
-    }
-
-    private File getWorkingDirectory( int threadNumber )
-        throws SurefireBooterForkException
-    {
-        File cwd = new File( replaceThreadNumberPlaceholder( workingDirectory.getAbsolutePath(), threadNumber ) );
         if ( !cwd.exists() && !cwd.mkdirs() )
         {
             throw new SurefireBooterForkException( "Cannot create workingDirectory " + cwd.getAbsolutePath() );
         }
+
         if ( !cwd.isDirectory() )
         {
             throw new SurefireBooterForkException(
@@ -236,11 +87,11 @@ public class ForkConfiguration
         return cwd;
     }
 
-    private String replaceThreadNumberPlaceholder( String argLine, int threadNumber )
+    protected static String replaceThreadNumberPlaceholder( String argLine, int threadNumber )
     {
-        return argLine.replace( AbstractSurefireMojo.THREAD_NUMBER_PLACEHOLDER,
-                                String.valueOf( threadNumber ) ).replace( AbstractSurefireMojo.FORK_NUMBER_PLACEHOLDER,
-                                                                          String.valueOf( threadNumber ) );
+        String threadNumberAsString = String.valueOf( threadNumber );
+        return argLine.replace( THREAD_NUMBER_PLACEHOLDER, threadNumberAsString )
+                .replace( FORK_NUMBER_PLACEHOLDER, threadNumberAsString );
     }
 
     /**
@@ -250,126 +101,33 @@ public class ForkConfiguration
      *
      * This allows other plugins to modify or set properties with the changes getting picked up by surefire.
      */
-    private String replacePropertyExpressions()
+    protected String replacePropertyExpressions()
     {
-        if ( argLine == null )
+        if ( getArgLine() == null )
         {
             return "";
         }
 
-        String resolvedArgLine = argLine.trim();
+        String resolvedArgLine = getArgLine().trim();
 
         if ( resolvedArgLine.isEmpty() )
         {
             return "";
         }
 
-        for ( final String key : modelProperties.stringPropertyNames() )
+        for ( final String key : getModelProperties().stringPropertyNames() )
         {
             String field = "@{" + key + "}";
-            if ( argLine.contains( field ) )
+            if ( getArgLine().contains( field ) )
             {
-                resolvedArgLine = resolvedArgLine.replace( field, modelProperties.getProperty( key, "" ) );
+                resolvedArgLine = resolvedArgLine.replace( field, getModelProperties().getProperty( key, "" ) );
             }
         }
 
         return resolvedArgLine;
     }
 
-    /**
-     * Create a jar with just a manifest containing a Main-Class entry for BooterConfiguration and a Class-Path entry
-     * for all classpath elements.
-     *
-     * @param classPath      List&lt;String&gt; of all classpath elements.
-     * @param startClassName  The classname to start (main-class)
-     * @return The file pointint to the jar
-     * @throws java.io.IOException When a file operation fails.
-     */
-    private File createJar( List<String> classPath, String startClassName )
-        throws IOException
-    {
-        File file = File.createTempFile( "surefirebooter", ".jar", tempDirectory );
-        if ( !debug )
-        {
-            file.deleteOnExit();
-        }
-        FileOutputStream fos = new FileOutputStream( file );
-        JarOutputStream jos = new JarOutputStream( fos );
-        try
-        {
-            jos.setLevel( JarOutputStream.STORED );
-            JarEntry je = new JarEntry( "META-INF/MANIFEST.MF" );
-            jos.putNextEntry( je );
-
-            Manifest man = new Manifest();
-
-            // we can't use StringUtils.join here since we need to add a '/' to
-            // the end of directory entries - otherwise the jvm will ignore them.
-            StringBuilder cp = new StringBuilder();
-            for ( Iterator<String> it = classPath.iterator(); it.hasNext(); )
-            {
-                File file1 = new File( it.next() );
-                String uri = file1.toURI().toASCIIString();
-                cp.append( uri );
-                if ( file1.isDirectory() && !uri.endsWith( "/" ) )
-                {
-                    cp.append( '/' );
-                }
-
-                if ( it.hasNext() )
-                {
-                    cp.append( ' ' );
-                }
-            }
-
-            man.getMainAttributes().putValue( "Manifest-Version", "1.0" );
-            man.getMainAttributes().putValue( "Class-Path", cp.toString().trim() );
-            man.getMainAttributes().putValue( "Main-Class", startClassName );
-
-            man.write( jos );
-
-            jos.closeEntry();
-            jos.flush();
-
-            return file;
-        }
-        finally
-        {
-            jos.close();
-        }
-    }
-
-    public boolean isDebug()
-    {
-        return debug;
-    }
-
-    public String getDebugLine()
-    {
-        return debugLine;
-    }
-
-    public File getTempDirectory()
-    {
-        return tempDirectory;
-    }
-
-    public int getForkCount()
-    {
-        return forkCount;
-    }
-
-    public boolean isReuseForks()
-    {
-        return reuseForks;
-    }
-
-    public Platform getPluginPlatform()
-    {
-        return pluginPlatform;
-    }
-
-    private static String stripNewLines( String argLine )
+    protected static String stripNewLines( String argLine )
     {
         return argLine.replace( "\n", " " ).replace( "\r", " " );
     }
@@ -382,7 +140,7 @@ public class ForkConfiguration
      * @param <V>    value type
      * @return never returns null
      */
-    private static <K, V> Map<K, V> toImmutable( Map<K, V> map )
+    protected static <K, V> Map<K, V> toImmutable( Map<K, V> map )
     {
         return map == null ? Collections.<K, V>emptyMap() : new ImmutableMap<K, V>( map );
     }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b671a583/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
index 3efbd57..6387d3d 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
@@ -553,13 +553,11 @@ public class ForkStarter
         {
             tempDir = forkConfiguration.getTempDirectory().getCanonicalPath();
             BooterSerializer booterSerializer = new BooterSerializer( forkConfiguration );
-
+            long pluginPid = forkConfiguration.getPluginPlatform().getPluginPid();
             surefireProperties = booterSerializer.serialize( providerProperties, providerConfiguration,
-                                                                   startupConfiguration, testSet,
-                                                                   readTestsFromInStream,
-                                                                   forkConfiguration.getPluginPlatform().getPid() );
+                    startupConfiguration, testSet, readTestsFromInStream, pluginPid );
 
-            log.debug( "Determined Maven Process ID " + forkConfiguration.getPluginPlatform().getPid() );
+            log.debug( "Determined Maven Process ID " + pluginPid );
 
             if ( effectiveSystemProperties != null )
             {

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b671a583/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarClasspathForkConfiguration.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarClasspathForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarClasspathForkConfiguration.java
new file mode 100644
index 0000000..059958a
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarClasspathForkConfiguration.java
@@ -0,0 +1,59 @@
+package org.apache.maven.plugin.surefire.booterclient;
+
+/*
+ * 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 org.apache.maven.plugin.surefire.booterclient.lazytestprovider.OutputStreamFlushableCommandline;
+import org.apache.maven.surefire.booter.Classpath;
+import org.apache.maven.surefire.booter.SurefireBooterForkException;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import static org.apache.maven.plugin.surefire.util.Relocator.relocate;
+import static org.apache.maven.shared.utils.StringUtils.join;
+
+/**
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.21.0.Jigsaw
+ */
+public final class JarClasspathForkConfiguration
+        extends JarForkConfiguration
+{
+    @SuppressWarnings( "checkstyle:parameternumber" )
+    public JarClasspathForkConfiguration( Classpath bootClasspathConfiguration, File tempDirectory, String debugLine,
+                                          File workingDirectory, Properties modelProperties, String argLine,
+                                          Map<String, String> environmentVariables, boolean debug, int forkCount,
+                                          boolean reuseForks, Platform pluginPlatform )
+    {
+        super( bootClasspathConfiguration, tempDirectory, debugLine, workingDirectory, modelProperties, argLine,
+                environmentVariables, debug, forkCount, reuseForks, pluginPlatform );
+    }
+
+    @Override
+    protected void resolveClasspath( OutputStreamFlushableCommandline cli, List<String> classPath,
+                                     String booterThatHasMainMethod, boolean shadefire )
+            throws SurefireBooterForkException
+    {
+        cli.addEnvironment( "CLASSPATH", join( classPath.iterator(), File.pathSeparator ) );
+        cli.createArg().setValue( shadefire ? relocate( booterThatHasMainMethod ) : booterThatHasMainMethod );
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b671a583/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarForkConfiguration.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarForkConfiguration.java
new file mode 100644
index 0000000..d3f8e65
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarForkConfiguration.java
@@ -0,0 +1,292 @@
+package org.apache.maven.plugin.surefire.booterclient;
+
+/*
+ * 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 org.apache.maven.plugin.surefire.JdkAttributes;
+import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.OutputStreamFlushableCommandline;
+import org.apache.maven.surefire.booter.Classpath;
+import org.apache.maven.surefire.booter.ForkedBooter;
+import org.apache.maven.surefire.booter.StartupConfiguration;
+import org.apache.maven.surefire.booter.SurefireBooterForkException;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+/**
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.21.0.Jigsaw
+ */
+abstract class JarForkConfiguration
+        extends ForkConfiguration
+{
+    private static final String ALL_JAVA_API = "--add-modules java.se.ee";
+    private static final String ADD_MODULES = "--add-modules";
+
+    @Nonnull private final Classpath bootClasspathConfiguration;
+    @Nonnull private final File tempDirectory;
+    @Nullable private final String debugLine;
+    @Nonnull private final File workingDirectory;
+    @Nonnull private final Properties modelProperties;
+    @Nullable private final String argLine;
+    @Nonnull private final Map<String, String> environmentVariables;
+    private final boolean debug;
+    private final int forkCount;
+    private final boolean reuseForks;
+    @Nonnull private final Platform pluginPlatform;
+
+    @SuppressWarnings( "checkstyle:parameternumber" )
+    public JarForkConfiguration( @Nonnull Classpath bootClasspathConfiguration,
+                                 @Nonnull File tempDirectory,
+                                 @Nullable String debugLine,
+                                 @Nonnull File workingDirectory,
+                                 @Nonnull Properties modelProperties,
+                                 @Nullable String argLine,
+                                 @Nonnull Map<String, String> environmentVariables,
+                                 boolean debug,
+                                 int forkCount,
+                                 boolean reuseForks,
+                                 @Nonnull Platform pluginPlatform )
+    {
+        this.bootClasspathConfiguration = bootClasspathConfiguration;
+        this.tempDirectory = tempDirectory;
+        this.debugLine = debugLine;
+        this.workingDirectory = workingDirectory;
+        this.modelProperties = modelProperties;
+        this.argLine = argLine;
+        this.environmentVariables = toImmutable( environmentVariables );
+        this.debug = debug;
+        this.forkCount = forkCount;
+        this.reuseForks = reuseForks;
+        this.pluginPlatform = pluginPlatform;
+    }
+
+
+    protected abstract void resolveClasspath( OutputStreamFlushableCommandline cli, List<String> classPath,
+                                              String booterThatHasMainMethod, boolean shadefire )
+            throws SurefireBooterForkException;
+
+
+    /**
+     * @param classPath    the classpath arguments
+     * @param config       The startup configuration
+     * @param threadNumber the thread number, to be the replacement in the argLine
+     * @return CommandLine able to flush entire command going to be sent to forked JVM
+     * @throws org.apache.maven.surefire.booter.SurefireBooterForkException when unable to perform the fork
+     */
+    @Override
+    @Nonnull
+    public OutputStreamFlushableCommandline createCommandLine( List<String> classPath, StartupConfiguration config,
+                                                               int threadNumber ) throws SurefireBooterForkException
+    {
+        OutputStreamFlushableCommandline cli = new OutputStreamFlushableCommandline();
+
+        cli.setExecutable( getJdkForTests().getJvmExecutable() );
+
+        String jvmArgLine =
+                replaceThreadNumberPlaceholder( stripNewLines( replacePropertyExpressions() ), threadNumber );
+
+        if ( getJdkForTests().isJava9AtLeast() && !jvmArgLine.contains( ADD_MODULES ) )
+        {
+            jvmArgLine = jvmArgLine.isEmpty() ? ALL_JAVA_API : ALL_JAVA_API + " " + jvmArgLine;
+        }
+
+        if ( !jvmArgLine.isEmpty() )
+        {
+            cli.createArg().setLine( jvmArgLine );
+        }
+
+        for ( Map.Entry<String, String> entry : getEnvironmentVariables().entrySet() )
+        {
+            String value = entry.getValue();
+            cli.addEnvironment( entry.getKey(), value == null ? "" : value );
+        }
+
+        if ( getDebugLine() != null && !getDebugLine().isEmpty() )
+        {
+            cli.createArg().setLine( getDebugLine() );
+        }
+
+        boolean shadefire = config.isShadefire();
+
+        String providerThatHasMainMethod =
+                config.isProviderMainClass() ? config.getActualClassName() : ForkedBooter.class.getName();
+
+        resolveClasspath( cli, classPath, providerThatHasMainMethod, shadefire );
+
+        cli.setWorkingDirectory( getWorkingDirectory( threadNumber ).getAbsolutePath() );
+
+        return cli;
+    }
+
+    /**
+     * Create a jar with just a manifest containing a Main-Class entry for BooterConfiguration and a Class-Path entry
+     * for all classpath elements.
+     *
+     * @param classPath      List&lt;String&gt; of all classpath elements.
+     * @param startClassName The classname to start (main-class)
+     * @return file of the jar
+     * @throws java.io.IOException When a file operation fails.
+     */
+    @Nonnull
+    protected File createJar( List<String> classPath, String startClassName )
+            throws IOException
+    {
+        File file = File.createTempFile( "surefirebooter", ".jar", getTempDirectory() );
+        if ( !isDebug() )
+        {
+            file.deleteOnExit();
+        }
+        FileOutputStream fos = new FileOutputStream( file );
+        JarOutputStream jos = new JarOutputStream( fos );
+        try
+        {
+            jos.setLevel( JarOutputStream.STORED );
+            JarEntry je = new JarEntry( "META-INF/MANIFEST.MF" );
+            jos.putNextEntry( je );
+
+            Manifest man = new Manifest();
+
+            // we can't use StringUtils.join here since we need to add a '/' to
+            // the end of directory entries - otherwise the jvm will ignore them.
+            StringBuilder cp = new StringBuilder();
+            for ( Iterator<String> it = classPath.iterator(); it.hasNext(); )
+            {
+                File file1 = new File( it.next() );
+                String uri = file1.toURI().toASCIIString();
+                cp.append( uri );
+                if ( file1.isDirectory() && !uri.endsWith( "/" ) )
+                {
+                    cp.append( '/' );
+                }
+
+                if ( it.hasNext() )
+                {
+                    cp.append( ' ' );
+                }
+            }
+
+            man.getMainAttributes().putValue( "Manifest-Version", "1.0" );
+            man.getMainAttributes().putValue( "Class-Path", cp.toString().trim() );
+            man.getMainAttributes().putValue( "Main-Class", startClassName );
+
+            man.write( jos );
+
+            jos.closeEntry();
+            jos.flush();
+
+            return file;
+        }
+        finally
+        {
+            jos.close();
+        }
+    }
+
+    @Override
+    @Nonnull
+    public File getTempDirectory()
+    {
+        return tempDirectory;
+    }
+
+    @Override
+    @Nullable
+    protected String getDebugLine()
+    {
+        return debugLine;
+    }
+
+    @Override
+    @Nonnull
+    protected File getWorkingDirectory()
+    {
+        return workingDirectory;
+    }
+
+    @Override
+    @Nonnull
+    protected Properties getModelProperties()
+    {
+        return modelProperties;
+    }
+
+    @Override
+    @Nullable
+    protected String getArgLine()
+    {
+        return argLine;
+    }
+
+    @Override
+    @Nonnull
+    protected Map<String, String> getEnvironmentVariables()
+    {
+        return environmentVariables;
+    }
+
+    @Override
+    protected boolean isDebug()
+    {
+        return debug;
+    }
+
+    @Override
+    protected int getForkCount()
+    {
+        return forkCount;
+    }
+
+    @Override
+    protected boolean isReuseForks()
+    {
+        return reuseForks;
+    }
+
+    @Override
+    @Nonnull
+    protected Platform getPluginPlatform()
+    {
+        return pluginPlatform;
+    }
+
+    @Override
+    @Nonnull
+    protected JdkAttributes getJdkForTests()
+    {
+        return getPluginPlatform().getJdkExecAttributesForTests();
+    }
+
+    @Override
+    @Nonnull
+    protected Classpath getBootClasspath()
+    {
+        return bootClasspathConfiguration;
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b671a583/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfiguration.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfiguration.java
new file mode 100644
index 0000000..d1edc3e
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfiguration.java
@@ -0,0 +1,69 @@
+package org.apache.maven.plugin.surefire.booterclient;
+
+/*
+ * 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 org.apache.maven.plugin.surefire.booterclient.lazytestprovider.OutputStreamFlushableCommandline;
+import org.apache.maven.surefire.booter.Classpath;
+import org.apache.maven.surefire.booter.SurefireBooterForkException;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import static org.apache.maven.plugin.surefire.SurefireHelper.escapeToPlatformPath;
+import static org.apache.maven.plugin.surefire.util.Relocator.relocate;
+
+/**
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.21.0.Jigsaw
+ */
+public final class JarManifestForkConfiguration
+    extends JarForkConfiguration
+{
+    @SuppressWarnings( "checkstyle:parameternumber" )
+    public JarManifestForkConfiguration( Classpath bootClasspathConfiguration, File tempDirectory, String debugLine,
+                                          File workingDirectory, Properties modelProperties, String argLine,
+                                          Map<String, String> environmentVariables, boolean debug, int forkCount,
+                                          boolean reuseForks, Platform pluginPlatform )
+    {
+        super( bootClasspathConfiguration, tempDirectory, debugLine, workingDirectory, modelProperties, argLine,
+                environmentVariables, debug, forkCount, reuseForks, pluginPlatform );
+    }
+
+    @Override
+    protected void resolveClasspath( OutputStreamFlushableCommandline cli, List<String> classPath,
+                                     String booterThatHasMainMethod, boolean shadefire )
+            throws SurefireBooterForkException
+    {
+        try
+        {
+            File jarFile =
+                    createJar( classPath, shadefire ? relocate( booterThatHasMainMethod ) : booterThatHasMainMethod );
+            cli.createArg().setValue( "-jar" );
+            cli.createArg().setValue( escapeToPlatformPath( jarFile.getAbsolutePath() ) );
+        }
+        catch ( IOException e )
+        {
+            throw new SurefireBooterForkException( "Error creating archive file", e );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b671a583/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JigsawForkConfiguration.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JigsawForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JigsawForkConfiguration.java
new file mode 100644
index 0000000..eaf4c6a
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JigsawForkConfiguration.java
@@ -0,0 +1,29 @@
+package org.apache.maven.plugin.surefire.booterclient;
+
+/*
+ * 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.
+ */
+
+/**
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.21.0.Jigsaw
+ */
+public class JigsawForkConfiguration
+{
+        /*Classpath testModulePath = config.getClasspathConfiguration().getTestModulepath();*/
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b671a583/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/Platform.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/Platform.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/Platform.java
index 10b16dc..4762453 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/Platform.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/Platform.java
@@ -19,6 +19,7 @@ package org.apache.maven.plugin.surefire.booterclient;
  * under the License.
  */
 
+import org.apache.maven.plugin.surefire.JdkAttributes;
 import org.apache.maven.surefire.booter.SystemUtils;
 
 import java.util.concurrent.Callable;
@@ -35,20 +36,28 @@ import static org.apache.maven.surefire.util.internal.DaemonThreadFactory.newDae
  */
 public final class Platform
 {
-    private final RunnableFuture<Long> pidJob;
+    private final RunnableFuture<Long> pluginPidJob;
+
+    private volatile JdkAttributes jdk;
 
     public Platform()
     {
         // the job may take 50 or 80 ms
-        pidJob = new FutureTask<Long>( pidJob() );
-        newDaemonThread( pidJob ).start();
+        this( new FutureTask<Long>( pidJob() ), null );
+        newDaemonThread( pluginPidJob ).start();
     }
 
-    public Long getPid()
+    private Platform( RunnableFuture<Long> pluginPidJob, JdkAttributes jdk )
+    {
+        this.pluginPidJob = pluginPidJob;
+        this.jdk = jdk;
+    }
+
+    public Long getPluginPid()
     {
         try
         {
-            return pidJob.get();
+            return pluginPidJob.get();
         }
         catch ( Exception e )
         {
@@ -56,6 +65,16 @@ public final class Platform
         }
     }
 
+    public JdkAttributes getJdkExecAttributesForTests()
+    {
+        return jdk;
+    }
+
+    public Platform withJdkExecAttributesForTests( JdkAttributes jdk )
+    {
+        return new Platform( pluginPidJob, jdk );
+    }
+
     private static Callable<Long> pidJob()
     {
         return new Callable<Long>()

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b671a583/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/Relocator.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/Relocator.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/Relocator.java
index 2237698..9d7706b 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/Relocator.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/Relocator.java
@@ -21,47 +21,38 @@ package org.apache.maven.plugin.surefire.util;
 
 
 import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 
 /**
  * Relocates class names when running with relocated provider
  *
  * @author Kristian Rosenvold
  */
-public class Relocator
+public final class Relocator
 {
     private static final String RELOCATION_BASE = "org.apache.maven.surefire.";
+    private static final String PACKAGE_DELIMITER = "shadefire";
 
-    @Nullable
-    private final String relocation;
-
-    public Relocator( @Nullable String relocation )
-    {
-        this.relocation = relocation;
-    }
-
-    public Relocator()
+    private Relocator()
     {
-        relocation = "shadefire";
+        throw new IllegalStateException( "no instantiable constructor" );
     }
 
-    @Nullable private String getRelocation()
+    @Nonnull
+    public static String relocate( @Nonnull String className )
     {
-        return relocation;
-    }
-
-    @Nonnull public String relocate( @Nonnull String className )
-    {
-        if ( relocation == null )
+        if ( className.contains( PACKAGE_DELIMITER ) )
         {
             return className;
         }
-        if ( className.contains( relocation ) )
+        else
         {
-            return className;
+            if ( !className.startsWith( RELOCATION_BASE ) )
+            {
+                throw new IllegalArgumentException( "'" + className + "' should start with '" + RELOCATION_BASE + "'" );
+            }
+            String rest = className.substring( RELOCATION_BASE.length() );
+            final String s = RELOCATION_BASE + PACKAGE_DELIMITER + ".";
+            return s + rest;
         }
-        String rest = className.substring( RELOCATION_BASE.length() );
-        final String s = RELOCATION_BASE + getRelocation() + ".";
-        return s + rest;
     }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b671a583/maven-surefire-common/src/main/java/org/apache/maven/surefire/providerapi/ServiceLoader.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/surefire/providerapi/ServiceLoader.java b/maven-surefire-common/src/main/java/org/apache/maven/surefire/providerapi/ServiceLoader.java
index 8753ff1..00b8b74 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/surefire/providerapi/ServiceLoader.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/surefire/providerapi/ServiceLoader.java
@@ -43,7 +43,7 @@ import static org.apache.maven.surefire.util.ReflectionUtils.getConstructor;
  *
  * @since 2.20
  */
-public class ServiceLoader
+public final class ServiceLoader
 {
 
     @Nonnull

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b671a583/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java
index b49e164..54f67cf 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java
@@ -25,7 +25,7 @@ import org.apache.commons.lang3.SystemUtils;
 import org.apache.maven.plugin.surefire.JdkAttributes;
 import org.apache.maven.shared.utils.StringUtils;
 import org.apache.maven.shared.utils.cli.Commandline;
-import org.apache.maven.surefire.booter.Classpath;
+import org.apache.maven.surefire.booter.StartupConfiguration;
 import org.apache.maven.surefire.booter.SurefireBooterForkException;
 import org.junit.Test;
 
@@ -34,21 +34,29 @@ import java.io.IOException;
 import java.util.Collections;
 import java.util.Properties;
 
+import static java.util.Collections.singletonList;
+import static org.apache.maven.surefire.booter.Classpath.emptyClasspath;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 public class ForkConfigurationTest
 {
+    private static final StartupConfiguration STARTUP_CONFIG = new StartupConfiguration( "", null, null, false, false );
+
+    private boolean useManifest;
+
     @Test
     public void testCreateCommandLine_UseSystemClassLoaderForkOnce_ShouldConstructManifestOnlyJar()
         throws IOException, SurefireBooterForkException
     {
+        useManifest = true;
+
         ForkConfiguration config = getForkConfiguration( (String) null );
         File cpElement = getTempClasspathFile();
 
         Commandline cli =
-            config.createCommandLine( Collections.singletonList( cpElement.getAbsolutePath() ), true, false, null, 1 );
+            config.createCommandLine( singletonList( cpElement.getAbsolutePath() ), STARTUP_CONFIG, 1 );
 
         String line = StringUtils.join( cli.getCommandline(), " " );
         assertTrue( line.contains( "-jar" ) );
@@ -63,8 +71,7 @@ public class ForkConfigurationTest
         ForkConfiguration forkConfiguration = getForkConfiguration( "abc\ndef" );
 
         final Commandline commandLine =
-            forkConfiguration.createCommandLine( Collections.singletonList( cpElement.getAbsolutePath() ), false, false,
-                                                 null, 1 );
+            forkConfiguration.createCommandLine( singletonList( cpElement.getAbsolutePath() ), STARTUP_CONFIG, 1 );
         assertTrue( commandLine.toString().contains( "abc def" ) );
     }
 
@@ -72,6 +79,8 @@ public class ForkConfigurationTest
     public void testCurrentWorkingDirectoryPropagationIncludingForkNumberExpansion()
         throws IOException, SurefireBooterForkException
     {
+        useManifest = true;
+
         // SUREFIRE-1136
         File baseDir =
             new File( FileUtils.getTempDirectory(), "SUREFIRE-1136-" + RandomStringUtils.randomAlphabetic( 3 ) );
@@ -81,7 +90,7 @@ public class ForkConfigurationTest
         File cwd = new File( baseDir, "fork_${surefire.forkNumber}" );
 
         ForkConfiguration config = getForkConfiguration( null, cwd.getCanonicalFile() );
-        Commandline commandLine = config.createCommandLine( Collections.<String>emptyList(), true, false, null, 1 );
+        Commandline commandLine = config.createCommandLine( Collections.<String>emptyList(), STARTUP_CONFIG, 1 );
 
         File forkDirectory = new File( baseDir, "fork_1" );
         forkDirectory.deleteOnExit();
@@ -93,6 +102,8 @@ public class ForkConfigurationTest
     public void testExceptionWhenCurrentDirectoryIsNotRealDirectory()
         throws IOException, SurefireBooterForkException
     {
+        useManifest = true;
+
         // SUREFIRE-1136
         File baseDir =
             new File( FileUtils.getTempDirectory(), "SUREFIRE-1136-" + RandomStringUtils.randomAlphabetic( 3 ) );
@@ -107,7 +118,7 @@ public class ForkConfigurationTest
 
         try
         {
-            config.createCommandLine( Collections.<String>emptyList(), true, false, null, 1 );
+            config.createCommandLine( Collections.<String>emptyList(), STARTUP_CONFIG, 1 );
         }
         catch ( SurefireBooterForkException sbfe )
         {
@@ -124,6 +135,8 @@ public class ForkConfigurationTest
     public void testExceptionWhenCurrentDirectoryCannotBeCreated()
         throws IOException, SurefireBooterForkException
     {
+        useManifest = true;
+
         // SUREFIRE-1136
         File baseDir =
             new File( FileUtils.getTempDirectory(), "SUREFIRE-1136-" + RandomStringUtils.randomAlphabetic( 3 ) );
@@ -137,7 +150,7 @@ public class ForkConfigurationTest
 
         try
         {
-            config.createCommandLine( Collections.<String>emptyList(), true, false, null, 1 );
+            config.createCommandLine( Collections.<String>emptyList(), STARTUP_CONFIG, 1 );
         }
         catch ( SurefireBooterForkException sbfe )
         {
@@ -182,8 +195,17 @@ public class ForkConfigurationTest
     private static ForkConfiguration getForkConfiguration( String argLine, String jvm, File cwd )
         throws IOException
     {
-        return new ForkConfiguration( Classpath.emptyClasspath(), null, null, new JdkAttributes( jvm, false ),
-                                            cwd, new Properties(), argLine, null, false, 1, false, new Platform() );
+        Platform platform = new Platform().withJdkExecAttributesForTests( new JdkAttributes( jvm, false ) );
+        if ( true )
+        {
+            return new JarManifestForkConfiguration( emptyClasspath(), null, null,
+                                                cwd, new Properties(), argLine, null, false, 1, false, platform );
+        }
+        else
+        {
+            return new JarClasspathForkConfiguration( emptyClasspath(), null, null,
+                    cwd, new Properties(), argLine, null, false, 1, false, platform );
+        }
     }
 
     // based on http://stackoverflow.com/questions/2591083/getting-version-of-java-in-runtime

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b671a583/maven-surefire-common/src/test/java/org/apache/maven/surefire/util/RelocatorTest.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/surefire/util/RelocatorTest.java b/maven-surefire-common/src/test/java/org/apache/maven/surefire/util/RelocatorTest.java
index 3cec846..4d5951a 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/surefire/util/RelocatorTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/surefire/util/RelocatorTest.java
@@ -32,15 +32,13 @@ public class RelocatorTest
 
     public void testFoo()
     {
-        Relocator relocator = new Relocator( "shadefire" );
         String cn = "org.apache.maven.surefire.report.ForkingConsoleReporter";
-        assertEquals( "org.apache.maven.surefire.shadefire.report.ForkingConsoleReporter", relocator.relocate( cn ) );
+        assertEquals( "org.apache.maven.surefire.shadefire.report.ForkingConsoleReporter", Relocator.relocate( cn ) );
     }
 
     public void testRelocation()
     {
-        Relocator relocator = new Relocator( "shadefire" );
         String org1 = "org.apache.maven.surefire.fooz.Baz";
-        assertEquals( "org.apache.maven.surefire.shadefire.fooz.Baz", relocator.relocate( org1 ) );
+        assertEquals( "org.apache.maven.surefire.shadefire.fooz.Baz", Relocator.relocate( org1 ) );
     }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b671a583/maven-surefire-plugin/src/test/java/org/apache/maven/plugin/surefire/SurefirePluginTest.java
----------------------------------------------------------------------
diff --git a/maven-surefire-plugin/src/test/java/org/apache/maven/plugin/surefire/SurefirePluginTest.java b/maven-surefire-plugin/src/test/java/org/apache/maven/plugin/surefire/SurefirePluginTest.java
index 5fa6df0..4cbbf00 100644
--- a/maven-surefire-plugin/src/test/java/org/apache/maven/plugin/surefire/SurefirePluginTest.java
+++ b/maven-surefire-plugin/src/test/java/org/apache/maven/plugin/surefire/SurefirePluginTest.java
@@ -20,7 +20,6 @@ package org.apache.maven.plugin.surefire;
 
 
 import java.lang.reflect.Field;
-import org.apache.maven.plugin.surefire.booterclient.ForkConfiguration;
 import org.apache.maven.toolchain.Toolchain;
 
 import junit.framework.TestCase;
@@ -35,7 +34,7 @@ public class SurefirePluginTest
         SurefirePlugin surefirePlugin = new SurefirePlugin();
         setFieldValue( surefirePlugin, "toolchain", new MyToolChain() );
         setFieldValue( surefirePlugin, "forkMode", "never" );
-        assertEquals( ForkConfiguration.FORK_ONCE, surefirePlugin.getEffectiveForkMode() );
+        assertEquals( "once", surefirePlugin.getEffectiveForkMode() );
     }
 
     public void testForkCountComputation()

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b671a583/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 4f0bf58..b108cde 100644
--- a/pom.xml
+++ b/pom.xml
@@ -233,9 +233,30 @@
       </dependency>
       <dependency>
         <groupId>org.apache.maven.shared</groupId>
+        <artifactId>maven-common-artifact-filters</artifactId>
+        <version>1.3</version>
+        <exclusions>
+          <exclusion>
+            <groupId>org.apache.maven.shared</groupId>
+            <artifactId>maven-plugin-testing-harness</artifactId>
+          </exclusion>
+        </exclusions>
+      </dependency>
+      <dependency>
+        <groupId>org.fusesource.jansi</groupId>
+        <artifactId>jansi</artifactId>
+        <version>1.13</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.maven.shared</groupId>
         <artifactId>maven-verifier</artifactId>
         <version>1.6</version>
       </dependency>
+      <!--<dependency>
+        <groupId>org.codehaus.plexus</groupId>
+        <artifactId>plexus-java</artifactId>
+        <version>0.9.3</version>
+      </dependency>-->
       <dependency>
         <groupId>org.mockito</groupId>
         <artifactId>mockito-core</artifactId>
@@ -387,6 +408,7 @@
               <artifactId>java16</artifactId>
               <version>1.1</version>
             </signature>
+            <excludeDependencies>org.codehaus.plexus:plexus-java,org.ow2.asm:asm</excludeDependencies>
           </configuration>
         </plugin>
         <plugin>
@@ -475,6 +497,24 @@
               </rules>
             </configuration>
           </execution>
+          <execution>
+            <id>enforce-bytecode-version</id>
+            <goals>
+              <goal>enforce</goal>
+            </goals>
+            <configuration>
+              <rules>
+                <enforceBytecodeVersion>
+                  <maxJdkVersion>${maven.compiler.target}</maxJdkVersion>
+                  <excludes>
+                    <exclude>org.codehaus.plexus:plexus-java</exclude>
+                    <exclude>org.ow2.asm:asm</exclude>
+                  </excludes>
+                </enforceBytecodeVersion>
+              </rules>
+              <fail>true</fail>
+            </configuration>
+          </execution>
         </executions>
       </plugin>
       <plugin>