[SUREFIRE-1570] Maven-fail-safe doesn't put testing JPMS module on module path
diff --git a/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java b/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
index 0e6e26d..71c652b 100644
--- a/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
+++ b/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
@@ -614,7 +614,7 @@
      * used instead. See the resolution of {@link #getClassLoaderConfiguration() ClassLoaderConfiguration}.
      */
     @Override
-    public File getClassesDirectory()
+    public File getMainBuildPath()
     {
         File artifact = getProject().getArtifact().getFile();
         boolean isDefaultClsDir = classesDirectory == null;
@@ -622,9 +622,9 @@
     }
 
     @Override
-    public void setClassesDirectory( File classesDirectory )
+    public void setMainBuildPath( File mainBuildPath )
     {
-        this.classesDirectory = toAbsoluteCanonical( classesDirectory );
+        classesDirectory = toAbsoluteCanonical( mainBuildPath );
     }
 
     public void setDefaultClassesDirectory( File defaultClassesDirectory )
diff --git a/maven-failsafe-plugin/src/test/java/org/apache/maven/plugin/failsafe/IntegrationTestMojoTest.java b/maven-failsafe-plugin/src/test/java/org/apache/maven/plugin/failsafe/IntegrationTestMojoTest.java
index c605938..f5edf0b 100644
--- a/maven-failsafe-plugin/src/test/java/org/apache/maven/plugin/failsafe/IntegrationTestMojoTest.java
+++ b/maven-failsafe-plugin/src/test/java/org/apache/maven/plugin/failsafe/IntegrationTestMojoTest.java
@@ -58,25 +58,25 @@
     public void shouldBeJar()
     {
         mojo.setDefaultClassesDirectory( new File( "./target/classes" ) );
-        File binaries = mojo.getClassesDirectory();
+        File binaries = mojo.getMainBuildPath();
         assertThat( binaries.getName() ).isEqualTo( "a-1.0.jar" );
     }
 
     @Test
     public void shouldBeAnotherJar()
     {
-        mojo.setClassesDirectory( new File( "./target/another-1.0.jar" ) );
+        mojo.setMainBuildPath( new File( "./target/another-1.0.jar" ) );
         mojo.setDefaultClassesDirectory( new File( "./target/classes" ) );
-        File binaries = mojo.getClassesDirectory();
+        File binaries = mojo.getMainBuildPath();
         assertThat( binaries.getName() ).isEqualTo( "another-1.0.jar" );
     }
 
     @Test
     public void shouldBeClasses()
     {
-        mojo.setClassesDirectory( new File( "./target/classes" ) );
+        mojo.setMainBuildPath( new File( "./target/classes" ) );
         mojo.setDefaultClassesDirectory( new File( "./target/classes" ) );
-        File binaries = mojo.getClassesDirectory();
+        File binaries = mojo.getMainBuildPath();
         assertThat( binaries.getName() ).isEqualTo( "classes" );
     }
 
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 9fa9d88..b1197e1 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
@@ -90,6 +90,8 @@
 import org.apache.maven.toolchain.Toolchain;
 import org.apache.maven.toolchain.ToolchainManager;
 import org.apache.maven.toolchain.java.DefaultJavaToolChain;
+import org.codehaus.plexus.languages.java.jpms.ResolvePathRequest;
+import org.codehaus.plexus.languages.java.jpms.ResolvePathResult;
 import org.codehaus.plexus.logging.Logger;
 import org.codehaus.plexus.languages.java.jpms.LocationManager;
 import org.codehaus.plexus.languages.java.jpms.ResolvePathsRequest;
@@ -101,7 +103,6 @@
 import java.lang.reflect.Method;
 import java.math.BigDecimal;
 import java.nio.file.Files;
-import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -909,6 +910,7 @@
         cli = commandLineOptions();
         // Stuff that should have been final
         setupStuff();
+        Platform platform = PLATFORM.withJdkExecAttributesForTests( getEffectiveJvm() );
 
         if ( verifyParameters() && !hasExecutedBefore() )
         {
@@ -924,7 +926,7 @@
                 return;
             }
             logReportsDirectory();
-            executeAfterPreconditionsChecked( scan );
+            executeAfterPreconditionsChecked( scan, platform );
         }
     }
 
@@ -1136,11 +1138,12 @@
         }
     }
 
