[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/f337881f Tree: http://git-wip-us.apache.org/repos/asf/maven-surefire/tree/f337881f Diff: http://git-wip-us.apache.org/repos/asf/maven-surefire/diff/f337881f Branch: refs/heads/SUREFIRE-1262_3 Commit: f337881ff7f47ab35669d9c499213f5a6b9a4d8b Parents: 660a4cf Author: Tibor17 <tibordig...@apache.org> Authored: Fri Nov 17 15:09:52 2017 +0100 Committer: Tibor17 <tibordig...@apache.org> Committed: Sun Nov 19 01:56:51 2017 +0100 ---------------------------------------------------------------------- maven-surefire-common/pom.xml | 27 +- .../plugin/surefire/AbstractSurefireMojo.java | 271 ++++++-- .../surefire/InPluginVMSurefireStarter.java | 5 +- .../AbstractClasspathForkConfiguration.java | 73 +++ .../surefire/booterclient/BooterSerializer.java | 94 +-- .../ClasspathForkConfiguration.java | 59 ++ .../booterclient/DefaultForkConfiguration.java | 343 ++++++++++ .../booterclient/ForkConfiguration.java | 367 +---------- .../surefire/booterclient/ForkStarter.java | 27 +- .../JarManifestForkConfiguration.java | 139 ++++ .../ModularClasspathForkConfiguration.java | 230 +++++++ .../plugin/surefire/booterclient/Platform.java | 29 +- .../maven/plugin/surefire/util/Relocator.java | 39 +- .../surefire/providerapi/ServiceLoader.java | 2 +- .../surefire/AbstractSurefireMojoTest.java | 641 ++++++++++++++++++- ...terDeserializerStartupConfigurationTest.java | 8 +- .../DefaultForkConfigurationTest.java | 319 +++++++++ .../booterclient/ForkConfigurationTest.java | 52 +- .../apache/maven/surefire/JUnit4SuiteTest.java | 4 +- .../maven/surefire/util/RelocatorTest.java | 6 +- .../plugin/surefire/SurefirePluginTest.java | 3 +- pom.xml | 65 +- .../maven/surefire/util/DefaultScanResult.java | 24 +- .../maven/surefire/util/ScanResultTest.java | 8 +- .../booter/AbstractPathConfiguration.java | 115 ++++ .../apache/maven/surefire/booter/Classpath.java | 19 +- .../surefire/booter/ClasspathConfiguration.java | 73 +-- .../maven/surefire/booter/ForkedBooter.java | 22 +- .../maven/surefire/booter/ModularClasspath.java | 70 ++ .../booter/ModularClasspathConfiguration.java | 62 ++ .../surefire/booter/StartupConfiguration.java | 28 +- .../maven/surefire/booter/SystemUtilsTest.java | 5 +- .../apache/maven/surefire/its/ModulePathIT.java | 45 ++ .../src/test/resources/modulepath/pom.xml | 45 ++ .../modulepath/src/main/java/com/app/Main.java | 34 + .../modulepath/src/main/java/module-info.java | 21 + .../src/test/java/com/app/AppTest.java | 36 ++ 37 files changed, 2759 insertions(+), 651 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/f337881f/maven-surefire-common/pom.xml ---------------------------------------------------------------------- diff --git a/maven-surefire-common/pom.xml b/maven-surefire-common/pom.xml index 1bcd7c5..7e063c8 100644 --- a/maven-surefire-common/pom.xml +++ b/maven-surefire-common/pom.xml @@ -97,25 +97,36 @@ <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> <scope>test</scope> </dependency> + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-module-junit4</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-api-mockito2</artifactId> + <scope>test</scope> + </dependency> </dependencies> <build> http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/f337881f/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 7aa1e37..16b59ca 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 @@ -24,6 +24,7 @@ import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.factory.ArtifactFactory; import org.apache.maven.artifact.metadata.ArtifactMetadataSource; import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.AbstractArtifactResolutionException; import org.apache.maven.artifact.resolver.ArtifactNotFoundException; import org.apache.maven.artifact.resolver.ArtifactResolutionException; import org.apache.maven.artifact.resolver.ArtifactResolutionResult; @@ -43,6 +44,9 @@ 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.ClasspathForkConfiguration; +import org.apache.maven.plugin.surefire.booterclient.JarManifestForkConfiguration; +import org.apache.maven.plugin.surefire.booterclient.ModularClasspathForkConfiguration; import org.apache.maven.plugin.surefire.booterclient.Platform; import org.apache.maven.plugin.surefire.booterclient.ProviderDetector; import org.apache.maven.plugin.surefire.log.PluginConsoleLogger; @@ -58,6 +62,8 @@ import org.apache.maven.surefire.booter.ClassLoaderConfiguration; import org.apache.maven.surefire.booter.Classpath; import org.apache.maven.surefire.booter.ClasspathConfiguration; import org.apache.maven.surefire.booter.KeyValueSource; +import org.apache.maven.surefire.booter.ModularClasspath; +import org.apache.maven.surefire.booter.ModularClasspathConfiguration; import org.apache.maven.surefire.booter.ProviderConfiguration; import org.apache.maven.surefire.booter.ProviderParameterNames; import org.apache.maven.surefire.booter.Shutdown; @@ -81,6 +87,9 @@ import org.apache.maven.toolchain.DefaultToolchain; import org.apache.maven.toolchain.Toolchain; import org.apache.maven.toolchain.ToolchainManager; import org.codehaus.plexus.logging.Logger; +import org.codehaus.plexus.languages.java.jpms.LocationManager; +import org.codehaus.plexus.languages.java.jpms.ResolvePathsRequest; +import org.codehaus.plexus.languages.java.jpms.ResolvePathsResult; import javax.annotation.Nonnull; import java.io.File; @@ -88,7 +97,6 @@ import java.io.IOException; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; @@ -99,13 +107,18 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import static java.lang.Thread.currentThread; +import static java.util.Arrays.asList; +import static java.util.Collections.addAll; import static java.util.Collections.singletonMap; import static org.apache.commons.lang3.JavaVersion.JAVA_1_7; import static org.apache.commons.lang3.JavaVersion.JAVA_9; import static org.apache.commons.lang3.JavaVersion.JAVA_RECENT; +import static org.apache.commons.lang3.StringUtils.substringBeforeLast; import static org.apache.commons.lang3.SystemUtils.IS_OS_WINDOWS; import static org.apache.commons.lang3.SystemUtils.isJavaVersionAtLeast; import static org.apache.maven.shared.utils.StringUtils.capitalizeFirstLetter; @@ -133,12 +146,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(); @@ -731,6 +745,9 @@ public abstract class AbstractSurefireMojo @Component private ToolchainManager toolchainManager; + @Component + private LocationManager locationManager; + private Artifact surefireBooterArtifact; private Toolchain toolchain; @@ -887,7 +904,7 @@ public abstract class AbstractSurefireMojo // @TODO noinspection unchecked, check MavenProject 3.x for Generics in surefire:3.0 @SuppressWarnings( "unchecked" ) List<File> dependenciesToScan = - DependencyScanner.filter( project.getTestArtifacts(), Arrays.asList( getDependenciesToScan() ) ); + DependencyScanner.filter( project.getTestArtifacts(), asList( getDependenciesToScan() ) ); DependencyScanner scanner = new DependencyScanner( dependenciesToScan, getIncludedAndExcludedTests() ); return scanner.scan(); } @@ -1107,7 +1124,7 @@ public abstract class AbstractSurefireMojo createCopyAndReplaceForkNumPlaceholder( effectiveProperties, 1 ).copyToSystemProperties(); InPluginVMSurefireStarter surefireStarter = - createInprocessStarter( provider, classLoaderConfiguration, runOrderParameters ); + createInprocessStarter( provider, classLoaderConfiguration, runOrderParameters, scanResult ); return surefireStarter.runSuitesInProcess( scanResult ); } else @@ -1123,7 +1140,7 @@ public abstract class AbstractSurefireMojo try { forkStarter = createForkStarter( provider, forkConfiguration, classLoaderConfiguration, - runOrderParameters, getConsoleLogger() ); + runOrderParameters, getConsoleLogger(), scanResult ); return forkStarter.run( effectiveProperties, scanResult ); } @@ -1199,6 +1216,16 @@ public abstract class AbstractSurefireMojo return tc; } + private boolean existsModuleDescriptor() + { + return getModuleDescriptor().isFile(); + } + + private File getModuleDescriptor() + { + return new File( getClassesDirectory(), "module-info.class" ); + } + /** * Converts old TestNG configuration parameters over to new properties based configuration * method. (if any are defined the old way) @@ -1511,7 +1538,7 @@ public abstract class AbstractSurefireMojo static boolean isForkModeNever( String forkMode ) { - return ForkConfiguration.FORK_NEVER.equals( forkMode ); + return FORK_NEVER.equals( forkMode ); } protected boolean isForking() @@ -1525,17 +1552,17 @@ 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() { String runOrderString = getRunOrder(); RunOrder[] runOrder = runOrderString == null ? RunOrder.DEFAULT : RunOrder.valueOfMulti( runOrderString ); - return Arrays.asList( runOrder ); + return asList( runOrder ); } private boolean requiresRunHistory() @@ -1649,8 +1676,9 @@ public abstract class AbstractSurefireMojo return new File( getBasedir(), ".surefire-" + configurationHash ); } - StartupConfiguration createStartupConfiguration( ProviderInfo provider, - ClassLoaderConfiguration classLoaderConfiguration ) + private StartupConfiguration createStartupConfiguration( ProviderInfo provider, boolean isInprocess, + ClassLoaderConfiguration classLoaderConfiguration, + DefaultScanResult scanResult ) throws MojoExecutionException, MojoFailureException { try @@ -1669,35 +1697,91 @@ public abstract class AbstractSurefireMojo providerClasspath.addClassPathElementUrl( surefireArtifact.getFile().getAbsolutePath() ) .addClassPathElementUrl( getApiArtifact().getFile().getAbsolutePath() ); - final Classpath testClasspath = generateTestClasspath(); - - getConsoleLogger().debug( testClasspath.getLogMessage( "test" ) ); - getConsoleLogger().debug( providerClasspath.getLogMessage( "provider" ) ); + File moduleDescriptor = getModuleDescriptor(); - getConsoleLogger().debug( testClasspath.getCompactLogMessage( "test(compact)" ) ); - getConsoleLogger().debug( providerClasspath.getCompactLogMessage( "provider(compact)" ) ); - - final ClasspathConfiguration classpathConfiguration = - new ClasspathConfiguration( testClasspath, providerClasspath, inprocClassPath, - effectiveIsEnableAssertions(), isChildDelegation() ); - - return new StartupConfiguration( providerName, classpathConfiguration, classLoaderConfiguration, - isForking(), false ); + if ( moduleDescriptor.exists() && !isInprocess ) + { + return newStartupConfigForModularClasspath( classLoaderConfiguration, providerClasspath, providerName, + moduleDescriptor, scanResult ); + } + else + { + return newStartupConfigForNonModularClasspath( classLoaderConfiguration, providerClasspath, + inprocClassPath, providerName ); + } } - catch ( ArtifactResolutionException e ) + catch ( AbstractArtifactResolutionException e ) { throw new MojoExecutionException( "Unable to generate classpath: " + e, e ); } - catch ( ArtifactNotFoundException e ) + catch ( InvalidVersionSpecificationException e ) { throw new MojoExecutionException( "Unable to generate classpath: " + e, e ); } - catch ( InvalidVersionSpecificationException e ) + catch ( IOException e ) { - throw new MojoExecutionException( "Unable to generate classpath: " + e, e ); + throw new MojoExecutionException( e.getMessage(), e ); } } + private StartupConfiguration newStartupConfigForNonModularClasspath( + ClassLoaderConfiguration classLoaderConfiguration, Classpath providerClasspath, Classpath inprocClasspath, + String providerName ) + throws MojoExecutionException, MojoFailureException, InvalidVersionSpecificationException, + AbstractArtifactResolutionException + { + Classpath testClasspath = generateTestClasspath(); + + getConsoleLogger().debug( testClasspath.getLogMessage( "test classpath:" ) ); + getConsoleLogger().debug( providerClasspath.getLogMessage( "provider classpath:" ) ); + getConsoleLogger().debug( testClasspath.getCompactLogMessage( "test(compact) classpath:" ) ); + getConsoleLogger().debug( providerClasspath.getCompactLogMessage( "provider(compact) classpath:" ) ); + + ClasspathConfiguration classpathConfiguration = new ClasspathConfiguration( testClasspath, providerClasspath, + inprocClasspath, effectiveIsEnableAssertions(), isChildDelegation() ); + + return new StartupConfiguration( providerName, classpathConfiguration, classLoaderConfiguration, isForking(), + false ); + } + + private StartupConfiguration newStartupConfigForModularClasspath( ClassLoaderConfiguration classLoaderConfiguration, + Classpath providerClasspath, String providerName, + File moduleDescriptor, DefaultScanResult scanResult ) + throws MojoExecutionException, MojoFailureException, InvalidVersionSpecificationException, + AbstractArtifactResolutionException, IOException + { + ResolvePathsRequest<String> req = ResolvePathsRequest.withStrings( generateTestClasspath().getClassPath() ) + .setMainModuleDescriptor( moduleDescriptor.getAbsolutePath() ); + + ResolvePathsResult<String> result = locationManager.resolvePaths( req ); + + Classpath testClasspath = new Classpath( result.getClasspathElements() ); + Classpath testModulepath = new Classpath( result.getModulepathElements().keySet() ); + + SortedSet<String> packages = new TreeSet<String>(); + + for ( String className : scanResult.getClasses() ) + { + packages.add( substringBeforeLast( className, "." ) ); + } + + ModularClasspath modularClasspath = new ModularClasspath( moduleDescriptor, testModulepath.getClassPath(), + packages, getTestClassesDirectory() ); + + ModularClasspathConfiguration classpathConfiguration = new ModularClasspathConfiguration( modularClasspath, + testClasspath, providerClasspath, effectiveIsEnableAssertions(), isChildDelegation() ); + + getConsoleLogger().debug( testClasspath.getLogMessage( "test classpath:" ) ); + getConsoleLogger().debug( testModulepath.getLogMessage( "test modulepath:" ) ); + getConsoleLogger().debug( providerClasspath.getLogMessage( "provider classpath:" ) ); + getConsoleLogger().debug( testClasspath.getCompactLogMessage( "test(compact) classpath:" ) ); + getConsoleLogger().debug( testModulepath.getCompactLogMessage( "test(compact) modulepath:" ) ); + getConsoleLogger().debug( providerClasspath.getCompactLogMessage( "provider(compact) classpath:" ) ); + + return new StartupConfiguration( providerName, classpathConfiguration, classLoaderConfiguration, isForking(), + false ); + } + private Artifact getCommonArtifact() { return getPluginArtifactMap().get( "org.apache.maven.surefire:maven-surefire-common" ); @@ -1800,7 +1884,7 @@ public abstract class AbstractSurefireMojo if ( isSpecificTestSpecified() ) { includes = new ArrayList<String>(); - Collections.addAll( includes, split( getTest(), "," ) ); + addAll( includes, split( getTest(), "," ) ); } else { @@ -1822,7 +1906,7 @@ public abstract class AbstractSurefireMojo if ( includes == null || includes.isEmpty() ) { - includes = Arrays.asList( getDefaultIncludes() ); + includes = asList( getDefaultIncludes() ); } } @@ -1941,11 +2025,13 @@ public abstract class AbstractSurefireMojo } private ForkStarter createForkStarter( ProviderInfo provider, ForkConfiguration forkConfiguration, - ClassLoaderConfiguration classLoaderConfiguration, - RunOrderParameters runOrderParameters, ConsoleLogger log ) + ClassLoaderConfiguration classLoaderConfiguration, + RunOrderParameters runOrderParameters, ConsoleLogger log, + DefaultScanResult scanResult ) throws MojoExecutionException, MojoFailureException { - StartupConfiguration startupConfiguration = createStartupConfiguration( provider, classLoaderConfiguration ); + StartupConfiguration startupConfiguration = + createStartupConfiguration( provider, false, classLoaderConfiguration, scanResult ); String configChecksum = getConfigChecksum(); StartupReportConfiguration startupReportConfiguration = getStartupReportConfiguration( configChecksum ); ProviderConfiguration providerConfiguration = createProviderConfiguration( runOrderParameters ); @@ -1954,16 +2040,18 @@ public abstract class AbstractSurefireMojo } private InPluginVMSurefireStarter createInprocessStarter( ProviderInfo provider, - ClassLoaderConfiguration classLoaderConfiguration, - RunOrderParameters runOrderParameters ) + ClassLoaderConfiguration classLoaderConfiguration, + RunOrderParameters runOrderParameters, + DefaultScanResult scanResult ) throws MojoExecutionException, MojoFailureException { - StartupConfiguration startupConfiguration = createStartupConfiguration( provider, classLoaderConfiguration ); + StartupConfiguration startupConfiguration = + createStartupConfiguration( provider, true, classLoaderConfiguration, scanResult ); String configChecksum = getConfigChecksum(); StartupReportConfiguration startupReportConfiguration = getStartupReportConfiguration( configChecksum ); ProviderConfiguration providerConfiguration = createProviderConfiguration( runOrderParameters ); - return new InPluginVMSurefireStarter( startupConfiguration, providerConfiguration, - startupReportConfiguration, consoleLogger ); + return new InPluginVMSurefireStarter( startupConfiguration, providerConfiguration, startupReportConfiguration, + getConsoleLogger() ); } private ForkConfiguration getForkConfiguration() throws MojoFailureException @@ -1973,36 +2061,76 @@ public abstract class AbstractSurefireMojo Artifact shadeFire = getPluginArtifactMap().get( "org.apache.maven.surefire:surefire-shadefire" ); // todo: 150 milli seconds, try to fetch List<String> within classpath asynchronously - 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 ); + Classpath bootClasspath = getArtifactClasspath( shadeFire != null ? shadeFire : surefireBooterArtifact ); + + Platform platform = PLATFORM.withJdkExecAttributesForTests( getEffectiveJvm() ); + + if ( platform.getJdkExecAttributesForTests().isJava9AtLeast() && existsModuleDescriptor() ) + { + return new ModularClasspathForkConfiguration( bootClasspath, + tmpDir, + getEffectiveDebugForkedProcess(), + getWorkingDirectory() != null ? getWorkingDirectory() : getBasedir(), + getProject().getModel().getProperties(), + getArgLine(), + getEnvironmentVariables(), + getConsoleLogger().isDebugEnabled(), + getEffectiveForkCount(), + reuseForks, + platform, + getConsoleLogger() ); + } + else if ( getClassLoaderConfiguration().isManifestOnlyJarRequestedAndUsable() ) + { + return new JarManifestForkConfiguration( bootClasspath, + tmpDir, + getEffectiveDebugForkedProcess(), + getWorkingDirectory() != null ? getWorkingDirectory() : getBasedir(), + getProject().getModel().getProperties(), + getArgLine(), + getEnvironmentVariables(), + getConsoleLogger().isDebugEnabled(), + getEffectiveForkCount(), + reuseForks, + platform, + getConsoleLogger() ); + } + else + { + return new ClasspathForkConfiguration( bootClasspath, + tmpDir, + getEffectiveDebugForkedProcess(), + getWorkingDirectory() != null ? getWorkingDirectory() : getBasedir(), + getProject().getModel().getProperties(), + getArgLine(), + getEnvironmentVariables(), + getConsoleLogger().isDebugEnabled(), + getEffectiveForkCount(), + reuseForks, + platform, + getConsoleLogger() ); + } } 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." ); @@ -2263,14 +2391,14 @@ public abstract class AbstractSurefireMojo if ( getClasspathDependencyScopeExclude() != null && !getClasspathDependencyScopeExclude().isEmpty() ) { ArtifactFilter dependencyFilter = new ScopeArtifactFilter( getClasspathDependencyScopeExclude() ); - classpathArtifacts = this.filterArtifacts( classpathArtifacts, dependencyFilter ); + classpathArtifacts = filterArtifacts( classpathArtifacts, dependencyFilter ); } if ( getClasspathDependencyExcludes() != null ) { - ArtifactFilter dependencyFilter = - new PatternIncludesArtifactFilter( Arrays.asList( getClasspathDependencyExcludes() ) ); - classpathArtifacts = this.filterArtifacts( classpathArtifacts, dependencyFilter ); + List<String> excludedDependencies = asList( getClasspathDependencyExcludes() ); + ArtifactFilter dependencyFilter = new PatternIncludesArtifactFilter( excludedDependencies ); + classpathArtifacts = filterArtifacts( classpathArtifacts, dependencyFilter ); } for ( Artifact artifact : classpathArtifacts ) @@ -2292,7 +2420,7 @@ public abstract class AbstractSurefireMojo { if ( classpathElement != null ) { - Collections.addAll( classpath, split( classpathElement, "," ) ); + addAll( classpath, split( classpathElement, "," ) ); } } } @@ -2337,7 +2465,7 @@ public abstract class AbstractSurefireMojo * @param filter The filter to apply * @return The filtered result */ - private Set<Artifact> filterArtifacts( Set<Artifact> artifacts, ArtifactFilter filter ) + private static Set<Artifact> filterArtifacts( Set<Artifact> artifacts, ArtifactFilter filter ) { Set<Artifact> filteredArtifacts = new LinkedHashSet<Artifact>(); @@ -2486,7 +2614,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" ); } @@ -3355,7 +3483,7 @@ public abstract class AbstractSurefireMojo { if ( getArgLine() != null ) { - List<String> args = Arrays.asList( getArgLine().split( " " ) ); + List<String> args = asList( getArgLine().split( " " ) ); if ( args.contains( "-da" ) || args.contains( "-disableassertions" ) ) { return false; @@ -3510,4 +3638,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/f337881f/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/InPluginVMSurefireStarter.java ---------------------------------------------------------------------- diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/InPluginVMSurefireStarter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/InPluginVMSurefireStarter.java index b97c192..1d85001 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/InPluginVMSurefireStarter.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/InPluginVMSurefireStarter.java @@ -20,6 +20,7 @@ package org.apache.maven.plugin.surefire; */ import org.apache.maven.plugin.surefire.log.api.ConsoleLogger; +import org.apache.maven.surefire.booter.ClasspathConfiguration; import org.apache.maven.surefire.booter.ProviderConfiguration; import org.apache.maven.surefire.booter.StartupConfiguration; import org.apache.maven.surefire.booter.SurefireExecutionException; @@ -73,7 +74,9 @@ public class InPluginVMSurefireStarter scanResult.writeTo( providerProperties ); startupConfig.writeSurefireTestClasspathProperty(); - ClassLoader testClassLoader = startupConfig.getClasspathConfiguration().createMergedClassLoader(); + ClassLoader testClassLoader = startupConfig.getClasspathConfiguration() + .toRealPath( ClasspathConfiguration.class ) + .createMergedClassLoader(); CommonReflector surefireReflector = new CommonReflector( testClassLoader ); http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/f337881f/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/AbstractClasspathForkConfiguration.java ---------------------------------------------------------------------- diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/AbstractClasspathForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/AbstractClasspathForkConfiguration.java new file mode 100644 index 0000000..90d1a1d --- /dev/null +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/AbstractClasspathForkConfiguration.java @@ -0,0 +1,73 @@ +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.log.api.ConsoleLogger; +import org.apache.maven.surefire.booter.Classpath; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.io.File; +import java.util.Map; +import java.util.Properties; + +/** + * @author <a href="mailto:tibordig...@apache.org">Tibor Digana (tibor17)</a> + * @since 2.21.0.Jigsaw + */ +abstract class AbstractClasspathForkConfiguration + extends DefaultForkConfiguration +{ + private static final String ADD_MODULES = "--add-modules"; + private static final String ADD_MODULES_VALUE = "java.se.ee"; + private static final String ALL_JAVA_API = ADD_MODULES + " " + ADD_MODULES_VALUE; + + @SuppressWarnings( "checkstyle:parameternumber" ) + public AbstractClasspathForkConfiguration( @Nonnull Classpath bootClasspath, + @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, + @Nonnull ConsoleLogger log ) + { + super( bootClasspath, tempDirectory, debugLine, workingDirectory, modelProperties, argLine, + environmentVariables, debug, forkCount, reuseForks, pluginPlatform, log ); + } + + @Override + @Nonnull + protected String extendJvmArgLine( @Nonnull String jvmArgLine ) + { + if ( getJdkForTests().isJava9AtLeast() && !jvmArgLine.contains( ADD_MODULES ) ) + { + return jvmArgLine.isEmpty() ? ALL_JAVA_API : ALL_JAVA_API + " " + jvmArgLine; + } + else + { + return jvmArgLine; + } + } +} http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/f337881f/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java ---------------------------------------------------------------------- diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java index 591e89c..97140ef 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java @@ -19,17 +19,12 @@ package org.apache.maven.plugin.surefire.booterclient; * under the License. */ -import java.io.File; -import java.io.IOException; -import java.util.List; - import org.apache.maven.plugin.surefire.SurefireProperties; +import org.apache.maven.surefire.booter.AbstractPathConfiguration; import org.apache.maven.surefire.booter.ClassLoaderConfiguration; -import org.apache.maven.surefire.booter.ClasspathConfiguration; import org.apache.maven.surefire.booter.KeyValueSource; import org.apache.maven.surefire.booter.ProviderConfiguration; import org.apache.maven.surefire.booter.StartupConfiguration; -import org.apache.maven.surefire.booter.SystemPropertyManager; import org.apache.maven.surefire.cli.CommandLineOption; import org.apache.maven.surefire.report.ReporterConfiguration; import org.apache.maven.surefire.testset.DirectoryScannerParameters; @@ -39,8 +34,40 @@ import org.apache.maven.surefire.testset.TestListResolver; import org.apache.maven.surefire.testset.TestRequest; import org.apache.maven.surefire.util.RunOrder; -// CHECKSTYLE_OFF: imports -import static org.apache.maven.surefire.booter.BooterConstants.*; +import java.io.File; +import java.io.IOException; +import java.util.List; + +import static org.apache.maven.surefire.booter.AbstractPathConfiguration.CHILD_DELEGATION; +import static org.apache.maven.surefire.booter.AbstractPathConfiguration.CLASSPATH; +import static org.apache.maven.surefire.booter.AbstractPathConfiguration.ENABLE_ASSERTIONS; +import static org.apache.maven.surefire.booter.AbstractPathConfiguration.SUREFIRE_CLASSPATH; +import static org.apache.maven.surefire.booter.BooterConstants.EXCLUDES_PROPERTY_PREFIX; +import static org.apache.maven.surefire.booter.BooterConstants.FAIL_FAST_COUNT; +import static org.apache.maven.surefire.booter.BooterConstants.FAILIFNOTESTS; +import static org.apache.maven.surefire.booter.BooterConstants.FORKTESTSET; +import static org.apache.maven.surefire.booter.BooterConstants.FORKTESTSET_PREFER_TESTS_FROM_IN_STREAM; +import static org.apache.maven.surefire.booter.BooterConstants.INCLUDES_PROPERTY_PREFIX; +import static org.apache.maven.surefire.booter.BooterConstants.ISTRIMSTACKTRACE; +import static org.apache.maven.surefire.booter.BooterConstants.MAIN_CLI_OPTIONS; +import static org.apache.maven.surefire.booter.BooterConstants.PLUGIN_PID; +import static org.apache.maven.surefire.booter.BooterConstants.PROVIDER_CONFIGURATION; +import static org.apache.maven.surefire.booter.BooterConstants.REPORTSDIRECTORY; +import static org.apache.maven.surefire.booter.BooterConstants.REQUESTEDTEST; +import static org.apache.maven.surefire.booter.BooterConstants.RERUN_FAILING_TESTS_COUNT; +import static org.apache.maven.surefire.booter.BooterConstants.RUN_ORDER; +import static org.apache.maven.surefire.booter.BooterConstants.RUN_STATISTICS_FILE; +import static org.apache.maven.surefire.booter.BooterConstants.SHUTDOWN; +import static org.apache.maven.surefire.booter.BooterConstants.SOURCE_DIRECTORY; +import static org.apache.maven.surefire.booter.BooterConstants.SPECIFIC_TEST_PROPERTY_PREFIX; +import static org.apache.maven.surefire.booter.BooterConstants.SYSTEM_EXIT_TIMEOUT; +import static org.apache.maven.surefire.booter.BooterConstants.TEST_CLASSES_DIRECTORY; +import static org.apache.maven.surefire.booter.BooterConstants.TEST_SUITE_XML_FILES; +import static org.apache.maven.surefire.booter.BooterConstants.TESTARTIFACT_CLASSIFIER; +import static org.apache.maven.surefire.booter.BooterConstants.TESTARTIFACT_VERSION; +import static org.apache.maven.surefire.booter.BooterConstants.USEMANIFESTONLYJAR; +import static org.apache.maven.surefire.booter.BooterConstants.USESYSTEMCLASSLOADER; +import static org.apache.maven.surefire.booter.SystemPropertyManager.writePropertiesFile; /** * Knows how to serialize and deserialize the booter configuration. @@ -78,11 +105,11 @@ class BooterSerializer properties.setProperty( PLUGIN_PID, pid ); - ClasspathConfiguration cp = providerConfiguration.getClasspathConfiguration(); - properties.setClasspath( ClasspathConfiguration.CLASSPATH, cp.getTestClasspath() ); - properties.setClasspath( ClasspathConfiguration.SUREFIRE_CLASSPATH, cp.getProviderClasspath() ); - properties.setProperty( ClasspathConfiguration.ENABLE_ASSERTIONS, String.valueOf( cp.isEnableAssertions() ) ); - properties.setProperty( ClasspathConfiguration.CHILD_DELEGATION, String.valueOf( cp.isChildDelegation() ) ); + AbstractPathConfiguration cp = providerConfiguration.getClasspathConfiguration(); + properties.setClasspath( CLASSPATH, cp.getTestClasspath() ); + properties.setClasspath( SUREFIRE_CLASSPATH, cp.getProviderClasspath() ); + properties.setProperty( ENABLE_ASSERTIONS, toString( cp.isEnableAssertions() ) ); + properties.setProperty( CHILD_DELEGATION, toString( cp.isChildDelegation() ) ); TestArtifactInfo testNg = booterConfiguration.getTestArtifact(); if ( testNg != null ) @@ -101,18 +128,17 @@ class BooterSerializer properties.addList( testSuiteDefinition.getSuiteXmlFiles(), TEST_SUITE_XML_FILES ); TestListResolver testFilter = testSuiteDefinition.getTestListResolver(); properties.setProperty( REQUESTEDTEST, testFilter == null ? "" : testFilter.getPluginParameterTest() ); - properties.setNullableProperty( RERUN_FAILING_TESTS_COUNT, - String.valueOf( testSuiteDefinition.getRerunFailingTestsCount() ) ); + int rerunFailingTestsCount = testSuiteDefinition.getRerunFailingTestsCount(); + properties.setNullableProperty( RERUN_FAILING_TESTS_COUNT, toString( rerunFailingTestsCount ) ); } DirectoryScannerParameters directoryScannerParameters = booterConfiguration.getDirScannerParams(); if ( directoryScannerParameters != null ) { - properties.setProperty( FAILIFNOTESTS, String.valueOf( directoryScannerParameters.isFailIfNoTests() ) ); + properties.setProperty( FAILIFNOTESTS, toString( directoryScannerParameters.isFailIfNoTests() ) ); properties.addList( directoryScannerParameters.getIncludes(), INCLUDES_PROPERTY_PREFIX ); properties.addList( directoryScannerParameters.getExcludes(), EXCLUDES_PROPERTY_PREFIX ); properties.addList( directoryScannerParameters.getSpecificTests(), SPECIFIC_TEST_PROPERTY_PREFIX ); - properties.setProperty( TEST_CLASSES_DIRECTORY, directoryScannerParameters.getTestClassesDirectory() ); } @@ -124,46 +150,40 @@ class BooterSerializer } ReporterConfiguration reporterConfiguration = booterConfiguration.getReporterConfiguration(); - boolean rep = reporterConfiguration.isTrimStackTrace(); properties.setProperty( ISTRIMSTACKTRACE, rep ); properties.setProperty( REPORTSDIRECTORY, reporterConfiguration.getReportsDirectory() ); ClassLoaderConfiguration classLoaderConfig = providerConfiguration.getClassLoaderConfiguration(); - properties.setProperty( USESYSTEMCLASSLOADER, String.valueOf( classLoaderConfig.isUseSystemClassLoader() ) ); - properties.setProperty( USEMANIFESTONLYJAR, String.valueOf( classLoaderConfig.isUseManifestOnlyJar() ) ); - properties.setProperty( FAILIFNOTESTS, String.valueOf( booterConfiguration.isFailIfNoTests() ) ); + properties.setProperty( USESYSTEMCLASSLOADER, toString( classLoaderConfig.isUseSystemClassLoader() ) ); + properties.setProperty( USEMANIFESTONLYJAR, toString( classLoaderConfig.isUseManifestOnlyJar() ) ); + properties.setProperty( FAILIFNOTESTS, toString( booterConfiguration.isFailIfNoTests() ) ); properties.setProperty( PROVIDER_CONFIGURATION, providerConfiguration.getProviderClassName() ); - properties.setProperty( FAIL_FAST_COUNT, String.valueOf( booterConfiguration.getSkipAfterFailureCount() ) ); + properties.setProperty( FAIL_FAST_COUNT, toString( booterConfiguration.getSkipAfterFailureCount() ) ); properties.setProperty( SHUTDOWN, booterConfiguration.getShutdown().name() ); List<CommandLineOption> mainCliOptions = booterConfiguration.getMainCliOptions(); if ( mainCliOptions != null ) { properties.addList( mainCliOptions, MAIN_CLI_OPTIONS ); } + properties.setNullableProperty( SYSTEM_EXIT_TIMEOUT, toString( booterConfiguration.getSystemExitTimeout() ) ); - properties.setNullableProperty( SYSTEM_EXIT_TIMEOUT, - String.valueOf( booterConfiguration.getSystemExitTimeout() ) ); - - return SystemPropertyManager.writePropertiesFile( properties, forkConfiguration.getTempDirectory(), - "surefire", forkConfiguration.isDebug() ); + File surefireTmpDir = forkConfiguration.getTempDirectory(); + boolean debug = forkConfiguration.isDebug(); + return writePropertiesFile( properties, surefireTmpDir, "surefire", debug ); } - private String getTypeEncoded( Object value ) + private static String getTypeEncoded( Object value ) { if ( value == null ) { return null; } - String valueToUse; - if ( value instanceof Class ) - { - valueToUse = ( (Class<?>) value ).getName(); - } - else - { - valueToUse = value.toString(); - } + String valueToUse = value instanceof Class ? ( (Class<?>) value ).getName() : value.toString(); return value.getClass().getName() + "|" + valueToUse; } + private static String toString( Object o ) + { + return String.valueOf( o ); + } } http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/f337881f/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ClasspathForkConfiguration.java ---------------------------------------------------------------------- diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ClasspathForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ClasspathForkConfiguration.java new file mode 100644 index 0000000..28b966e --- /dev/null +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ClasspathForkConfiguration.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.plugin.surefire.log.api.ConsoleLogger; +import org.apache.maven.surefire.booter.Classpath; +import org.apache.maven.surefire.booter.StartupConfiguration; +import org.apache.maven.surefire.booter.SurefireBooterForkException; + +import java.io.File; +import java.util.Map; +import java.util.Properties; + +import static org.apache.maven.shared.utils.StringUtils.join; + +/** + * @author <a href="mailto:tibordig...@apache.org">Tibor Digana (tibor17)</a> + * @since 2.21.0.Jigsaw + */ +public final class ClasspathForkConfiguration + extends AbstractClasspathForkConfiguration +{ + @SuppressWarnings( "checkstyle:parameternumber" ) + public ClasspathForkConfiguration( Classpath bootClasspath, File tempDirectory, String debugLine, + File workingDirectory, Properties modelProperties, String argLine, + Map<String, String> environmentVariables, boolean debug, int forkCount, + boolean reuseForks, Platform pluginPlatform, ConsoleLogger log ) + { + super( bootClasspath, tempDirectory, debugLine, workingDirectory, modelProperties, argLine, + environmentVariables, debug, forkCount, reuseForks, pluginPlatform, log ); + } + + @Override + protected void resolveClasspath( OutputStreamFlushableCommandline cli, String booterThatHasMainMethod, + StartupConfiguration config ) + throws SurefireBooterForkException + { + cli.addEnvironment( "CLASSPATH", join( toCompleteClasspath( config ).iterator(), File.pathSeparator ) ); + cli.createArg().setValue( booterThatHasMainMethod ); + } +} http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/f337881f/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfiguration.java ---------------------------------------------------------------------- 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 new file mode 100644 index 0000000..8c3a9e6 --- /dev/null +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfiguration.java @@ -0,0 +1,343 @@ +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.plugin.surefire.log.api.ConsoleLogger; +import org.apache.maven.surefire.booter.AbstractPathConfiguration; +import org.apache.maven.surefire.booter.Classpath; +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.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; + +import static org.apache.maven.plugin.surefire.AbstractSurefireMojo.FORK_NUMBER_PLACEHOLDER; +import static org.apache.maven.plugin.surefire.AbstractSurefireMojo.THREAD_NUMBER_PLACEHOLDER; +import static org.apache.maven.plugin.surefire.util.Relocator.relocate; +import static org.apache.maven.surefire.booter.Classpath.join; + +/** + * Basic framework which constructs CLI. + * + * @author <a href="mailto:tibordig...@apache.org">Tibor Digana (tibor17)</a> + * @since 2.21.0.Jigsaw + */ +public abstract class DefaultForkConfiguration + extends ForkConfiguration +{ + @Nonnull private final Classpath booterClasspath; + @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; + @Nonnull private final ConsoleLogger log; + + @SuppressWarnings( "checkstyle:parameternumber" ) + protected DefaultForkConfiguration( @Nonnull Classpath booterClasspath, + @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, + @Nonnull ConsoleLogger log ) + { + this.booterClasspath = booterClasspath; + 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; + this.log = log; + } + + protected abstract void resolveClasspath( OutputStreamFlushableCommandline cli, String booterThatHasMainMethod, + StartupConfiguration config ) + throws SurefireBooterForkException; + + @Nonnull + protected String extendJvmArgLine( @Nonnull String jvmArgLine ) + { + return jvmArgLine; + } + + /** + * @param config The startup configuration + * @param forkNumber index of forked JVM, 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 + */ + @Nonnull + @Override + public OutputStreamFlushableCommandline createCommandLine( @Nonnull StartupConfiguration config, int forkNumber ) + throws SurefireBooterForkException + { + OutputStreamFlushableCommandline cli = new OutputStreamFlushableCommandline(); + + cli.setWorkingDirectory( getWorkingDirectory( forkNumber ).getAbsolutePath() ); + + for ( Entry<String, String> entry : getEnvironmentVariables().entrySet() ) + { + String value = entry.getValue(); + cli.addEnvironment( entry.getKey(), value == null ? "" : value ); + } + + cli.setExecutable( getJdkForTests().getJvmExecutable() ); + + String jvmArgLine = newJvmArgLine( forkNumber ); + if ( !jvmArgLine.isEmpty() ) + { + cli.createArg() + .setLine( jvmArgLine ); + } + + if ( getDebugLine() != null && !getDebugLine().isEmpty() ) + { + cli.createArg() + .setLine( getDebugLine() ); + } + + resolveClasspath( cli, findStartClass( config ), config ); + + return cli; + } + + protected List<String> toCompleteClasspath( StartupConfiguration conf ) throws SurefireBooterForkException + { + AbstractPathConfiguration pathConfig = conf.getClasspathConfiguration(); + if ( pathConfig.isClassPathConfig() == pathConfig.isModularPathConfig() ) + { + throw new SurefireBooterForkException( "Could not find class-path config nor modular class-path either." ); + } + + //todo this could probably be simplified further + Classpath bootClasspath = conf.isProviderMainClass() ? pathConfig.getProviderClasspath() : getBooterClasspath(); + Classpath testClasspath = pathConfig.getTestClasspath(); + Classpath providerClasspath = pathConfig.getProviderClasspath(); + + Classpath completeClasspath = join( join( bootClasspath, testClasspath ), providerClasspath ); + + log.debug( completeClasspath.getLogMessage( "boot classpath:" ) ); + log.debug( completeClasspath.getCompactLogMessage( "boot(compact) classpath:" ) ); + + return completeClasspath.getClassPath(); + } + + @Nonnull + private File getWorkingDirectory( int forkNumber ) + throws SurefireBooterForkException + { + File cwd = new File( replaceThreadNumberPlaceholder( getWorkingDirectory().getAbsolutePath(), forkNumber ) ); + + if ( !cwd.exists() && !cwd.mkdirs() ) + { + throw new SurefireBooterForkException( "Cannot create workingDirectory " + cwd.getAbsolutePath() ); + } + + if ( !cwd.isDirectory() ) + { + throw new SurefireBooterForkException( + "WorkingDirectory " + cwd.getAbsolutePath() + " exists and is not a directory" ); + } + return cwd; + } + + @Nonnull + private static String replaceThreadNumberPlaceholder( @Nonnull String argLine, int threadNumber ) + { + String threadNumberAsString = String.valueOf( threadNumber ); + return argLine.replace( THREAD_NUMBER_PLACEHOLDER, threadNumberAsString ) + .replace( FORK_NUMBER_PLACEHOLDER, threadNumberAsString ); + } + + /** + * Replaces expressions <pre>@{property-name}</pre> with the corresponding properties + * from the model. This allows late evaluation of property values when the plugin is executed (as compared + * to evaluation when the pom is parsed as is done with <pre>${property-name}</pre> expressions). + * + * This allows other plugins to modify or set properties with the changes getting picked up by surefire. + */ + @Nonnull + private String interpolateArgLineWithPropertyExpressions() + { + if ( getArgLine() == null ) + { + return ""; + } + + String resolvedArgLine = getArgLine().trim(); + + if ( resolvedArgLine.isEmpty() ) + { + return ""; + } + + for ( final String key : getModelProperties().stringPropertyNames() ) + { + String field = "@{" + key + "}"; + if ( getArgLine().contains( field ) ) + { + resolvedArgLine = resolvedArgLine.replace( field, getModelProperties().getProperty( key, "" ) ); + } + } + + return resolvedArgLine; + } + + @Nonnull + private static String stripNewLines( @Nonnull String argLine ) + { + return argLine.replace( "\n", " " ).replace( "\r", " " ); + } + + /** + * Immutable map. + * + * @param map immutable map copies elements from <code>map</code> + * @param <K> key type + * @param <V> value type + * @return never returns null + */ + @Nonnull + private static <K, V> Map<K, V> toImmutable( @Nullable Map<K, V> map ) + { + return map == null ? Collections.<K, V>emptyMap() : new ImmutableMap<K, V>( map ); + } + + @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 getBooterClasspath() + { + return booterClasspath; + } + + @Nonnull + private String newJvmArgLine( int forks ) + { + String interpolatedArgs = stripNewLines( interpolateArgLineWithPropertyExpressions() ); + String argsWithReplacedForkNumbers = replaceThreadNumberPlaceholder( interpolatedArgs, forks ); + return extendJvmArgLine( argsWithReplacedForkNumbers ); + } + + @Nonnull + private static String findStartClass( StartupConfiguration config ) + { + return config.isShadefire() ? relocate( DEFAULT_PROVIDER_CLASS ) : DEFAULT_PROVIDER_CLASS; + } +} http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/f337881f/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..1ec0fc3 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,371 +19,48 @@ 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; /** * Configuration for forking tests. - * - * @author <a href="mailto:br...@apache.org">Brett Porter</a> - * @author <a href="mailto:ken...@apache.org">Kenney Westerhof</a> - * @author <a href="mailto:krosenv...@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" ); - } - } + public static final String DEFAULT_PROVIDER_CLASS = ForkedBooter.class.getName(); + + @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 getBooterClasspath(); /** - * @param classPath cli the classpath arguments * @param config The startup configuration - * @param threadNumber the thread number, to be the replacement in the argLine @return A commandline + * @param forkNumber index of forked JVM, 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(); - - 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 ) - 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() ); - } - - 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( - "WorkingDirectory " + cwd.getAbsolutePath() + " exists and is not a directory" ); - } - return cwd; - } - - private String replaceThreadNumberPlaceholder( String argLine, int threadNumber ) - { - return argLine.replace( AbstractSurefireMojo.THREAD_NUMBER_PLACEHOLDER, - String.valueOf( threadNumber ) ).replace( AbstractSurefireMojo.FORK_NUMBER_PLACEHOLDER, - String.valueOf( threadNumber ) ); - } - - /** - * Replaces expressions <pre>@{property-name}</pre> with the corresponding properties - * from the model. This allows late evaluation of property values when the plugin is executed (as compared - * to evaluation when the pom is parsed as is done with <pre>${property-name}</pre> expressions). - * - * This allows other plugins to modify or set properties with the changes getting picked up by surefire. - */ - private String replacePropertyExpressions() - { - if ( argLine == null ) - { - return ""; - } - - String resolvedArgLine = argLine.trim(); - - if ( resolvedArgLine.isEmpty() ) - { - return ""; - } - - for ( final String key : modelProperties.stringPropertyNames() ) - { - String field = "@{" + key + "}"; - if ( argLine.contains( field ) ) - { - resolvedArgLine = resolvedArgLine.replace( field, modelProperties.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<String> 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 ) - { - return argLine.replace( "\n", " " ).replace( "\r", " " ); - } - - /** - * Immutable map. - * - * @param map immutable map copies elements from <code>map</code> - * @param <K> key type - * @param <V> value type - * @return never returns null - */ - private static <K, V> Map<K, V> toImmutable( Map<K, V> map ) - { - return map == null ? Collections.<K, V>emptyMap() : new ImmutableMap<K, V>( map ); - } + @Nonnull + public abstract OutputStreamFlushableCommandline createCommandLine( @Nonnull StartupConfiguration config, + int forkNumber ) + throws SurefireBooterForkException; } http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/f337881f/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 31237b2..a7c0311 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 @@ -35,8 +35,7 @@ import org.apache.maven.plugin.surefire.log.api.ConsoleLogger; import org.apache.maven.plugin.surefire.report.DefaultReporterFactory; import org.apache.maven.shared.utils.cli.CommandLineCallable; import org.apache.maven.shared.utils.cli.CommandLineException; -import org.apache.maven.surefire.booter.Classpath; -import org.apache.maven.surefire.booter.ClasspathConfiguration; +import org.apache.maven.surefire.booter.AbstractPathConfiguration; import org.apache.maven.surefire.booter.KeyValueSource; import org.apache.maven.surefire.booter.PropertiesWrapper; import org.apache.maven.surefire.booter.ProviderConfiguration; @@ -88,7 +87,6 @@ import static org.apache.maven.plugin.surefire.booterclient.lazytestprovider.Tes import static org.apache.maven.shared.utils.cli.CommandLineUtils.executeCommandLineAsCallable; import static org.apache.maven.shared.utils.cli.ShutdownHookUtils.addShutDownHook; import static org.apache.maven.shared.utils.cli.ShutdownHookUtils.removeShutdownHook; -import static org.apache.maven.surefire.booter.Classpath.join; import static org.apache.maven.surefire.booter.SystemPropertyManager.writePropertiesFile; import static org.apache.maven.surefire.suite.RunResult.SUCCESS; import static org.apache.maven.surefire.suite.RunResult.failure; @@ -552,13 +550,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 ) { @@ -579,20 +575,9 @@ public class ForkStarter throw new SurefireBooterForkException( "Error creating properties files for forking", e ); } - // this could probably be simplified further - final Classpath bootClasspathConfiguration = startupConfiguration.isProviderMainClass() - ? startupConfiguration.getClasspathConfiguration().getProviderClasspath() - : forkConfiguration.getBootClasspath(); - - Classpath bootClasspath = join( - join( bootClasspathConfiguration, startupConfiguration.getClasspathConfiguration().getTestClasspath() ), - startupConfiguration.getClasspathConfiguration().getProviderClasspath() ); - log.debug( bootClasspath.getLogMessage( "boot" ) ); - log.debug( bootClasspath.getCompactLogMessage( "boot(compact)" ) ); - OutputStreamFlushableCommandline cli = - forkConfiguration.createCommandLine( bootClasspath.getClassPath(), startupConfiguration, forkNumber ); + OutputStreamFlushableCommandline cli = forkConfiguration.createCommandLine( startupConfiguration, forkNumber ); if ( testProvidingInputStream != null ) { @@ -701,7 +686,7 @@ public class ForkStarter { try { - final ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration(); + AbstractPathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration(); ClassLoader unifiedClassLoader = classpathConfiguration.createMergedClassLoader(); CommonReflector commonReflector = new CommonReflector( unifiedClassLoader ); http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/f337881f/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..0c19cdf --- /dev/null +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfiguration.java @@ -0,0 +1,139 @@ +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.plugin.surefire.log.api.ConsoleLogger; +import org.apache.maven.surefire.booter.Classpath; +import org.apache.maven.surefire.booter.StartupConfiguration; +import org.apache.maven.surefire.booter.SurefireBooterForkException; + +import javax.annotation.Nonnull; +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; + +import static org.apache.maven.plugin.surefire.SurefireHelper.escapeToPlatformPath; + +/** + * @author <a href="mailto:tibordig...@apache.org">Tibor Digana (tibor17)</a> + * @since 2.21.0.Jigsaw + */ +public final class JarManifestForkConfiguration + extends AbstractClasspathForkConfiguration +{ + @SuppressWarnings( "checkstyle:parameternumber" ) + public JarManifestForkConfiguration( Classpath bootClasspath, File tempDirectory, String debugLine, + File workingDirectory, Properties modelProperties, String argLine, + Map<String, String> environmentVariables, boolean debug, int forkCount, + boolean reuseForks, Platform pluginPlatform, ConsoleLogger log ) + { + super( bootClasspath, tempDirectory, debugLine, workingDirectory, modelProperties, argLine, + environmentVariables, debug, forkCount, reuseForks, pluginPlatform, log ); + } + + @Override + protected void resolveClasspath( OutputStreamFlushableCommandline cli, String booterThatHasMainMethod, + StartupConfiguration config ) + throws SurefireBooterForkException + { + try + { + File jar = createJar( toCompleteClasspath( config ), booterThatHasMainMethod ); + cli.createArg().setValue( "-jar" ); + cli.createArg().setValue( escapeToPlatformPath( jar.getAbsolutePath() ) ); + } + catch ( IOException e ) + { + throw new SurefireBooterForkException( "Error creating archive file", e ); + } + } + + /** + * 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<String> of all classpath elements. + * @param startClassName The class name to start (main-class) + * @return file of the jar + * @throws IOException When a file operation fails. + */ + @Nonnull + private File createJar( @Nonnull List<String> classPath, @Nonnull 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(); + } + } +}