-    private void executeAfterPreconditionsChecked( @Nonnull DefaultScanResult scanResult )
+    private void executeAfterPreconditionsChecked( @Nonnull DefaultScanResult scanResult, @Nonnull Platform platform )
         throws MojoExecutionException, MojoFailureException
     {
         TestClassPath testClasspath = generateTestClasspath();
         List<ProviderInfo> providers = createProviders( testClasspath );
+        ResolvePathResultWrapper wrapper = findModuleDescriptor( platform.getJdkExecAttributesForTests().getJdkHome() );
 
         RunResult current = noTestsRun();
 
@@ -1149,7 +1152,8 @@
         {
             try
             {
-                current = current.aggregate( executeProvider( provider, scanResult, testClasspath ) );
+                current =
+                    current.aggregate( executeProvider( provider, scanResult, testClasspath, platform, wrapper ) );
             }
             catch ( SurefireBooterForkException | SurefireExecutionException | TestSetFailedException e )
             {
@@ -1267,7 +1271,8 @@
 
     @Nonnull
     private RunResult executeProvider( @Nonnull ProviderInfo provider, @Nonnull DefaultScanResult scanResult,
-                                       @Nonnull TestClassPath testClasspathWrapper )
+                                       @Nonnull TestClassPath testClasspathWrapper, @Nonnull Platform platform,
+                                       @Nonnull ResolvePathResultWrapper resolvedJavaModularityResult )
         throws MojoExecutionException, MojoFailureException, SurefireExecutionException, SurefireBooterForkException,
         TestSetFailedException
     {
@@ -1278,7 +1283,6 @@
         RunOrderParameters runOrderParameters =
             new RunOrderParameters( getRunOrder(), getStatisticsFile( getConfigChecksum() ) );
 
-        Platform platform = PLATFORM.withJdkExecAttributesForTests( getEffectiveJvm() );
         if ( isNotForking() )
         {
             createCopyAndReplaceForkNumPlaceholder( effectiveProperties, 1 ).copyToSystemProperties();
@@ -1289,7 +1293,7 @@
         }
         else
         {
-            ForkConfiguration forkConfiguration = createForkConfiguration( platform );
+            ForkConfiguration forkConfiguration = createForkConfiguration( platform, resolvedJavaModularityResult );
             if ( getConsoleLogger().isDebugEnabled() )
             {
                 showMap( getEnvironmentVariables(), "environment variable" );
@@ -1301,8 +1305,8 @@
             try
             {
                 forkStarter = createForkStarter( provider, forkConfiguration, classLoaderConfiguration,
-                                                       runOrderParameters, getConsoleLogger(), scanResult, platform,
-                                                       testClasspathWrapper );
+                                                       runOrderParameters, getConsoleLogger(), scanResult,
+                                                       testClasspathWrapper, platform, resolvedJavaModularityResult );
 
                 return forkStarter.run( effectiveProperties, scanResult );
             }
@@ -1377,21 +1381,38 @@
         this.dependencyResolver = dependencyResolver;
     }
 
-    private boolean existsModuleDescriptor()
+    private boolean existsModuleDescriptor( ResolvePathResultWrapper resolvedJavaModularityResult )
     {
-        return getModuleDescriptor().isFile();
+        return resolvedJavaModularityResult.getResolvePathResult() != null;
     }
 
-    private File getModuleDescriptor()
+    private ResolvePathResultWrapper findModuleDescriptor( File jdkHome )
     {
-        return new File( getClassesDirectory(), "module-info.class" );
+        File mainBuildPath = getMainBuildPath();
+
+        if ( mainBuildPath.isDirectory() && !new File( mainBuildPath, "module-info.class" ).exists() )
+        {
+            return new ResolvePathResultWrapper( null, true );
+        }
+
+        try
+        {
+            ResolvePathRequest<?> request = ResolvePathRequest.ofFile( mainBuildPath ).setJdkHome( jdkHome );
+            ResolvePathResult result = getLocationManager().resolvePath( request );
+            return new ResolvePathResultWrapper( result.getModuleNameSource() == null ? null : result, true );
+        }
+        catch ( Exception e )
+        {
+            return new ResolvePathResultWrapper( null, true );
+        }
     }
 
-    private boolean canExecuteProviderWithModularPath( Platform platform )
+    private boolean canExecuteProviderWithModularPath( Platform platform,
+                                                       ResolvePathResultWrapper resolvedJavaModularityResult )
     {
         return useModulePath()
                 && platform.getJdkExecAttributesForTests().isJava9AtLeast()
-                && existsModuleDescriptor();
+                && existsModuleDescriptor( resolvedJavaModularityResult );
     }
 
     /**
@@ -1847,26 +1868,20 @@
     private StartupConfiguration createStartupConfiguration( @Nonnull ProviderInfo provider, boolean isForking,
                                                              @Nonnull ClassLoaderConfiguration classLoaderConfiguration,
                                                              @Nonnull DefaultScanResult scanResult,
+                                                             @Nonnull TestClassPath testClasspathWrapper,
                                                              @Nonnull Platform platform,
-                                                             @Nonnull TestClassPath testClasspathWrapper )
+                                                             @Nonnull ResolvePathResultWrapper resolvedJavaModularity )
         throws MojoExecutionException
     {
         try
         {
             Set<Artifact> providerArtifacts = provider.getProviderClasspath();
             String providerName = provider.getProviderName();
-            if ( isForking && canExecuteProviderWithModularPath( platform ) )
+            if ( isForking && canExecuteProviderWithModularPath( platform, resolvedJavaModularity ) )
             {
-                String jvmExecutable = platform.getJdkExecAttributesForTests().getJvmExecutable();
-                String javaHome = Paths.get( jvmExecutable )
-                        .toAbsolutePath()
-                        .normalize()
-                        .getParent()
-                        .getParent()
-                        .toString();
-
+                File jdkHome = platform.getJdkExecAttributesForTests().getJdkHome();
                 return newStartupConfigWithModularPath( classLoaderConfiguration, providerArtifacts, providerName,
-                        getModuleDescriptor(), scanResult, javaHome, testClasspathWrapper );
+                    resolvedJavaModularity, scanResult, jdkHome.getAbsolutePath(), testClasspathWrapper );
             }
             else
             {
@@ -1960,9 +1975,9 @@
     }
 
     private StartupConfiguration newStartupConfigWithModularPath(
-            @Nonnull ClassLoaderConfiguration classLoaderConfiguration, @Nonnull Set<Artifact> providerArtifacts,
-            @Nonnull String providerName, @Nonnull File moduleDescriptor, @Nonnull DefaultScanResult scanResult,
-            @Nonnull String javaHome, @Nonnull TestClassPath testClasspathWrapper )
+        @Nonnull ClassLoaderConfiguration classLoaderConfiguration, @Nonnull Set<Artifact> providerArtifacts,
+        @Nonnull String providerName, @Nonnull ResolvePathResultWrapper moduleDescriptor,
+        @Nonnull DefaultScanResult scanResult, @Nonnull String javaHome, @Nonnull TestClassPath testClasspathWrapper )
             throws IOException
     {
         Classpath testClasspath = testClasspathWrapper.toClasspath();
@@ -1975,7 +1990,7 @@
 
         ResolvePathsRequest<String> req = ResolvePathsRequest.ofStrings( testClasspath.getClassPath() )
                 .setJdkHome( javaHome )
-                .setMainModuleDescriptor( moduleDescriptor.getAbsolutePath() );
+                .setModuleDescriptor( moduleDescriptor.getResolvePathResult().getModuleDescriptor() );
 
         ResolvePathsResult<String> result = getLocationManager().resolvePaths( req );
         for ( Entry<String, Exception> entry : result.getPathExceptions().entrySet() )
@@ -2327,12 +2342,13 @@
     private ForkStarter createForkStarter( @Nonnull ProviderInfo provider, @Nonnull ForkConfiguration forkConfiguration,
                                            @Nonnull ClassLoaderConfiguration classLoaderConfiguration,
                                            @Nonnull RunOrderParameters runOrderParameters, @Nonnull ConsoleLogger log,
-                                           @Nonnull DefaultScanResult scanResult, @Nonnull Platform platform,
-                                           @Nonnull TestClassPath testClasspathWrapper )
+                                           @Nonnull DefaultScanResult scanResult,
+                                           @Nonnull TestClassPath testClasspathWrapper, @Nonnull Platform platform,
+                                           ResolvePathResultWrapper resolvedJavaModularityResult )
         throws MojoExecutionException, MojoFailureException
     {
         StartupConfiguration startupConfiguration = createStartupConfiguration( provider, true,
-                classLoaderConfiguration, scanResult, platform, testClasspathWrapper );
+                classLoaderConfiguration, scanResult, testClasspathWrapper, platform, resolvedJavaModularityResult );
         String configChecksum = getConfigChecksum();
         StartupReportConfiguration startupReportConfiguration = getStartupReportConfiguration( configChecksum, true );
         ProviderConfiguration providerConfiguration = createProviderConfiguration( runOrderParameters );
@@ -2349,7 +2365,7 @@
         throws MojoExecutionException, MojoFailureException
     {
         StartupConfiguration startupConfiguration = createStartupConfiguration( provider, false, classLoaderConfig,
-                scanResult, platform, testClasspathWrapper );
+                scanResult, testClasspathWrapper, platform, new ResolvePathResultWrapper( null, true ) );
         String configChecksum = getConfigChecksum();
         StartupReportConfiguration startupReportConfiguration = getStartupReportConfiguration( configChecksum, false );
         ProviderConfiguration providerConfiguration = createProviderConfiguration( runOrderParameters );
@@ -2366,7 +2382,8 @@
     }
 
     @Nonnull
-    private ForkConfiguration createForkConfiguration( Platform platform )
+    private ForkConfiguration createForkConfiguration( @Nonnull Platform platform,
+                                                       @Nonnull ResolvePathResultWrapper resolvedJavaModularityResult )
     {
         File tmpDir = getSurefireTempDir();
 
@@ -2378,7 +2395,7 @@
 
         getConsoleLogger().debug( "Found implementation of fork node factory: " + forkNode.getClass().getName() );
 
-        if ( canExecuteProviderWithModularPath( platform ) )
+        if ( canExecuteProviderWithModularPath( platform, resolvedJavaModularityResult ) )
         {
             return new ModularClasspathForkConfiguration( bootClasspath,
                     tmpDir,
@@ -2516,9 +2533,9 @@
 
     private JdkAttributes getEffectiveJvm() throws MojoFailureException
     {
-        if ( isNotEmpty( jvm ) )
+        if ( isNotEmpty( getJvm() ) )
         {
-            File pathToJava = new File( jvm ).getAbsoluteFile();
+            File pathToJava = new File( getJvm() ).getAbsoluteFile();
             if ( !endsWithJavaPath( pathToJava.getPath() ) )
             {
                 throw new MojoFailureException( "Given path does not end with java executor \""
@@ -2533,13 +2550,17 @@
             }
 
             File jdkHome = toJdkHomeFromJvmExec( pathToJava.getPath() );
-            if ( !environmentVariables.containsKey( "JAVA_HOME" ) )
+            if ( jdkHome == null )
             {
-                environmentVariables.put( "JAVA_HOME", jdkHome.getAbsolutePath() );
+                getConsoleLogger().warning( "Cannot determine JAVA_HOME of jvm exec path " + pathToJava );
+            }
+            else if ( !getEnvironmentVariables().containsKey( "JAVA_HOME" ) )
+            {
+                getEnvironmentVariables().put( "JAVA_HOME", jdkHome.getAbsolutePath() );
             }
             BigDecimal version = jdkHome == null ? null : toJdkVersionFromReleaseFile( jdkHome );
             boolean javaVersion9 = version == null ? isJava9AtLeast( pathToJava.getPath() ) : isJava9AtLeast( version );
-            return new JdkAttributes( pathToJava.getPath(), javaVersion9 );
+            return new JdkAttributes( pathToJava, jdkHome, javaVersion9 );
         }
 
         if ( toolchain != null )
@@ -2548,6 +2569,7 @@
             if ( isNotEmpty( jvmToUse ) )
             {
                 boolean javaVersion9 = false;
+                String jdkHome = null;
 
                 if ( toolchain instanceof DefaultToolchain )
                 {
@@ -2559,9 +2581,10 @@
                 if ( toolchain instanceof DefaultJavaToolChain )
                 {
                     DefaultJavaToolChain defaultJavaToolChain = (DefaultJavaToolChain) toolchain;
-                    if ( !environmentVariables.containsKey( "JAVA_HOME" ) )
+                    if ( !getEnvironmentVariables().containsKey( "JAVA_HOME" ) )
                     {
-                        environmentVariables.put( "JAVA_HOME", defaultJavaToolChain.getJavaHome() );
+                        jdkHome = defaultJavaToolChain.getJavaHome();
+                        getEnvironmentVariables().put( "JAVA_HOME", jdkHome );
                     }
                 }
 
@@ -2570,7 +2593,8 @@
                     javaVersion9 = isJava9AtLeast( jvmToUse );
                 }
 
-                return new JdkAttributes( jvmToUse, javaVersion9 );
+                return new JdkAttributes( new File( jvmToUse ),
+                    jdkHome == null ? toJdkHomeFromJvmExec( jvmToUse ) : new File( jdkHome ), javaVersion9 );
             }
         }
 
@@ -2621,7 +2645,7 @@
         checksum.add( isSkipExec() );
         checksum.add( isSkip() );
         checksum.add( getTestClassesDirectory() );
-        checksum.add( getClassesDirectory() );
+        checksum.add( getMainBuildPath() );
         checksum.add( getClasspathDependencyExcludes() );
         checksum.add( getClasspathDependencyScopeExclude() );
         checksum.add( getAdditionalClasspathElements() );
@@ -2741,7 +2765,7 @@
             classpathArtifacts = filterArtifacts( classpathArtifacts, dependencyFilter );
         }
 
-        return new TestClassPath( classpathArtifacts, getClassesDirectory(),
+        return new TestClassPath( classpathArtifacts, getMainBuildPath(),
                 getTestClassesDirectory(), getAdditionalClasspathElements() );
     }
 
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/JdkAttributes.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/JdkAttributes.java
index 96049a5..f8ab5f7 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/JdkAttributes.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/JdkAttributes.java
@@ -19,7 +19,10 @@
  * under the License.
  */
 
+import java.io.File;
+
 import static java.util.Objects.requireNonNull;
+import static org.apache.maven.surefire.booter.SystemUtils.toJdkHomeFromJvmExec;
 
 /**
  * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
@@ -27,20 +30,32 @@
  */
 public final class JdkAttributes
 {
-    private final String jvmExecutable;
+    private final File jvmExecutable;
+    private final File jdkHome;
     private final boolean java9AtLeast;
 
-    public JdkAttributes( String jvmExecutable, boolean java9AtLeast )
+    public JdkAttributes( File jvmExecutable, File jdkHome, boolean java9AtLeast )
     {
         this.jvmExecutable = requireNonNull( jvmExecutable, "null path to java executable" );
+        this.jdkHome = jdkHome;
         this.java9AtLeast = java9AtLeast;
     }
 
-    public String getJvmExecutable()
+    public JdkAttributes( String jvmExecutable, boolean java9AtLeast )
+    {
+        this( new File( jvmExecutable ), toJdkHomeFromJvmExec( jvmExecutable ), java9AtLeast );
+    }
+
+    public File getJvmExecutable()
     {
         return jvmExecutable;
     }
 
+    public File getJdkHome()
+    {
+        return jdkHome;
+    }
+
     public boolean isJava9AtLeast()
     {
         return java9AtLeast;
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ResolvePathResultWrapper.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ResolvePathResultWrapper.java
new file mode 100644
index 0000000..9197d89
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ResolvePathResultWrapper.java
@@ -0,0 +1,50 @@
+package org.apache.maven.plugin.surefire;
+
+/*
+ * 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.codehaus.plexus.languages.java.jpms.ResolvePathResult;
+
+/**
+ * Wraps {@link ResolvePathResult} and place marker.
+ */
+final class ResolvePathResultWrapper
+{
+    private final ResolvePathResult resolvePathResult;
+    private final boolean isMainModuleDescriptor;
+
+    ResolvePathResultWrapper( ResolvePathResult resolvePathResult, boolean isMainModuleDescriptor )
+    {
+        this.resolvePathResult = resolvePathResult;
+        this.isMainModuleDescriptor = isMainModuleDescriptor;
+    }
+
+    ResolvePathResult getResolvePathResult()
+    {
+        return resolvePathResult;
+    }
+
+    /**
+     * @return {@code true} if module-info appears in src/main/java module
+     */
+    boolean isMainModuleDescriptor()
+    {
+        return isMainModuleDescriptor;
+    }
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java
index 4425ddc..e66a7dd 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java
@@ -50,9 +50,9 @@
 
     void setTestClassesDirectory( File testClassesDirectory );
 
-    File getClassesDirectory();
+    File getMainBuildPath();
 
-    void setClassesDirectory( File classesDirectory );
+    void setMainBuildPath( File mainBuildPath );
 
     File getReportsDirectory();
 
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfiguration.java
index 443bf45..af09d67 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfiguration.java
@@ -144,7 +144,7 @@
             cli.addEnvironment( entry.getKey(), value == null ? "" : value );
         }
 
-        cli.setExecutable( getJdkForTests().getJvmExecutable() );
+        cli.setExecutable( getJdkForTests().getJvmExecutable().getAbsolutePath() );
 
         String jvmArgLine = newJvmArgLine( forkNumber );
         if ( !jvmArgLine.isEmpty() )
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 074a3cb..14196f8 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
@@ -38,7 +38,7 @@
 {
     private final RunnableFuture<Long> pluginPidJob;
 
-    private volatile JdkAttributes jdk;
+    private final JdkAttributes jdk;
 
     public Platform()
     {
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoJava7PlusTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoJava7PlusTest.java
index abd896e..e1a3b27 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoJava7PlusTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoJava7PlusTest.java
@@ -33,6 +33,7 @@
 import org.apache.maven.surefire.util.DefaultScanResult;
 import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor;
 import org.codehaus.plexus.languages.java.jpms.LocationManager;
+import org.codehaus.plexus.languages.java.jpms.ResolvePathResult;
 import org.codehaus.plexus.languages.java.jpms.ResolvePathsRequest;
 import org.codehaus.plexus.languages.java.jpms.ResolvePathsResult;
 import org.codehaus.plexus.languages.java.jpms.ModuleNameSource;
@@ -138,14 +139,15 @@
         provider.setFile( mockFile( "surefire-provider.jar" ) );
         Set<Artifact> providerClasspath = singleton( provider );
 
-        File moduleInfo = mockFile( "classes/module-info.class" );
+        ResolvePathResult moduleInfo = mock( ResolvePathResult.class );
+        when( moduleInfo.getModuleDescriptor() ).thenReturn( descriptor );
 
         @SuppressWarnings( "unchecked" )
         ResolvePathsRequest<String> req = mock( ResolvePathsRequest.class );
         mockStatic( ResolvePathsRequest.class );
         when( ResolvePathsRequest.ofStrings( eq( testClasspath.toClasspath().getClassPath() ) ) ).thenReturn( req );
         when( req.setJdkHome( anyString() ) ).thenReturn( req );
-        when( req.setMainModuleDescriptor( eq( moduleInfo.getAbsolutePath() ) ) ).thenReturn( req );
+        when( req.setModuleDescriptor( eq( descriptor ) ) ).thenReturn( req );
 
         when( descriptor.name() ).thenReturn( "abc" );
 
@@ -204,8 +206,8 @@
         when( mojo.getPluginArtifactMap() ).thenReturn( artifacts );
 
         StartupConfiguration conf = invokeMethod( mojo, "newStartupConfigWithModularPath",
-                classLoaderConfiguration, providerClasspath, "org.asf.Provider", moduleInfo, scanResult,
-                "", testClasspath );
+            classLoaderConfiguration, providerClasspath, "org.asf.Provider",
+            new ResolvePathResultWrapper( moduleInfo, true ), scanResult, "", testClasspath );
 
         verify( mojo, times( 1 ) ).effectiveIsEnableAssertions();
         verify( mojo, times( 1 ) ).isChildDelegation();
@@ -213,7 +215,7 @@
         verify( scanResult, times( 1 ) ).getClasses();
         verifyStatic( ResolvePathsRequest.class, times( 1 ) );
         ResolvePathsRequest.ofStrings( eq( testClasspath.toClasspath().getClassPath() ) );
-        verify( req, times( 1 ) ).setMainModuleDescriptor( eq( moduleInfo.getAbsolutePath() ) );
+        verify( req, times( 1 ) ).setModuleDescriptor( eq( descriptor ) );
         verify( res, times( 1 ) ).getClasspathElements();
         verify( res, times( 1 ) ).getModulepathElements();
         verify( locationManager, times( 1 ) ).resolvePaths( eq( req ) );
@@ -347,13 +349,13 @@
         }
 
         @Override
-        public File getClassesDirectory()
+        public File getMainBuildPath()
         {
             return null;
         }
 
         @Override
-        public void setClassesDirectory( File classesDirectory )
+        public void setMainBuildPath( File mainBuildPath )
         {
 
         }
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java
index 7ed96b3..8647c0f 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java
@@ -33,6 +33,7 @@
 import org.apache.maven.plugin.MojoFailureException;
 import org.apache.maven.plugin.descriptor.PluginDescriptor;
 import org.apache.maven.plugin.surefire.AbstractSurefireMojo.JUnitPlatformProviderInfo;
+import org.apache.maven.plugin.surefire.booterclient.Platform;
 import org.apache.maven.plugin.surefire.log.PluginConsoleLogger;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.ProjectBuildingRequest;
@@ -43,15 +44,24 @@
 import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolver;
 import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
 import org.apache.maven.surefire.booter.Classpath;
+import org.apache.maven.surefire.booter.ModularClasspathConfiguration;
 import org.apache.maven.surefire.booter.StartupConfiguration;
 import org.apache.maven.surefire.extensions.ForkNodeFactory;
 import org.apache.maven.surefire.suite.RunResult;
+import org.apache.maven.surefire.util.DefaultScanResult;
 import org.apache.maven.toolchain.Toolchain;
+import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor;
+import org.codehaus.plexus.languages.java.jpms.LocationManager;
+import org.codehaus.plexus.languages.java.jpms.ResolvePathRequest;
+import org.codehaus.plexus.languages.java.jpms.ResolvePathResult;
+import org.codehaus.plexus.languages.java.jpms.ResolvePathsRequest;
+import org.codehaus.plexus.languages.java.jpms.ResolvePathsResult;
 import org.codehaus.plexus.logging.Logger;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
@@ -63,6 +73,8 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -75,21 +87,27 @@
 import java.util.Set;
 
 import static java.io.File.separatorChar;
+import static java.nio.file.Files.write;
 import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.emptyMap;
 import static java.util.Collections.emptySet;
 import static java.util.Collections.singleton;
 import static java.util.Collections.singletonList;
-import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_WINDOWS;
 import static org.apache.maven.artifact.versioning.VersionRange.createFromVersion;
 import static org.apache.maven.artifact.versioning.VersionRange.createFromVersionSpec;
+import static org.apache.maven.surefire.shared.lang3.JavaVersion.JAVA_9;
+import static org.apache.maven.surefire.shared.lang3.JavaVersion.JAVA_RECENT;
+import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_WINDOWS;
+import static org.codehaus.plexus.languages.java.jpms.ModuleNameSource.MODULEDESCRIPTOR;
 import static org.fest.assertions.Assertions.assertThat;
 import static org.fest.assertions.MapAssert.entry;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 import static org.powermock.api.mockito.PowerMockito.doNothing;
 import static org.powermock.api.mockito.PowerMockito.doReturn;
 import static org.powermock.api.mockito.PowerMockito.mock;
@@ -109,6 +127,9 @@
     @Rule
     public final ExpectedException e = ExpectedException.none();
 
+    @Rule
+    public final TemporaryFolder tempFolder = new TemporaryFolder();
+
     @Mock
     private ArtifactHandler handler;
 
@@ -140,6 +161,119 @@
     }
 
     @Test
+    public void noModuleDescriptorFile() throws Exception
+    {
+        AbstractSurefireMojo mojo = spy( new Mojo() );
+        mojo.setMainBuildPath( tempFolder.newFolder() );
+        File jdkHome = new File( System.getProperty( "java.home" ) );
+        ResolvePathResultWrapper wrapper = invokeMethod( mojo, "findModuleDescriptor", jdkHome );
+
+        assertThat( wrapper )
+            .isNotNull();
+
+        assertThat( wrapper.getResolvePathResult() )
+            .isNull();
+
+        assertThat( invokeMethod( mojo, "existsModuleDescriptor", wrapper ) )
+            .isEqualTo( false );
+
+        when( mojo.useModulePath() ).thenReturn( true );
+
+        File jvmExecutable = new File( jdkHome, IS_OS_WINDOWS ? "bin\\java.exe" : "bin/java" );
+        JdkAttributes jdkAttributes = new JdkAttributes( jvmExecutable, jdkHome, true );
+        Platform platform = new Platform().withJdkExecAttributesForTests( jdkAttributes );
+        assertThat( invokeMethod( mojo, "canExecuteProviderWithModularPath", platform, wrapper ) )
+            .isEqualTo( false );
+    }
+
+    @Test
+    public void correctModuleDescriptor() throws Exception
+    {
+        AbstractSurefireMojo mojo = spy( new Mojo() );
+        LocationManager locationManager = mock( LocationManager.class );
+        ResolvePathResult result = mock( ResolvePathResult.class );
+        when( result.getModuleNameSource() ).thenReturn( MODULEDESCRIPTOR );
+        JavaModuleDescriptor descriptor = mock( JavaModuleDescriptor.class );
+        when( result.getModuleDescriptor() ).thenReturn( descriptor );
+        when( locationManager.resolvePath( any( ResolvePathRequest.class ) ) ).thenReturn( result );
+        doReturn( locationManager )
+            .when( mojo, "getLocationManager" );
+        File classesDir = tempFolder.newFolder();
+        mojo.setMainBuildPath( classesDir );
+        File descriptorFile = new File( classesDir, "module-info.class" );
+        assertThat( descriptorFile.createNewFile() ).isTrue();
+        File jdkHome = new File( System.getProperty( "java.home" ) );
+        ResolvePathResultWrapper wrapper = invokeMethod( mojo, "findModuleDescriptor", jdkHome );
+
+        assertThat( wrapper )
+            .isNotNull();
+
+        assertThat( wrapper.getResolvePathResult() )
+            .isSameAs( result );
+
+        assertThat( wrapper.getResolvePathResult().getModuleNameSource() )
+            .isSameAs( MODULEDESCRIPTOR );
+
+        assertThat( wrapper.getResolvePathResult().getModuleDescriptor() )
+            .isSameAs( descriptor );
+
+        assertThat( invokeMethod( mojo, "existsModuleDescriptor", wrapper ) )
+            .isEqualTo( true );
+
+        when( mojo.useModulePath() ).thenReturn( true );
+
+        File jvmExecutable = new File( jdkHome, IS_OS_WINDOWS ? "bin\\java.exe" : "bin/java" );
+        JdkAttributes jdkAttributes = new JdkAttributes( jvmExecutable, jdkHome, true );
+        Platform platform = new Platform().withJdkExecAttributesForTests( jdkAttributes );
+        assertThat( invokeMethod( mojo, "canExecuteProviderWithModularPath", platform, wrapper ) )
+            .isEqualTo( true );
+
+        jdkAttributes = new JdkAttributes( jvmExecutable, jdkHome, false );
+        platform = new Platform().withJdkExecAttributesForTests( jdkAttributes );
+        assertThat( invokeMethod( mojo, "canExecuteProviderWithModularPath", platform, wrapper ) )
+            .isEqualTo( false );
+
+        when( mojo.useModulePath() ).thenReturn( false );
+
+        jdkAttributes = new JdkAttributes( jvmExecutable, jdkHome, true );
+        platform = new Platform().withJdkExecAttributesForTests( jdkAttributes );
+        assertThat( invokeMethod( mojo, "canExecuteProviderWithModularPath", platform, wrapper ) )
+            .isEqualTo( false );
+    }
+
+    @Test
+    @SuppressWarnings( "checkstyle:magicnumber" )
+    public void corruptedModuleDescriptor() throws Exception
+    {
+        if ( !JAVA_RECENT.atLeast( JAVA_9 ) )
+        {
+            return;
+        }
+
+        AbstractSurefireMojo mojo = spy( new Mojo() );
+        doReturn( new LocationManager() )
+            .when( mojo, "getLocationManager" );
+        File classesDir = tempFolder.newFolder();
+        mojo.setMainBuildPath( classesDir );
+
+        File descriptorFile = new File( classesDir, "module-info.class" );
+        assertThat( descriptorFile.createNewFile() ).isTrue();
+        write( descriptorFile.toPath(), new byte[]{(byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE} );
+
+        File jdkHome = new File( System.getProperty( "java.home" ) );
+        ResolvePathResultWrapper wrapper = invokeMethod( mojo, "findModuleDescriptor", jdkHome );
+
+        assertThat( wrapper )
+            .isNotNull();
+
+        assertThat( wrapper.getResolvePathResult() )
+            .isNull();
+
+        assertThat( invokeMethod( mojo, "existsModuleDescriptor", wrapper ) )
+            .isEqualTo( false );
+    }
+
+    @Test
     public void shouldShowArray() throws Exception
     {
         Logger logger = mock( Logger.class );
@@ -249,7 +383,7 @@
     {
         AbstractSurefireMojo mojo = spy( this.mojo );
 
-        when( mojo.getClassesDirectory() ).thenReturn( new File( "target" + separatorChar + "classes" ) );
+        when( mojo.getMainBuildPath() ).thenReturn( new File( "target" + separatorChar + "classes" ) );
         when( mojo.getTestClassesDirectory() ).thenReturn( new File( "target" + separatorChar + "test-classes" ) );
         when( mojo.getClasspathDependencyScopeExclude() ).thenReturn( "runtime" );
         when( mojo.getClasspathDependencyExcludes() ).thenReturn( new String[]{ "g3:a3" } );
@@ -298,7 +432,7 @@
         TestClassPath cp = invokeMethod( mojo, "generateTestClasspath" );
 
         verifyPrivate( mojo, times( 1 ) ).invoke( "generateTestClasspath" );
-        verify( mojo, times( 1 ) ).getClassesDirectory();
+        verify( mojo, times( 1 ) ).getMainBuildPath();
         verify( mojo, times( 1 ) ).getTestClassesDirectory();
         verify( mojo, times( 3 ) ).getClasspathDependencyScopeExclude();
         verify( mojo, times( 2 ) ).getClasspathDependencyExcludes();
@@ -508,7 +642,81 @@
 
         doReturn( 1 ).when( mojo, "getEffectiveForkCount" );
 
-        return invokeMethod( mojo, "createStartupConfiguration", providerInfo, false, null, null, null, testClassPath );
+        return invokeMethod( mojo, "createStartupConfiguration",
+            providerInfo, false, null, null, testClassPath, null, null );
+    }
+
+    @Test
+    public void shouldCreateStartupConfigWithModularPath() throws Exception
+    {
+        String baseDir = System.getProperty( "user.dir" );
+
+        Mojo mojo = new Mojo();
+
+        // ### BEGIN
+        // we cannot mock private method newStartupConfigWithModularPath() - mocking the data to prevent from errors
+        LocationManager locationManager = mock( LocationManager.class );
+        ResolvePathsResult resolvePathsResult = mock( ResolvePathsResult.class );
+        when( locationManager.resolvePaths( any( ResolvePathsRequest.class ) ) ).thenReturn( resolvePathsResult );
+        when( resolvePathsResult.getPathExceptions() ).thenReturn( emptyMap() );
+        when( resolvePathsResult.getClasspathElements() ).thenReturn( emptyList() );
+        when( resolvePathsResult.getModulepathElements() ).thenReturn( emptyMap() );
+        JavaModuleDescriptor desc = mock( JavaModuleDescriptor.class );
+        when( desc.name() ).thenReturn( "" );
+        when( resolvePathsResult.getMainModuleDescriptor() ).thenReturn( desc );
+
+        mojo.setLogger( mock( Logger.class ) );
+        mojo.setUseModulePath( true );
+        setInternalState( mojo, "locationManager", locationManager );
+
+        File jdkHome = new File( System.getProperty( "java.home" ) );
+        File jvmExecutable = new File( jdkHome, IS_OS_WINDOWS ? "bin\\java.exe" : "bin/java" );
+        JdkAttributes jdkAttributes = new JdkAttributes( jvmExecutable, jdkHome, true );
+        Platform platform = new Platform().withJdkExecAttributesForTests( jdkAttributes );
+
+        File classesDirectory = new File( baseDir, "mock-dir" );
+        File testClassesDirectory = new File( baseDir, "mock-dir" );
+        TestClassPath testClassPath = new TestClassPath( Collections.<Artifact>emptySet(),
+            classesDirectory, testClassesDirectory, new String[0] );
+
+        ProviderInfo providerInfo = mock( ProviderInfo.class );
+        when( providerInfo.getProviderName() ).thenReturn( "provider mock" );
+        when( providerInfo.getProviderClasspath() ).thenReturn( Collections.<Artifact>emptySet() );
+
+        DefaultScanResult defaultScanResult = mock( DefaultScanResult.class );
+        when( defaultScanResult.getClasses() ).thenReturn( Collections.<String>emptyList() );
+
+        Path pathToModularDescriptor =
+            Paths.get( baseDir, "src", "test", "resources", "org", "apache", "maven", "plugin", "surefire" );
+        mojo.setMainBuildPath( pathToModularDescriptor.toFile() );
+
+        Map<String, Artifact> artifacts = new HashMap<>();
+        Artifact dummyArtifact = mock( Artifact.class );
+        when( dummyArtifact.getFile() ).thenReturn( new File( baseDir, "mock-file" ) );
+        artifacts.put( "org.apache.maven.surefire:maven-surefire-common", dummyArtifact );
+        artifacts.put( "org.apache.maven.surefire:surefire-extensions-api", dummyArtifact );
+        artifacts.put( "org.apache.maven.surefire:surefire-api", dummyArtifact );
+        artifacts.put( "org.apache.maven.surefire:surefire-logger-api", dummyArtifact );
+        artifacts.put( "org.apache.maven.surefire:surefire-extensions-spi", dummyArtifact );
+        artifacts.put( "org.apache.maven.surefire:surefire-booter", dummyArtifact );
+        artifacts.put( "org.apache.maven.surefire:surefire-shared-utils", dummyArtifact );
+        mojo.setPluginArtifactMap( artifacts );
+
+        ResolvePathResult resolvePathResult = mock( ResolvePathResult.class );
+        ResolvePathResultWrapper wrapper = new ResolvePathResultWrapper( resolvePathResult, true );
+        // ### END
+
+        StartupConfiguration actualConfig = invokeMethod( mojo, "createStartupConfiguration",
+            new Class[] {ProviderInfo.class, boolean.class, ClassLoaderConfiguration.class, DefaultScanResult.class,
+                TestClassPath.class, Platform.class, ResolvePathResultWrapper.class},
+            providerInfo, true, mock( ClassLoaderConfiguration.class ), defaultScanResult,
+            testClassPath, platform, wrapper );
+
+        assertThat( actualConfig )
+            .isNotNull();
+
+        assertThat( actualConfig.getClasspathConfiguration() )
+            .isInstanceOf( ModularClasspathConfiguration.class );
     }
 
     @Test
@@ -1804,6 +2012,9 @@
     public static class Mojo
             extends AbstractSurefireMojo implements SurefireReportParameters
     {
+        private File mainBuildPath;
+        private boolean useModulePath;
+
         private JUnitPlatformProviderInfo createJUnitPlatformProviderInfo( Artifact junitPlatformArtifact,
                                                                            TestClassPath testClasspathWrapper )
         {
@@ -1901,15 +2112,15 @@
         }
 
         @Override
-        public File getClassesDirectory()
+        public File getMainBuildPath()
         {
-            return null;
+            return mainBuildPath;
         }
 
         @Override
-        public void setClassesDirectory( File classesDirectory )
+        public void setMainBuildPath( File mainBuildPath )
         {
-
+            this.mainBuildPath = mainBuildPath;
         }
 
         @Override
@@ -2185,13 +2396,13 @@
         @Override
         protected boolean useModulePath()
         {
-            return false;
+            return useModulePath;
         }
 
         @Override
         protected void setUseModulePath( boolean useModulePath )
         {
-
+            this.useModulePath = useModulePath;
         }
 
         @Override
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoToolchainsTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoToolchainsTest.java
index 64437fb..ddcb169 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoToolchainsTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoToolchainsTest.java
@@ -37,6 +37,7 @@
 import java.util.List;
 import java.util.Map;
 
+import static java.io.File.separatorChar;
 import static java.util.Collections.emptyMap;
 import static java.util.Collections.singletonList;
 import static java.util.Collections.singletonMap;
@@ -155,7 +156,8 @@
         assertThat( mojo.getEnvironmentVariables() ).isEmpty();
         JdkAttributes effectiveJvm = invokeMethod( mojo, "getEffectiveJvm" );
         assertThat( mojo.getEnvironmentVariables() ).includes( MapAssert.entry( "JAVA_HOME", "/some/path" ) );
-        assertThat( effectiveJvm.getJvmExecutable() ).contains( "/path/from/toolchain" );
+        assertThat( effectiveJvm.getJvmExecutable().getPath() )
+            .contains( "/path/from/toolchain".replace( '/', separatorChar ) );
     }
 
     @Test
@@ -171,7 +173,8 @@
 
         JdkAttributes effectiveJvm = invokeMethod( mojo, "getEffectiveJvm" );
         assertThat( mojo.getEnvironmentVariables() ).includes( MapAssert.entry( "JAVA_HOME", "/already/set/path" ) );
-        assertThat( effectiveJvm.getJvmExecutable() ).contains( "/path/from/toolchain" );
+        assertThat( effectiveJvm.getJvmExecutable().getPath() )
+            .contains( "/path/from/toolchain".replace( '/', separatorChar ) );
     }
 
     /**
@@ -191,9 +194,9 @@
 
         assertThat( mojo.getEnvironmentVariables() ).isEmpty();
         JdkAttributes effectiveJvm = invokeMethod( mojo, "getEffectiveJvm" );
-        assertThat( mojo.getEnvironmentVariables() ).
-            includes( MapAssert.entry( "JAVA_HOME", currentJdkHome.getAbsolutePath() ) );
-        assertThat( effectiveJvm.getJvmExecutable() ).contains( javaExecutablePath );
+        assertThat( mojo.getEnvironmentVariables() )
+            .includes( MapAssert.entry( "JAVA_HOME", currentJdkHome.getAbsolutePath() ) );
+        assertThat( effectiveJvm.getJvmExecutable().getPath() ).contains( javaExecutablePath );
     }
 
     /**
@@ -207,15 +210,14 @@
         mojo.setEnvironmentVariables( singletonMap( "JAVA_HOME", "/already/set/path" ) );
 
         File currentJdkHome = toJdkHomeFromJre();
-        String javaExecutablePath = FilenameUtils.concat(
-            currentJdkHome.getAbsolutePath(), "bin/java" );
+        String javaExecutablePath = FilenameUtils.concat( currentJdkHome.getAbsolutePath(), "bin/java" );
 
         mojo.setJvm( javaExecutablePath );
 
         JdkAttributes effectiveJvm = invokeMethod( mojo, "getEffectiveJvm" );
-        assertThat( mojo.getEnvironmentVariables() ).
-            includes( MapAssert.entry( "JAVA_HOME", "/already/set/path" ) );
-        assertThat( effectiveJvm.getJvmExecutable() ).contains( javaExecutablePath );
+        assertThat( mojo.getEnvironmentVariables() )
+            .includes( MapAssert.entry( "JAVA_HOME", "/already/set/path" ) );
+        assertThat( effectiveJvm.getJvmExecutable().getPath() ).contains( javaExecutablePath );
     }
 
 
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/MojoMocklessTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/MojoMocklessTest.java
index 835ce8e..dda40b3 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/MojoMocklessTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/MojoMocklessTest.java
@@ -452,13 +452,13 @@
         }
 
         @Override
-        public File getClassesDirectory()
+        public File getMainBuildPath()
         {
             return null;
         }
 
         @Override
-        public void setClassesDirectory( File classesDirectory )
+        public void setMainBuildPath( File mainBuildPath )
         {
 
         }
diff --git a/maven-surefire-common/src/test/resources/org/apache/maven/plugin/surefire/module-info.class b/maven-surefire-common/src/test/resources/org/apache/maven/plugin/surefire/module-info.class
new file mode 100644
index 0000000..51e6dcd
--- /dev/null
+++ b/maven-surefire-common/src/test/resources/org/apache/maven/plugin/surefire/module-info.class
Binary files differ
diff --git a/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java b/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
index 4d47596..35d15fa 100644
--- a/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
+++ b/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
@@ -565,15 +565,15 @@
     }
 
     @Override
-    public File getClassesDirectory()
+    public File getMainBuildPath()
     {
         return classesDirectory;
     }
 
     @Override
-    public void setClassesDirectory( File classesDirectory )
+    public void setMainBuildPath( File mainBuildPath )
     {
-        this.classesDirectory = classesDirectory;
+        classesDirectory = mainBuildPath;
     }
 
     @Override
diff --git a/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1570ModularFailsafeIT.java b/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1570ModularFailsafeIT.java
new file mode 100644
index 0000000..83d3d08
--- /dev/null
+++ b/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1570ModularFailsafeIT.java
@@ -0,0 +1,54 @@
+package org.apache.maven.surefire.its.jiras;
+
+/*
+ * 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.surefire.its.fixture.SurefireJUnit4IntegrationTestCase;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.apache.maven.surefire.its.fixture.HelperAssertions.assumeJavaVersion;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+
+/**
+ *
+ * @see <a href="https://issues.apache.org/jira/browse/SUREFIRE-1570">SUREFIRE-1570</a>
+ */
+@SuppressWarnings( "checkstyle:magicnumber" )
+public class Surefire1570ModularFailsafeIT
+    extends SurefireJUnit4IntegrationTestCase
+{
+    @Before
+    public void setUp()
+    {
+        assumeJavaVersion( 9d );
+    }
+
+    @Test
+    public void shouldRunWithJupiterApi() throws Exception
+    {
+        unpack( "surefire-1570" )
+            .debugLogging()
+            .executeVerify()
+            .verifyErrorFreeLog()
+            .assertThatLogLine( containsString( "Lets see JDKModulePath" ), is( 2 ) )
+            .assertThatLogLine( containsString( "Lets see JDKModulePath: null" ), is( 0 ) );
+    }
+}
diff --git a/surefire-its/src/test/resources/java9-full-api/src/test/java/J9IT.java b/surefire-its/src/test/resources/java9-full-api/src/test/java/java9/full/api/J9IT.java
similarity index 98%
rename from surefire-its/src/test/resources/java9-full-api/src/test/java/J9IT.java
rename to surefire-its/src/test/resources/java9-full-api/src/test/java/java9/full/api/J9IT.java
index c74678d..9c8f976 100644
--- a/surefire-its/src/test/resources/java9-full-api/src/test/java/J9IT.java
+++ b/surefire-its/src/test/resources/java9-full-api/src/test/java/java9/full/api/J9IT.java
@@ -1,3 +1,5 @@
+package java9.full.api;
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
diff --git a/surefire-its/src/test/resources/java9-full-api/src/test/java/J9Test.java b/surefire-its/src/test/resources/java9-full-api/src/test/java/java9/full/api/J9Test.java
similarity index 98%
rename from surefire-its/src/test/resources/java9-full-api/src/test/java/J9Test.java
rename to surefire-its/src/test/resources/java9-full-api/src/test/java/java9/full/api/J9Test.java
index 6fc8536..71e645f 100644
--- a/surefire-its/src/test/resources/java9-full-api/src/test/java/J9Test.java
+++ b/surefire-its/src/test/resources/java9-full-api/src/test/java/java9/full/api/J9Test.java
@@ -1,3 +1,5 @@
+package java9.full.api;
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
diff --git a/surefire-its/src/test/resources/surefire-1570/com.foo.api/pom.xml b/surefire-its/src/test/resources/surefire-1570/com.foo.api/pom.xml
new file mode 100644
index 0000000..6e1883c
--- /dev/null
+++ b/surefire-its/src/test/resources/surefire-1570/com.foo.api/pom.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<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>com.foo</groupId>
+        <artifactId>com.foo</artifactId>
+        <version>1.0.0</version>
+    </parent>
+
+    <artifactId>com.foo.api</artifactId>
+    
+    <dependencies>
+        <dependency>
+            <groupId>jakarta.persistence</groupId>
+            <artifactId>jakarta.persistence-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.ws.rs</groupId>
+            <artifactId>jakarta.ws.rs-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.xml.bind</groupId>
+            <artifactId>jakarta.xml.bind-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/surefire-its/src/test/resources/surefire-1570/com.foo.api/src/main/java/com/foo/api/SomeInterface.java b/surefire-its/src/test/resources/surefire-1570/com.foo.api/src/main/java/com/foo/api/SomeInterface.java
new file mode 100644
index 0000000..43ecf73
--- /dev/null
+++ b/surefire-its/src/test/resources/surefire-1570/com.foo.api/src/main/java/com/foo/api/SomeInterface.java
@@ -0,0 +1,28 @@
+package com.foo.api;
+
+/*
+ * 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.
+ */
+
+/**
+ *
+ */
+public interface SomeInterface
+{
+    void doItNow( Class<?> observer );
+}
diff --git a/surefire-its/src/test/resources/surefire-1570/com.foo.api/src/main/java/module-info.java b/surefire-its/src/test/resources/surefire-1570/com.foo.api/src/main/java/module-info.java
new file mode 100644
index 0000000..95b05f7
--- /dev/null
+++ b/surefire-its/src/test/resources/surefire-1570/com.foo.api/src/main/java/module-info.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+module com.foo.api
+{
+    exports com.foo.api;
+    requires org.slf4j;
+    requires java.persistence;
+    requires java.ws.rs;
+    requires java.xml.bind;
+    requires jakarta.activation;
+}
diff --git a/surefire-its/src/test/resources/surefire-1570/com.foo.impl/pom.xml b/surefire-its/src/test/resources/surefire-1570/com.foo.impl/pom.xml
new file mode 100644
index 0000000..e151aff
--- /dev/null
+++ b/surefire-its/src/test/resources/surefire-1570/com.foo.impl/pom.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<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>com.foo</groupId>
+        <artifactId>com.foo</artifactId>
+        <version>1.0.0</version>
+    </parent>
+
+    <artifactId>com.foo.impl</artifactId>
+    
+    <dependencies>
+        <dependency>
+            <groupId>com.foo</groupId>
+            <artifactId>com.foo.api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+<!--                    <multiReleaseOutput>true</multiReleaseOutput>-->
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifestEntries>
+<!--                            <Multi-Release>true</Multi-Release>-->
+                        </manifestEntries>
+                    </archive>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>integration-tests</id>
+                        <goals>
+                            <goal>integration-test</goal>
+                            <goal>verify</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/surefire-its/src/test/resources/surefire-1570/com.foo.impl/src/main/java/com/foo/impl/Bar.java b/surefire-its/src/test/resources/surefire-1570/com.foo.impl/src/main/java/com/foo/impl/Bar.java
new file mode 100644
index 0000000..50009e3
--- /dev/null
+++ b/surefire-its/src/test/resources/surefire-1570/com.foo.impl/src/main/java/com/foo/impl/Bar.java
@@ -0,0 +1,51 @@
+package com.foo.impl;
+
+/*
+ * 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 com.foo.api.SomeInterface;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ */
+public class Bar implements SomeInterface
+{
+    private static final Logger LOG = LoggerFactory.getLogger( Bar.class );
+
+    private boolean weAreAmongModules;
+
+    @Override
+    public void doItNow( Class<?> observer )
+    {
+        ModuleLayer.boot().modules().forEach( m -> {
+            if ( m == observer.getModule() || m == Bar.class.getModule() || m == Logger.class.getModule() )
+            {
+                weAreAmongModules = true;
+            }
+        } );
+        LOG.info( "" );
+        LOG.info( "Let's see if I or SLF4J are among boot layer modules: {}", weAreAmongModules );
+        if ( !weAreAmongModules )
+        {
+            LOG.info( "Maybe we are in child layer? Or this is not module path?" );
+        }
+    }
+}
diff --git a/surefire-its/src/test/resources/surefire-1570/com.foo.impl/src/main/java/module-info.java b/surefire-its/src/test/resources/surefire-1570/com.foo.impl/src/main/java/module-info.java
new file mode 100644
index 0000000..af383c6
--- /dev/null
+++ b/surefire-its/src/test/resources/surefire-1570/com.foo.impl/src/main/java/module-info.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+module com.foo.impl
+{
+    requires org.slf4j;
+    requires org.slf4j.simple;
+    opens com.foo.impl;
+    requires com.foo.api;
+}
diff --git a/surefire-its/src/test/resources/surefire-1570/com.foo.impl/src/test/java/com/foo/impl/BarIT.java b/surefire-its/src/test/resources/surefire-1570/com.foo.impl/src/test/java/com/foo/impl/BarIT.java
new file mode 100644
index 0000000..43596b7
--- /dev/null
+++ b/surefire-its/src/test/resources/surefire-1570/com.foo.impl/src/test/java/com/foo/impl/BarIT.java
@@ -0,0 +1,41 @@
+package com.foo.impl;
+
+/*
+ * 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.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ */
+public class BarIT
+{
+    private static final Logger LOG = LoggerFactory.getLogger( BarIT.class );
+
+    @Test
+    void shouldPrintModulePath()
+    {
+        Bar bar = new Bar();
+        LOG.info( "======INTEGRATION TEST=======" );
+        LOG.info( "Lets see JDKModulePath: {}", System.getProperty( "jdk.module.path" ) );
+        bar.doItNow( getClass() );
+    }
+}
diff --git a/surefire-its/src/test/resources/surefire-1570/com.foo.impl/src/test/java/com/foo/impl/BarTest.java b/surefire-its/src/test/resources/surefire-1570/com.foo.impl/src/test/java/com/foo/impl/BarTest.java
new file mode 100644
index 0000000..6e392b6
--- /dev/null
+++ b/surefire-its/src/test/resources/surefire-1570/com.foo.impl/src/test/java/com/foo/impl/BarTest.java
@@ -0,0 +1,41 @@
+package com.foo.impl;
+
+/*
+ * 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.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ */
+public class BarTest
+{
+    private static final Logger LOG = LoggerFactory.getLogger( BarTest.class );
+
+    @Test
+    void shouldPrintModulePath()
+    {
+        Bar bar = new Bar();
+        LOG.info( "======UNIT TEST=======" );
+        LOG.info( "Lets see JDKModulePath: {}", System.getProperty( "jdk.module.path" ) );
+        bar.doItNow( getClass() );
+    }
+}
diff --git a/surefire-its/src/test/resources/surefire-1570/pom.xml b/surefire-its/src/test/resources/surefire-1570/pom.xml
new file mode 100644
index 0000000..0f6bc9a
--- /dev/null
+++ b/surefire-its/src/test/resources/surefire-1570/pom.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<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>
+
+    <groupId>com.foo</groupId>
+    <artifactId>com.foo</artifactId>
+    <version>1.0.0</version>
+    <packaging>pom</packaging>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <maven.compiler.release>${java.specification.version}</maven.compiler.release>
+    </properties>
+
+    <modules>
+        <module>com.foo.api</module>
+        <module>com.foo.impl</module>
+    </modules>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>jakarta.persistence</groupId>
+                <artifactId>jakarta.persistence-api</artifactId>
+                <version>2.2.3</version>
+            </dependency>
+            <dependency>
+                <groupId>jakarta.ws.rs</groupId>
+                <artifactId>jakarta.ws.rs-api</artifactId>
+                <version>2.1.6</version>
+            </dependency>
+            <dependency>
+                <groupId>jakarta.xml.bind</groupId>
+                <artifactId>jakarta.xml.bind-api</artifactId>
+                <version>2.3.2</version>
+            </dependency>
+            <dependency>
+                <groupId>org.slf4j</groupId>
+                <artifactId>slf4j-api</artifactId>
+                <version>1.8.0-beta2</version>
+            </dependency>
+            <dependency>
+                <groupId>org.slf4j</groupId>
+                <artifactId>slf4j-simple</artifactId>
+                <version>1.8.0-beta2</version>
+            </dependency>
+            <dependency>
+               <groupId>org.junit.jupiter</groupId>
+               <artifactId>junit-jupiter-api</artifactId>
+               <version>5.6.2</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <version>3.8.1</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <version>${surefire.version}</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-jar-plugin</artifactId>
+                    <version>3.2.0</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-failsafe-plugin</artifactId>
+                    <version>${surefire.version}</version>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+</project>