SUREFIRE-1302_4
Project: http://git-wip-us.apache.org/repos/asf/maven-surefire/repo Commit: http://git-wip-us.apache.org/repos/asf/maven-surefire/commit/9354c310 Tree: http://git-wip-us.apache.org/repos/asf/maven-surefire/tree/9354c310 Diff: http://git-wip-us.apache.org/repos/asf/maven-surefire/diff/9354c310 Branch: refs/heads/SUREFIRE-1302_4 Commit: 9354c310085a6998f452ff69b04d34c8ff4efd86 Parents: 4de017b Author: Tibor17 <[email protected]> Authored: Wed Jul 19 01:21:41 2017 +0200 Committer: Tibor17 <[email protected]> Committed: Sun Jul 23 21:59:32 2017 +0200 ---------------------------------------------------------------------- maven-failsafe-plugin/pom.xml | 17 - maven-surefire-common/pom.xml | 13 +- .../plugin/surefire/AbstractSurefireMojo.java | 14 +- .../plugin/surefire/SurefireProperties.java | 8 + .../surefire/booterclient/BooterSerializer.java | 6 +- .../booterclient/ForkConfiguration.java | 10 +- .../surefire/booterclient/ForkStarter.java | 6 +- .../plugin/surefire/booterclient/Platform.java | 70 +++ .../surefire/report/FileReporterUtils.java | 11 +- ...erDeserializerProviderConfigurationTest.java | 3 +- ...terDeserializerStartupConfigurationTest.java | 3 +- .../booterclient/ForkConfigurationTest.java | 2 +- maven-surefire-plugin/pom.xml | 17 - .../src/site/apt/examples/shutdown.apt.vm | 26 +- maven-surefire-report-plugin/pom.xml | 4 - pom.xml | 17 +- surefire-api/pom.xml | 10 +- .../maven/surefire/booter/CommandReader.java | 4 +- .../maven/surefire/util/ReflectionUtils.java | 49 ++- .../surefire/util/internal/SystemUtils.java | 99 ----- .../java/org/apache/maven/JUnit4SuiteTest.java | 6 +- .../surefire/util/ReflectionUtilsTest.java | 110 +++++ .../surefire/util/internal/SystemUtilsTest.java | 98 ----- surefire-booter/pom.xml | 25 +- .../maven/surefire/booter/BooterConstants.java | 1 + .../surefire/booter/BooterDeserializer.java | 8 + .../apache/maven/surefire/booter/Classpath.java | 2 - .../maven/surefire/booter/ForkedBooter.java | 426 ++++++++++--------- .../maven/surefire/booter/PpidChecker.java | 295 +++++++++++++ .../maven/surefire/booter/ProcessInfo.java | 109 +++++ .../surefire/booter/PropertiesWrapper.java | 6 + .../maven/surefire/booter/SystemUtils.java | 180 ++++++++ .../maven/surefire/booter/JUnit4SuiteTest.java | 4 +- .../maven/surefire/booter/PpidCheckerTest.java | 144 +++++++ .../maven/surefire/booter/SystemUtilsTest.java | 131 ++++++ ...urefire1295AttributeJvmCrashesToTestsIT.java | 6 +- 36 files changed, 1458 insertions(+), 482 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/maven-failsafe-plugin/pom.xml ---------------------------------------------------------------------- diff --git a/maven-failsafe-plugin/pom.xml b/maven-failsafe-plugin/pom.xml index f42e682..ec48929 100644 --- a/maven-failsafe-plugin/pom.xml +++ b/maven-failsafe-plugin/pom.xml @@ -45,27 +45,10 @@ <dependencies> <dependency> - <groupId>org.apache.maven</groupId> - <artifactId>maven-plugin-api</artifactId> - </dependency> - <dependency> <groupId>org.apache.maven.surefire</groupId> <artifactId>maven-surefire-common</artifactId> </dependency> <dependency> - <groupId>org.apache.maven.surefire</groupId> - <artifactId>surefire-api</artifactId> - </dependency> - <dependency> - <groupId>org.apache.maven.shared</groupId> - <artifactId>maven-shared-utils</artifactId> - </dependency> - <dependency> - <groupId>org.apache.maven.plugin-tools</groupId> - <artifactId>maven-plugin-annotations</artifactId> - <scope>compile</scope> - </dependency> - <dependency> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>${project.version}</version> http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/maven-surefire-common/pom.xml ---------------------------------------------------------------------- diff --git a/maven-surefire-common/pom.xml b/maven-surefire-common/pom.xml index 4064bc2..e81a8fd 100644 --- a/maven-surefire-common/pom.xml +++ b/maven-surefire-common/pom.xml @@ -36,6 +36,11 @@ <maven>2.2.1</maven> </prerequisites> + <properties> + <!-- Override with Jigsaw JDK 9 --> + <test.jdk>${java.home}/../bin/java</test.jdk> + </properties> + <dependencies> <dependency> <groupId>org.apache.maven</groupId> @@ -44,7 +49,6 @@ <dependency> <groupId>org.apache.maven.plugin-tools</groupId> <artifactId>maven-plugin-annotations</artifactId> - <scope>compile</scope> </dependency> <dependency> <groupId>org.apache.maven.surefire</groupId> @@ -93,6 +97,7 @@ <dependency> <groupId>com.google.code.findbugs</groupId> <artifactId>jsr305</artifactId> + <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.maven.shared</groupId> @@ -160,6 +165,7 @@ <plugin> <artifactId>maven-surefire-plugin</artifactId> <configuration> + <jvm>${test.jdk}</jvm> <redirectTestOutputToFile>true</redirectTestOutputToFile> <includes> <include>**/JUnit4SuiteTest.java</include> @@ -192,6 +198,7 @@ <include>org.apache.maven.shared:maven-shared-utils</include> <include>org.apache.maven.shared:maven-common-artifact-filters</include> <include>commons-io:commons-io</include> + <include>org.apache.commons:commons-lang3</include> </includes> </artifactSet> <relocations> @@ -203,6 +210,10 @@ <pattern>org.apache.commons.io</pattern> <shadedPattern>org.apache.maven.surefire.shade.org.apache.commons.io</shadedPattern> </relocation> + <relocation> + <pattern>org.apache.commons.lang3</pattern> + <shadedPattern>org.apache.maven.surefire.shade.org.apache.commons.lang3</shadedPattern> + </relocation> </relocations> </configuration> </execution> http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/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 551d980..b3df567 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java @@ -43,6 +43,7 @@ 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.Platform; import org.apache.maven.plugin.surefire.booterclient.ProviderDetector; import org.apache.maven.plugin.surefire.log.PluginConsoleLogger; import org.apache.maven.plugin.surefire.log.api.ConsoleLogger; @@ -114,6 +115,8 @@ public abstract class AbstractSurefireMojo extends AbstractMojo implements SurefireExecutionParameters { + private static final Platform PLATFORM = new Platform(); + private final ProviderDetector providerDetector = new ProviderDetector(); /** @@ -1623,13 +1626,14 @@ public abstract class AbstractSurefireMojo Classpath providerClasspath = ClasspathCache.getCachedClassPath( providerName ); if ( providerClasspath == null ) { + // todo: 100 milli seconds, try to fetch List<String> within classpath asynchronously providerClasspath = provider.getProviderClasspath(); ClasspathCache.setCachedClasspath( providerName, providerClasspath ); } Artifact surefireArtifact = getCommonArtifact(); - Classpath inprocClassPath = providerClasspath. - addClassPathElementUrl( surefireArtifact.getFile().getAbsolutePath() ) - .addClassPathElementUrl( getApiArtifact().getFile().getAbsolutePath() ); + Classpath inprocClassPath = + providerClasspath.addClassPathElementUrl( surefireArtifact.getFile().getAbsolutePath() ) + .addClassPathElementUrl( getApiArtifact().getFile().getAbsolutePath() ); final Classpath testClasspath = generateTestClasspath(); @@ -1926,7 +1930,6 @@ public abstract class AbstractSurefireMojo ProviderConfiguration providerConfiguration = createProviderConfiguration( runOrderParameters ); return new InPluginVMSurefireStarter( startupConfiguration, providerConfiguration, startupReportConfiguration, consoleLogger ); - } protected ForkConfiguration getForkConfiguration() @@ -1937,6 +1940,7 @@ 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 ); @@ -1945,7 +1949,7 @@ public abstract class AbstractSurefireMojo getWorkingDirectory() != null ? getWorkingDirectory() : getBasedir(), getProject().getModel().getProperties(), getArgLine(), getEnvironmentVariables(), getConsoleLogger().isDebugEnabled(), - getEffectiveForkCount(), reuseForks ); + getEffectiveForkCount(), reuseForks, PLATFORM ); } private void convertDeprecatedForkMode() http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireProperties.java ---------------------------------------------------------------------- diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireProperties.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireProperties.java index 4b13898..3783376 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireProperties.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireProperties.java @@ -196,6 +196,14 @@ public class SurefireProperties } } + public void setProperty( String key, Long value ) + { + if ( value != null ) + { + setProperty( key, value.toString() ); + } + } + public void addList( List<?> items, String propertyPrefix ) { if ( items != null && !items.isEmpty() ) http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/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 0299525..591e89c 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 @@ -70,12 +70,14 @@ class BooterSerializer * Does not modify sourceProperties */ File serialize( KeyValueSource sourceProperties, ProviderConfiguration booterConfiguration, - StartupConfiguration providerConfiguration, Object testSet, boolean readTestsFromInStream ) + StartupConfiguration providerConfiguration, Object testSet, boolean readTestsFromInStream, + Long pid ) throws IOException { - SurefireProperties properties = new SurefireProperties( sourceProperties ); + properties.setProperty( PLUGIN_PID, pid ); + ClasspathConfiguration cp = providerConfiguration.getClasspathConfiguration(); properties.setClasspath( ClasspathConfiguration.CLASSPATH, cp.getTestClasspath() ); properties.setClasspath( ClasspathConfiguration.SUREFIRE_CLASSPATH, cp.getProviderClasspath() ); http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/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 2768210..1c7626e 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 @@ -82,11 +82,13 @@ public class ForkConfiguration private final String debugLine; + private final Platform pluginPlatform; + @SuppressWarnings( "checkstyle:parameternumber" ) public ForkConfiguration( Classpath bootClasspathConfiguration, File tmpDir, String debugLine, String jvmExecutable, File workingDirectory, Properties modelProperties, String argLine, Map<String, String> environmentVariables, boolean debugEnabled, int forkCount, - boolean reuseForks ) + boolean reuseForks, Platform pluginPlatform ) { this.bootClasspathConfiguration = bootClasspathConfiguration; this.tempDirectory = tmpDir; @@ -99,6 +101,7 @@ public class ForkConfiguration this.debug = debugEnabled; this.forkCount = forkCount; this.reuseForks = reuseForks; + this.pluginPlatform = pluginPlatform; } public Classpath getBootClasspath() @@ -339,6 +342,11 @@ public class ForkConfiguration return reuseForks; } + public Platform getPluginPlatform() + { + return pluginPlatform; + } + private static String stripNewLines( String argLine ) { return argLine.replace( "\n", " " ).replace( "\r", " " ); http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/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 a2a5095..2d82855 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 @@ -550,7 +550,11 @@ public class ForkStarter BooterSerializer booterSerializer = new BooterSerializer( forkConfiguration ); surefireProperties = booterSerializer.serialize( providerProperties, providerConfiguration, - startupConfiguration, testSet, readTestsFromInStream ); + startupConfiguration, testSet, + readTestsFromInStream, + forkConfiguration.getPluginPlatform().getPid() ); + + log.debug( "Determined Maven Process ID " + forkConfiguration.getPluginPlatform().getPid() ); if ( effectiveSystemProperties != null ) { http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/Platform.java ---------------------------------------------------------------------- diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/Platform.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/Platform.java new file mode 100644 index 0000000..10b16dc --- /dev/null +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/Platform.java @@ -0,0 +1,70 @@ +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.surefire.booter.SystemUtils; + +import java.util.concurrent.Callable; +import java.util.concurrent.FutureTask; +import java.util.concurrent.RunnableFuture; + +import static org.apache.maven.surefire.util.internal.DaemonThreadFactory.newDaemonThread; + +/** + * Loads platform specifics. + * + * @author <a href="mailto:[email protected]">Tibor Digana (tibor17)</a> + * @since 2.20.1 + */ +public final class Platform +{ + private final RunnableFuture<Long> pidJob; + + public Platform() + { + // the job may take 50 or 80 ms + pidJob = new FutureTask<Long>( pidJob() ); + newDaemonThread( pidJob ).start(); + } + + public Long getPid() + { + try + { + return pidJob.get(); + } + catch ( Exception e ) + { + return null; + } + } + + private static Callable<Long> pidJob() + { + return new Callable<Long>() + { + @Override + public Long call() throws Exception + { + return SystemUtils.pid(); + } + }; + } +} http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporterUtils.java ---------------------------------------------------------------------- diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporterUtils.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporterUtils.java index 36bc269..fd33d8e 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporterUtils.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporterUtils.java @@ -19,6 +19,8 @@ package org.apache.maven.plugin.surefire.report; * under the License. */ +import static org.apache.commons.lang3.SystemUtils.IS_OS_WINDOWS; + /** * Utils class for file-based reporters * @@ -45,13 +47,6 @@ public final class FileReporterUtils private static String getOSSpecificIllegalChars() { - if ( System.getProperty( "os.name" ).toLowerCase().startsWith( "win" ) ) - { - return "\\/:*?\"<>|\0"; - } - else - { - return "/\0"; - } + return IS_OS_WINDOWS ? "\\/:*?\"<>|\0" : "/\0"; } } http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java ---------------------------------------------------------------------- diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java index 6759367..5d970d8 100644 --- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java +++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java @@ -220,8 +220,9 @@ public class BooterDeserializerProviderConfigurationTest test = "aTest"; } final File propsTest = booterSerializer.serialize( props, booterConfiguration, testProviderConfiguration, test, - readTestsFromInStream ); + readTestsFromInStream, 51L ); BooterDeserializer booterDeserializer = new BooterDeserializer( new FileInputStream( propsTest ) ); + assertEquals( 51L, (Object) booterDeserializer.getPluginPid() ); return booterDeserializer.deserialize(); } http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java ---------------------------------------------------------------------- diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java index 1ca20d2..0cb292c 100644 --- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java +++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java @@ -124,8 +124,9 @@ public class BooterDeserializerStartupConfigurationTest BooterSerializer booterSerializer = new BooterSerializer( forkConfiguration ); String aTest = "aTest"; final File propsTest = - booterSerializer.serialize( props, getProviderConfiguration(), startupConfiguration, aTest, false ); + booterSerializer.serialize( props, getProviderConfiguration(), startupConfiguration, aTest, false, null ); BooterDeserializer booterDeserializer = new BooterDeserializer( new FileInputStream( propsTest ) ); + assertNull( booterDeserializer.getPluginPid() ); return booterDeserializer.getProviderConfiguration(); } http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java ---------------------------------------------------------------------- diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java index 4f62670..1e09d6f 100644 --- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java +++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java @@ -162,7 +162,7 @@ public class ForkConfigurationTest throws IOException { return new ForkConfiguration( Classpath.emptyClasspath(), null, null, jvm, cwd, new Properties(), argLine, null, - false, 1, false ); + false, 1, false, new Platform() ); } // based on http://stackoverflow.com/questions/2591083/getting-version-of-java-in-runtime http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/maven-surefire-plugin/pom.xml ---------------------------------------------------------------------- diff --git a/maven-surefire-plugin/pom.xml b/maven-surefire-plugin/pom.xml index 62ec4a7..2a186e3 100644 --- a/maven-surefire-plugin/pom.xml +++ b/maven-surefire-plugin/pom.xml @@ -45,26 +45,9 @@ <dependencies> <dependency> - <groupId>org.apache.maven</groupId> - <artifactId>maven-plugin-api</artifactId> - </dependency> - <dependency> <groupId>org.apache.maven.surefire</groupId> <artifactId>maven-surefire-common</artifactId> </dependency> - <dependency> - <groupId>org.apache.maven.surefire</groupId> - <artifactId>surefire-api</artifactId> - </dependency> - <dependency> - <groupId>org.apache.maven</groupId> - <artifactId>maven-toolchain</artifactId> - </dependency> - <dependency> - <groupId>org.apache.maven.plugin-tools</groupId> - <artifactId>maven-plugin-annotations</artifactId> - <scope>compile</scope> - </dependency> </dependencies> <build> http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/maven-surefire-plugin/src/site/apt/examples/shutdown.apt.vm ---------------------------------------------------------------------- diff --git a/maven-surefire-plugin/src/site/apt/examples/shutdown.apt.vm b/maven-surefire-plugin/src/site/apt/examples/shutdown.apt.vm index a546853..7126247 100644 --- a/maven-surefire-plugin/src/site/apt/examples/shutdown.apt.vm +++ b/maven-surefire-plugin/src/site/apt/examples/shutdown.apt.vm @@ -39,8 +39,32 @@ Shutdown of Forked JVM * Pinging forked JVM + << Since ${thisPlugin} Plugin 2.20.1 ping is platform dependent and fallbacks to old mechanism if PID of Maven + process or platform is not recognized, native commands fail in Java. >> + + Simply the mechanism checks the <<< Maven PID >>> is still alive and it is not reused by OS in another application. + If Maven process has died, the forked JVM is killed. + + << Implementation: >> The <<< Maven PID >>> is determined by: + + * resolving the link <<< /proc/self >>> on Linux, or + + * the JMX call <<< ManagementFactory.getRuntimeMXBean().getName() >>>, or + + * Java 9 call <<< ProcessHandle.current().pid() >>>. + + [] + + On Unix like systems the process' uptime is determined by native command <<< (/usr)/bin/ps -o etime= -p [PID] >>>. + + On Windows the start time is determined using <<< wmic process where (ProcessId=[PID]) get CreationDate >>> + in the forked JVM. + + + << Since ${thisPlugin} Plugin 2.19 the old mechanism is significantly slower: >> + The master process sends NOOP command to a forked JVM every 10 seconds. - Forked JVM is waiting for the command every 20 seconds. + Forked JVM is waiting for the command every 30 seconds. If the master process is killed (received SIGKILL signal) or shutdown (pressed CTRL+C, received SIGTERM signal), forked JVM is killed after timing out waiting period. http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/maven-surefire-report-plugin/pom.xml ---------------------------------------------------------------------- diff --git a/maven-surefire-report-plugin/pom.xml b/maven-surefire-report-plugin/pom.xml index a4fe7e2..93a2e80 100644 --- a/maven-surefire-report-plugin/pom.xml +++ b/maven-surefire-report-plugin/pom.xml @@ -48,10 +48,6 @@ <dependencies> <dependency> - <groupId>org.apache.maven.surefire</groupId> - <artifactId>surefire-logger-api</artifactId> - </dependency> - <dependency> <groupId>org.apache.maven</groupId> <artifactId>maven-project</artifactId> </dependency> http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index 962a5bf..397d5d9 100644 --- a/pom.xml +++ b/pom.xml @@ -91,6 +91,9 @@ <mavenVersion>2.2.1</mavenVersion> <!-- <shadedVersion>2.12.4</shadedVersion> commented out due to https://issues.apache.org/jira/browse/MRELEASE-799 --> <mavenPluginPluginVersion>3.3</mavenPluginPluginVersion> + <commonsLang3Version>3.5</commonsLang3Version> + <commonsIoVersion>2.5</commonsIoVersion> + <mavenSharedUtilsVersion>0.9</mavenSharedUtilsVersion> <maven.surefire.scm.devConnection>scm:git:https://git-wip-us.apache.org/repos/asf/maven-surefire.git</maven.surefire.scm.devConnection> <maven.site.path>surefire-archives/surefire-LATEST</maven.site.path> </properties> @@ -105,12 +108,12 @@ <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> - <version>3.1</version> + <version>${commonsLang3Version}</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> - <version>2.2</version> + <version>${commonsIoVersion}</version> </dependency> <dependency> <groupId>org.apache.maven.surefire</groupId> @@ -215,7 +218,13 @@ <dependency> <groupId>org.apache.maven.shared</groupId> <artifactId>maven-shared-utils</artifactId> - <version>0.9</version> + <version>${mavenSharedUtilsVersion}</version> + <exclusions> + <exclusion> + <groupId>com.google.code.findbugs</groupId> + <artifactId>jsr305</artifactId> + </exclusion> + </exclusions> </dependency> <dependency> <groupId>org.apache.maven.shared</groupId> @@ -377,7 +386,7 @@ </plugin> <plugin> <artifactId>maven-shade-plugin</artifactId> - <version>1.5</version> + <version>3.0.0</version> </plugin> <plugin> <artifactId>maven-plugin-plugin</artifactId> http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/surefire-api/pom.xml ---------------------------------------------------------------------- diff --git a/surefire-api/pom.xml b/surefire-api/pom.xml index 7e407d2..96c5be3 100644 --- a/surefire-api/pom.xml +++ b/surefire-api/pom.xml @@ -40,6 +40,11 @@ <groupId>org.apache.maven.shared</groupId> <artifactId>maven-shared-utils</artifactId> </dependency> + <dependency> + <groupId>com.google.code.findbugs</groupId> + <artifactId>jsr305</artifactId> + <scope>provided</scope> + </dependency> </dependencies> <build> @@ -74,7 +79,6 @@ <artifactSet> <includes> <include>org.apache.maven.shared:maven-shared-utils</include> - <include>commons-lang:commons-lang</include> </includes> </artifactSet> <relocations> @@ -82,10 +86,6 @@ <pattern>org.apache.maven.shared</pattern> <shadedPattern>org.apache.maven.surefire.shade.org.apache.maven.shared</shadedPattern> </relocation> - <relocation> - <pattern>org.apache.commons.lang</pattern> - <shadedPattern>org.apache.maven.surefire.shade.org.apache.commons.lang</shadedPattern> - </relocation> </relocations> </configuration> </execution> http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java ---------------------------------------------------------------------- diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java index ed7d4fa..c3d80ea 100644 --- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java +++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java @@ -438,7 +438,7 @@ public final class CommandReader DumpErrorSingleton.getSingleton().dumpStreamException( e, msg ); exitByConfiguration(); - // does not go to finally + // does not go to finally for non-default config: Shutdown.EXIT or Shutdown.KILL } } catch ( IOException e ) @@ -493,7 +493,7 @@ public final class CommandReader { Runtime.getRuntime().halt( 1 ); } - // else is default: should not happen; otherwise you missed enum case + // else is default: other than Shutdown.DEFAULT should not happen; otherwise you missed enum case } } } http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/surefire-api/src/main/java/org/apache/maven/surefire/util/ReflectionUtils.java ---------------------------------------------------------------------- diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/util/ReflectionUtils.java b/surefire-api/src/main/java/org/apache/maven/surefire/util/ReflectionUtils.java index 6844dda..a0dcd66 100644 --- a/surefire-api/src/main/java/org/apache/maven/surefire/util/ReflectionUtils.java +++ b/surefire-api/src/main/java/org/apache/maven/surefire/util/ReflectionUtils.java @@ -28,10 +28,6 @@ import java.lang.reflect.Method; */ public final class ReflectionUtils { - private static final Class[] NO_ARGS = new Class[0]; - - private static final Object[] NO_ARGS_VALUES = new Object[0]; - private ReflectionUtils() { throw new IllegalStateException( "no instantiable constructor" ); @@ -68,8 +64,8 @@ public final class ReflectionUtils public static Object invokeGetter( Object instance, String methodName ) { - final Method method = getMethod( instance, methodName, NO_ARGS ); - return invokeMethodWithArray( instance, method, NO_ARGS_VALUES ); + final Method method = getMethod( instance, methodName ); + return invokeMethodWithArray( instance, method ); } public static Constructor getConstructor( Class<?> clazz, Class<?>... arguments ) @@ -245,4 +241,45 @@ public final class ReflectionUtils throw new SurefireReflectionException( e ); } } + + /** + * Invoker of public static no-argument method. + * + * @param clazz class on which public static no-argument {@code methodName} is invoked + * @param methodName public static no-argument method to be called + * @return value returned by {@code methodName} + * @throws RuntimeException if no such method found + * @throws SurefireReflectionException if the method could not be called or threw an exception + */ + public static Object invokeStaticMethod( Class<?> clazz, String methodName ) + { + Method method = getMethod( clazz, methodName ); + return invokeMethodWithArray( null, method ); + } + + /** + * Method chain invoker. + * + * @param firstStaticClass class to start method chain + * @param noArgMethodNames chain of public methods to call + * @param fallback returned value if a chain could not be invoked due to an error + * @return successfully returned value from the last method call; {@code fallback} otherwise + */ + public static Object invokeMethodChain( Class<?> firstStaticClass, String[] noArgMethodNames, Object fallback ) + { + Object obj = null; + try + { + for ( int i = 0, len = noArgMethodNames.length; i < len; i++ ) + { + String methodName = noArgMethodNames[i]; + obj = i == 0 ? invokeStaticMethod( firstStaticClass, methodName ) : invokeGetter( obj, methodName ); + } + return obj; + } + catch ( RuntimeException e ) + { + return fallback; + } + } } http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/SystemUtils.java ---------------------------------------------------------------------- diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/SystemUtils.java b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/SystemUtils.java deleted file mode 100644 index 7881b5d..0000000 --- a/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/SystemUtils.java +++ /dev/null @@ -1,99 +0,0 @@ -package org.apache.maven.surefire.util.internal; - -/* - * 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.util.ReflectionUtils; - -import java.lang.reflect.Method; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static java.lang.Integer.parseInt; -import static java.lang.Math.pow; - -/** - * JDK 9 support. - * - * @author <a href="mailto:[email protected]">Tibor Digana (tibor17)</a> - * @since 2.20.1 - */ -public final class SystemUtils -{ - private static final Pattern JAVA_SPEC_VERSION_PATTERN = Pattern.compile( "(\\d+)(\\.?)(\\d*).*" ); - private static final double JAVA_SPEC_VERSION = javaSpecVersion(); - - public SystemUtils() - { - throw new IllegalStateException( "no instantiable constructor" ); - } - - public static ClassLoader platformClassLoader() - { - if ( JAVA_SPEC_VERSION < 9 ) - { - return null; - } - - return reflectClassLoader( ClassLoader.class, "getPlatformClassLoader" ); - } - - public static double javaSpecVersion() - { - return extractJavaSpecVersion( System.getProperty( "java.specification.version" ) ); - } - - static ClassLoader reflectClassLoader( Class<?> target, String getterMethodName ) - { - try - { - Method getter = ReflectionUtils.getMethod( target, getterMethodName ); - return (ClassLoader) ReflectionUtils.invokeMethodWithArray( null, getter ); - } - catch ( RuntimeException e ) - { - return null; - } - } - - static double extractJavaSpecVersion( String property ) - { - Matcher versionRegexMatcher = JAVA_SPEC_VERSION_PATTERN.matcher( property ); - int groups = versionRegexMatcher.groupCount(); - if ( !versionRegexMatcher.matches() ) - { - throw new IllegalStateException( "Java Spec Version does not match the pattern " - + JAVA_SPEC_VERSION_PATTERN - ); - } - - if ( groups >= 3 ) - { - String majorVersion = versionRegexMatcher.group( 1 ); - String minorVersion = versionRegexMatcher.group( 3 ); - int major = parseInt( majorVersion ); - double minor = minorVersion.isEmpty() ? 0 : parseInt( minorVersion ) / pow( 10, minorVersion.length() ); - return major + minor; - } - else - { - return parseInt( versionRegexMatcher.group( 0 ) ); - } - } -} http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java ---------------------------------------------------------------------- diff --git a/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java b/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java index dbf46ea..66cca64 100644 --- a/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java +++ b/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java @@ -32,6 +32,7 @@ import org.apache.maven.surefire.testset.FundamentalFilterTest; import org.apache.maven.surefire.testset.ResolvedTestTest; import org.apache.maven.surefire.testset.TestListResolverTest; import org.apache.maven.surefire.util.DefaultDirectoryScannerTest; +import org.apache.maven.surefire.util.ReflectionUtilsTest; import org.apache.maven.surefire.util.RunOrderCalculatorTest; import org.apache.maven.surefire.util.RunOrderTest; import org.apache.maven.surefire.util.ScanResultTest; @@ -40,7 +41,6 @@ import org.apache.maven.surefire.util.UrlUtilsTest; import org.apache.maven.surefire.util.internal.ConcurrencyUtilsTest; import org.apache.maven.surefire.util.internal.ImmutableMapTest; import org.apache.maven.surefire.util.internal.StringUtilsTest; -import org.apache.maven.surefire.util.internal.SystemUtilsTest; import org.junit.runner.RunWith; import org.junit.runners.Suite; @@ -69,8 +69,8 @@ import org.junit.runners.Suite; UrlUtilsTest.class, SpecificTestClassFilterTest.class, FundamentalFilterTest.class, - SystemUtilsTest.class, - ImmutableMapTest.class + ImmutableMapTest.class, + ReflectionUtilsTest.class } ) @RunWith( Suite.class ) public class JUnit4SuiteTest http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/surefire-api/src/test/java/org/apache/maven/surefire/util/ReflectionUtilsTest.java ---------------------------------------------------------------------- diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/util/ReflectionUtilsTest.java b/surefire-api/src/test/java/org/apache/maven/surefire/util/ReflectionUtilsTest.java new file mode 100644 index 0000000..89bc205 --- /dev/null +++ b/surefire-api/src/test/java/org/apache/maven/surefire/util/ReflectionUtilsTest.java @@ -0,0 +1,110 @@ +package org.apache.maven.surefire.util; + +/* + * 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.Test; + +import static org.fest.assertions.Assertions.assertThat; + +/** + * Unit test for {@link ReflectionUtils}. + * + * @author <a href="mailto:[email protected]">Tibor Digana (tibor17)</a> + * @since 2.20.1 + */ +public class ReflectionUtilsTest +{ + @Test(expected = RuntimeException.class) + public void shouldNotInvokeStaticMethod() + { + ReflectionUtils.invokeStaticMethod( ReflectionUtilsTest.class, "notCallable" ); + } + + @Test + public void shouldInvokeStaticMethod() + { + Object o = ReflectionUtils.invokeStaticMethod( ReflectionUtilsTest.class, "callable" ); + assertThat( o ) + .isEqualTo( 3L ); + } + + @Test + public void shouldInvokeMethodChain() + { + String[] chain = { "current", "pid" }; + Object o = ReflectionUtils.invokeMethodChain( A.class, chain, null ); + assertThat( o ) + .isEqualTo( 3L ); + + String[] longChain = { "current", "createB", "pid" }; + o = ReflectionUtils.invokeMethodChain( A.class, longChain, null ); + assertThat( o ) + .isEqualTo( 1L ); + } + + @Test + public void shouldInvokeFallbackOnMethodChain() + { + String[] chain = { "current", "abc" }; + Object o = ReflectionUtils.invokeMethodChain( A.class, chain, 5L ); + assertThat( o ) + .isEqualTo( 5L ); + + String[] longChain = { "current", "createB", "abc" }; + o = ReflectionUtils.invokeMethodChain( A.class, longChain, 6L ); + assertThat( o ) + .isEqualTo( 6L ); + } + + private static void notCallable() + { + } + + public static long callable() + { + return 3L; + } + + public static class A + { + public static A current() + { + return new A(); + } + + public long pid() + { + return 3L; + } + + public B createB() + { + return new B(); + } + } + + public static class B + { + public long pid() + { + return 1L; + } + } +} http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/SystemUtilsTest.java ---------------------------------------------------------------------- diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/SystemUtilsTest.java b/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/SystemUtilsTest.java deleted file mode 100644 index bbd9b3c..0000000 --- a/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/SystemUtilsTest.java +++ /dev/null @@ -1,98 +0,0 @@ -package org.apache.maven.surefire.util.internal; - -/* - * 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.Test; - -import static org.fest.assertions.Assertions.assertThat; - -/** - * Test of {@link SystemUtils}. - * - * @author <a href="mailto:[email protected]">Tibor Digana (tibor17)</a> - * @since 2.20.1 - */ -public class SystemUtilsTest -{ - @Test - public void shouldBeJava9() - { - String simpleVersion = "9"; - double version = SystemUtils.extractJavaSpecVersion( simpleVersion ); - assertThat( version ).isEqualTo( 9d ); - } - - @Test - public void shouldBeJava8() - { - String simpleVersion = "1.8"; - double version = SystemUtils.extractJavaSpecVersion( simpleVersion ); - assertThat( version ).isEqualTo( 1.8d ); - } - - @Test - public void shouldBeJavaMajorAndMinor() - { - String simpleVersion = "12.345.6"; - double version = SystemUtils.extractJavaSpecVersion( simpleVersion ); - assertThat( version ).isEqualTo( 12.345d ); - } - - @Test - public void shouldBeCurrentJavaVersion() - { - Double parsedVersion = SystemUtils.javaSpecVersion(); - String expectedVersion = System.getProperty( "java.specification.version" ); - assertThat( parsedVersion.toString() ).isEqualTo( expectedVersion ); - } - - @Test - public void shouldBePlatformClassLoader() - { - ClassLoader cl = SystemUtils.platformClassLoader(); - if ( SystemUtils.javaSpecVersion() < 9 ) - { - assertThat( cl ).isNull(); - } - else - { - assertThat( cl ).isNotNull(); - } - } - - @Test - public void shouldNotFindClassLoader() - { - ClassLoader cl = SystemUtils.reflectClassLoader( getClass(), "_getPlatformClassLoader_" ); - assertThat( cl ).isNull(); - } - - @Test - public void shouldFindClassLoader() - { - ClassLoader cl = SystemUtils.reflectClassLoader( getClass(), "getPlatformClassLoader" ); - assertThat( cl ).isSameAs( ClassLoader.getSystemClassLoader() ); - } - - public static ClassLoader getPlatformClassLoader() - { - return ClassLoader.getSystemClassLoader(); - } -} http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/surefire-booter/pom.xml ---------------------------------------------------------------------- diff --git a/surefire-booter/pom.xml b/surefire-booter/pom.xml index b79cceb..1dc7591 100644 --- a/surefire-booter/pom.xml +++ b/surefire-booter/pom.xml @@ -35,6 +35,20 @@ <dependency> <groupId>org.apache.maven.surefire</groupId> <artifactId>surefire-api</artifactId> + <exclusions> + <exclusion> + <groupId>org.apache.maven.shared</groupId> + <artifactId>maven-shared-utils</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + </dependency> + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> </dependency> </dependencies> @@ -69,13 +83,18 @@ <minimizeJar>true</minimizeJar> <artifactSet> <includes> - <include>commons-lang:commons-lang</include> + <include>org.apache.commons:commons-lang3</include> + <include>commons-io:commons-io</include> </includes> </artifactSet> <relocations> <relocation> - <pattern>org.apache.commons.lang</pattern> - <shadedPattern>org.apache.maven.surefire.shade.org.apache.commons.lang</shadedPattern> + <pattern>org.apache.commons.lang3</pattern> + <shadedPattern>org.apache.maven.surefire.shade.org.apache.commons.lang3</shadedPattern> + </relocation> + <relocation> + <pattern>org.apache.commons.io</pattern> + <shadedPattern>org.apache.maven.surefire.shade.org.apache.commons.io</shadedPattern> </relocation> </relocations> </configuration> http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java ---------------------------------------------------------------------- diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java index c21edf8..3551910 100644 --- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java +++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java @@ -56,4 +56,5 @@ public final class BooterConstants public static final String FAIL_FAST_COUNT = "failFastCount"; public static final String SHUTDOWN = "shutdown"; public static final String SYSTEM_EXIT_TIMEOUT = "systemExitTimeout"; + public static final String PLUGIN_PID = "pluginPid"; } http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java ---------------------------------------------------------------------- diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java index 8fa760b..75aad1f 100644 --- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java +++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java @@ -58,6 +58,14 @@ public class BooterDeserializer properties = SystemPropertyManager.loadProperties( inputStream ); } + /** + * @return PID of Maven process where plugin is executed; or null if PID could not be determined. + */ + public Long getPluginPid() + { + return properties.getLongProperty( PLUGIN_PID ); + } + public ProviderConfiguration deserialize() { final File reportsDirectory = new File( properties.getProperty( REPORTSDIRECTORY ) ); http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/surefire-booter/src/main/java/org/apache/maven/surefire/booter/Classpath.java ---------------------------------------------------------------------- diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/Classpath.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/Classpath.java index 531e7a1..609be0f 100644 --- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/Classpath.java +++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/Classpath.java @@ -19,8 +19,6 @@ package org.apache.maven.surefire.booter; * under the License. */ -import org.apache.maven.surefire.util.internal.SystemUtils; - import java.io.File; import java.net.MalformedURLException; import java.net.URL; http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/9354c310/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java ---------------------------------------------------------------------- diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java index 1e3863e..d3c6306 100644 --- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java +++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java @@ -22,7 +22,6 @@ package org.apache.maven.surefire.booter; import org.apache.maven.surefire.providerapi.ProviderParameters; import org.apache.maven.surefire.providerapi.SurefireProvider; import org.apache.maven.surefire.report.LegacyPojoStackTraceWriter; -import org.apache.maven.surefire.report.ReporterFactory; import org.apache.maven.surefire.report.StackTraceWriter; import org.apache.maven.surefire.suite.RunResult; import org.apache.maven.surefire.testset.TestSetFailedException; @@ -30,6 +29,7 @@ import org.apache.maven.surefire.testset.TestSetFailedException; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.lang.management.ManagementFactory; @@ -37,7 +37,6 @@ import java.lang.reflect.InvocationTargetException; import java.security.AccessControlException; import java.security.AccessController; import java.security.PrivilegedAction; -import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.Semaphore; @@ -45,14 +44,9 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicBoolean; import static java.lang.Math.max; -import static java.lang.System.err; -import static java.lang.System.out; -import static java.lang.System.setErr; -import static java.lang.System.setOut; import static java.lang.Thread.currentThread; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; -import static org.apache.maven.surefire.booter.CommandReader.getReader; import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_BYE; import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_ERROR; import static org.apache.maven.surefire.booter.ForkingRunListener.encode; @@ -73,107 +67,91 @@ import static org.apache.maven.surefire.util.internal.StringUtils.encodeStringFo */ public final class ForkedBooter { - private static final long DEFAULT_SYSTEM_EXIT_TIMEOUT_IN_SECONDS = 30; - private static final long PING_TIMEOUT_IN_SECONDS = 20; - private static final long ONE_SECOND_IN_MILLIS = 1000; - private static final CommandReader COMMAND_READER = startupMasterProcessReader(); + private static final long DEFAULT_SYSTEM_EXIT_TIMEOUT_IN_SECONDS = 30L; + private static final long PING_TIMEOUT_IN_SECONDS = 30L; + private static final long ONE_SECOND_IN_MILLIS = 1000L; - private static volatile ScheduledThreadPoolExecutor jvmTerminator; - private static volatile long systemExitTimeoutInSeconds = DEFAULT_SYSTEM_EXIT_TIMEOUT_IN_SECONDS; + private final CommandReader commandReader = CommandReader.getReader(); + private final PrintStream originalOut = System.out; - /** - * This method is invoked when Surefire is forked - this method parses and organizes the arguments passed to it and - * then calls the Surefire class' run method. <br> The system exit code will be 1 if an exception is thrown. - * - * @param args Commandline arguments - */ - public static void main( String... args ) - { - final ExecutorService pingScheduler = isDebugging() ? null : listenToShutdownCommands(); - final PrintStream originalOut = out; - try - { - final String tmpDir = args[0]; - final String dumpFileName = args[1]; - final String surefirePropsFileName = args[2]; + private volatile long systemExitTimeoutInSeconds = DEFAULT_SYSTEM_EXIT_TIMEOUT_IN_SECONDS; + private volatile PingScheduler pingScheduler; - BooterDeserializer booterDeserializer = - new BooterDeserializer( createSurefirePropertiesIfFileExists( tmpDir, surefirePropsFileName ) ); - if ( args.length > 3 ) - { - final String effectiveSystemPropertiesFileName = args[3]; - setSystemProperties( new File( tmpDir, effectiveSystemPropertiesFileName ) ); - } + private ScheduledThreadPoolExecutor jvmTerminator; + private ProviderConfiguration providerConfiguration; + private StartupConfiguration startupConfiguration; + private Object testSet; - final ProviderConfiguration providerConfiguration = booterDeserializer.deserialize(); - DumpErrorSingleton.getSingleton().init( dumpFileName, providerConfiguration.getReporterConfiguration() ); + private void setupBooter( String tmpDir, String dumpFileName, String surefirePropsFileName, + String effectiveSystemPropertiesFileName ) + throws IOException, SurefireExecutionException + { + BooterDeserializer booterDeserializer = + new BooterDeserializer( createSurefirePropertiesIfFileExists( tmpDir, surefirePropsFileName ) ); + // todo: print PID in debug console logger in version 2.20.2 + pingScheduler = isDebugging() ? null : listenToShutdownCommands( booterDeserializer.getPluginPid() ); + setSystemProperties( new File( tmpDir, effectiveSystemPropertiesFileName ) ); - final StartupConfiguration startupConfiguration = booterDeserializer.getProviderConfiguration(); - systemExitTimeoutInSeconds = - providerConfiguration.systemExitTimeout( DEFAULT_SYSTEM_EXIT_TIMEOUT_IN_SECONDS ); - final TypeEncodedValue forkedTestSet = providerConfiguration.getTestForFork(); - final boolean readTestsFromInputStream = providerConfiguration.isReadTestsFromInStream(); + providerConfiguration = booterDeserializer.deserialize(); + DumpErrorSingleton.getSingleton().init( dumpFileName, providerConfiguration.getReporterConfiguration() ); - final ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration(); - if ( startupConfiguration.isManifestOnlyJarRequestedAndUsable() ) - { - classpathConfiguration.trickClassPathWhenManifestOnlyClasspath(); - } + startupConfiguration = booterDeserializer.getProviderConfiguration(); + systemExitTimeoutInSeconds = + providerConfiguration.systemExitTimeout( DEFAULT_SYSTEM_EXIT_TIMEOUT_IN_SECONDS ); - final ClassLoader classLoader = currentThread().getContextClassLoader(); - classLoader.setDefaultAssertionStatus( classpathConfiguration.isEnableAssertions() ); - startupConfiguration.writeSurefireTestClasspathProperty(); + ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration(); + if ( startupConfiguration.isManifestOnlyJarRequestedAndUsable() ) + { + classpathConfiguration.trickClassPathWhenManifestOnlyClasspath(); + } - final Object testSet; - if ( forkedTestSet != null ) - { - testSet = forkedTestSet.getDecodedValue( classLoader ); - } - else if ( readTestsFromInputStream ) - { - testSet = new LazyTestsToRun( originalOut ); - } - else - { - testSet = null; - } + ClassLoader classLoader = currentThread().getContextClassLoader(); + classLoader.setDefaultAssertionStatus( classpathConfiguration.isEnableAssertions() ); + startupConfiguration.writeSurefireTestClasspathProperty(); + testSet = createTestSet( providerConfiguration.getTestForFork(), + providerConfiguration.isReadTestsFromInStream(), classLoader ); + } - try - { - runSuitesInProcess( testSet, startupConfiguration, providerConfiguration, originalOut ); - } - catch ( InvocationTargetException t ) - { - DumpErrorSingleton.getSingleton().dumpException( t ); - StackTraceWriter stackTraceWriter = + private void execute() + { + try + { + runSuitesInProcess(); + } + catch ( InvocationTargetException t ) + { + DumpErrorSingleton.getSingleton().dumpException( t ); + StackTraceWriter stackTraceWriter = new LegacyPojoStackTraceWriter( "test subsystem", "no method", t.getTargetException() ); - StringBuilder stringBuilder = new StringBuilder(); - encode( stringBuilder, stackTraceWriter, false ); - encodeAndWriteToOutput( ( (char) BOOTERCODE_ERROR ) + ",0," + stringBuilder + "\n" , originalOut ); - } - catch ( Throwable t ) - { - DumpErrorSingleton.getSingleton().dumpException( t ); - StackTraceWriter stackTraceWriter = new LegacyPojoStackTraceWriter( "test subsystem", "no method", t ); - StringBuilder stringBuilder = new StringBuilder(); - encode( stringBuilder, stackTraceWriter, false ); - encodeAndWriteToOutput( ( (char) BOOTERCODE_ERROR ) + ",0," + stringBuilder + "\n", originalOut ); - } - acknowledgedExit( originalOut, pingScheduler ); + StringBuilder stringBuilder = new StringBuilder(); + encode( stringBuilder, stackTraceWriter, false ); + encodeAndWriteToOutput( ( (char) BOOTERCODE_ERROR ) + ",0," + stringBuilder + "\n" ); } catch ( Throwable t ) { DumpErrorSingleton.getSingleton().dumpException( t ); - // Just throwing does getMessage() and a local trace - we want to call printStackTrace for a full trace - // noinspection UseOfSystemOutOrSystemErr - t.printStackTrace( err ); - cancelPingScheduler( pingScheduler ); - // noinspection ProhibitedExceptionThrown,CallToSystemExit - exit( 1 ); + StackTraceWriter stackTraceWriter = new LegacyPojoStackTraceWriter( "test subsystem", "no method", t ); + StringBuilder stringBuilder = new StringBuilder(); + encode( stringBuilder, stackTraceWriter, false ); + encodeAndWriteToOutput( ( (char) BOOTERCODE_ERROR ) + ",0," + stringBuilder + "\n" ); } + acknowledgedExit(); } - private static void cancelPingScheduler( final ExecutorService pingScheduler ) + private Object createTestSet( TypeEncodedValue forkedTestSet, boolean readTestsFromCommandReader, ClassLoader cl ) + { + if ( forkedTestSet != null ) + { + return forkedTestSet.getDecodedValue( cl ); + } + else if ( readTestsFromCommandReader ) + { + return new LazyTestsToRun( originalOut ); + } + return null; + } + + private void cancelPingScheduler() { if ( pingScheduler != null ) { @@ -197,23 +175,50 @@ public final class ForkedBooter } } - private static CommandReader startupMasterProcessReader() + private PingScheduler listenToShutdownCommands( Long pluginPid ) { - return getReader(); + commandReader.addShutdownListener( createExitHandler() ); + AtomicBoolean pingDone = new AtomicBoolean( true ); + commandReader.addNoopListener( createPingHandler( pingDone ) ); + + PingScheduler pingMechanisms = new PingScheduler( createPingScheduler(), + pluginPid == null ? null : new PpidChecker( pluginPid ) ); + if ( pingMechanisms.pluginProcessChecker != null ) + { + Runnable checkerJob = processCheckerJob( pingMechanisms ); + pingMechanisms.pingScheduler.scheduleWithFixedDelay( checkerJob, 0L, 1L, SECONDS ); + } + Runnable pingJob = createPingJob( pingDone, pingMechanisms.pluginProcessChecker ); + pingMechanisms.pingScheduler.scheduleAtFixedRate( pingJob, 0L, PING_TIMEOUT_IN_SECONDS, SECONDS ); + + return pingMechanisms; } - private static ExecutorService listenToShutdownCommands() + private Runnable processCheckerJob( final PingScheduler pingMechanism ) { - COMMAND_READER.addShutdownListener( createExitHandler() ); - AtomicBoolean pingDone = new AtomicBoolean( true ); - COMMAND_READER.addNoopListener( createPingHandler( pingDone ) ); - Runnable pingJob = createPingJob( pingDone ); - ScheduledExecutorService pingScheduler = createPingScheduler(); - pingScheduler.scheduleAtFixedRate( pingJob, 0, PING_TIMEOUT_IN_SECONDS, SECONDS ); - return pingScheduler; + return new Runnable() + { + @Override + public void run() + { + try + { + if ( pingMechanism.pluginProcessChecker.canUse() + && !pingMechanism.pluginProcessChecker.isProcessAlive() + && !pingMechanism.pingScheduler.isShutdown() ) + { + kill(); + } + } + catch ( Exception e ) + { + // nothing to do + } + } + }; } - private static CommandListener createPingHandler( final AtomicBoolean pingDone ) + private CommandListener createPingHandler( final AtomicBoolean pingDone ) { return new CommandListener() { @@ -225,7 +230,7 @@ public final class ForkedBooter }; } - private static CommandListener createExitHandler() + private CommandListener createExitHandler() { return new CommandListener() { @@ -239,6 +244,7 @@ public final class ForkedBooter } else if ( shutdown.isExit() ) { + cancelPingScheduler(); exit( 1 ); } // else refers to shutdown=testset, but not used now, keeping reader open @@ -246,98 +252,88 @@ public final class ForkedBooter }; } - private static Runnable createPingJob( final AtomicBoolean pingDone ) + private Runnable createPingJob( final AtomicBoolean pingDone, final PpidChecker pluginProcessChecker ) { return new Runnable() { @Override public void run() { - boolean hasPing = pingDone.getAndSet( false ); - if ( !hasPing ) + if ( !canUseNewPingMechanism( pluginProcessChecker ) ) { - kill(); + boolean hasPing = pingDone.getAndSet( false ); + if ( !hasPing ) + { + kill(); + } } } }; } - private static void encodeAndWriteToOutput( String string, PrintStream out ) + private void encodeAndWriteToOutput( String string ) { byte[] encodeBytes = encodeStringForForkCommunication( string ); //noinspection SynchronizationOnLocalVariableOrMethodParameter - synchronized ( out ) + synchronized ( originalOut ) { - out.write( encodeBytes, 0, encodeBytes.length ); - out.flush(); + originalOut.write( encodeBytes, 0, encodeBytes.length ); + originalOut.flush(); } } - private static void kill() + private void kill() + { + kill( 1 ); + } + + private void kill( int returnCode ) { - COMMAND_READER.stop(); - Runtime.getRuntime().halt( 1 ); + commandReader.stop(); + Runtime.getRuntime().halt( returnCode ); } - private static void exit( int returnCode ) + private void exit( int returnCode ) { launchLastDitchDaemonShutdownThread( returnCode ); - COMMAND_READER.stop(); System.exit( returnCode ); } - private static void acknowledgedExit( PrintStream originalOut, ExecutorService pingScheduler ) + private void acknowledgedExit() { final Semaphore barrier = new Semaphore( 0 ); - COMMAND_READER.addByeAckListener( new CommandListener() - { - @Override - public void update( Command command ) - { - barrier.release(); - } - } + commandReader.addByeAckListener( new CommandListener() + { + @Override + public void update( Command command ) + { + barrier.release(); + } + } ); - encodeAndWriteToOutput( ( (char) BOOTERCODE_BYE ) + ",0,BYE!\n", originalOut ); + encodeAndWriteToOutput( ( (char) BOOTERCODE_BYE ) + ",0,BYE!\n" ); launchLastDitchDaemonShutdownThread( 0 ); long timeoutMillis = max( systemExitTimeoutInSeconds * ONE_SECOND_IN_MILLIS, ONE_SECOND_IN_MILLIS ); acquireOnePermit( barrier, timeoutMillis ); - cancelPingScheduler( pingScheduler ); - COMMAND_READER.stop(); + cancelPingScheduler(); + commandReader.stop(); System.exit( 0 ); } - private static boolean acquireOnePermit( Semaphore barrier, long timeoutMillis ) - { - try - { - return barrier.tryAcquire( timeoutMillis, MILLISECONDS ); - } - catch ( InterruptedException e ) - { - return true; - } - } - - private static RunResult runSuitesInProcess( Object testSet, StartupConfiguration startupConfiguration, - ProviderConfiguration providerConfiguration, - PrintStream originalSystemOut ) + private RunResult runSuitesInProcess() throws SurefireExecutionException, TestSetFailedException, InvocationTargetException { - final ReporterFactory factory = createForkingReporterFactory( providerConfiguration, originalSystemOut ); - - return invokeProviderInSameClassLoader( testSet, factory, providerConfiguration, true, startupConfiguration, - false ); + ForkingReporterFactory factory = createForkingReporterFactory(); + return invokeProviderInSameClassLoader( factory ); } - private static ReporterFactory createForkingReporterFactory( ProviderConfiguration providerConfiguration, - PrintStream originalSystemOut ) + private ForkingReporterFactory createForkingReporterFactory() { final boolean trimStackTrace = providerConfiguration.getReporterConfiguration().isTrimStackTrace(); - return new ForkingReporterFactory( trimStackTrace, originalSystemOut ); + return new ForkingReporterFactory( trimStackTrace, originalOut ); } - private static synchronized ScheduledThreadPoolExecutor getJvmTerminator() + private synchronized ScheduledThreadPoolExecutor getJvmTerminator() { if ( jvmTerminator == null ) { @@ -345,71 +341,34 @@ public final class ForkedBooter newDaemonThreadFactory( "last-ditch-daemon-shutdown-thread-" + systemExitTimeoutInSeconds + "s" ); jvmTerminator = new ScheduledThreadPoolExecutor( 1, threadFactory ); jvmTerminator.setMaximumPoolSize( 1 ); - return jvmTerminator; - } - else - { - return jvmTerminator; } - } - - private static ScheduledExecutorService createPingScheduler() - { - ThreadFactory threadFactory = newDaemonThreadFactory( "ping-" + PING_TIMEOUT_IN_SECONDS + "s" ); - ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor( 1, threadFactory ); - executor.setMaximumPoolSize( 1 ); - executor.prestartCoreThread(); - return executor; + return jvmTerminator; } @SuppressWarnings( "checkstyle:emptyblock" ) - private static void launchLastDitchDaemonShutdownThread( final int returnCode ) + private void launchLastDitchDaemonShutdownThread( final int returnCode ) { getJvmTerminator().schedule( new Runnable() { @Override public void run() { - COMMAND_READER.stop(); - Runtime.getRuntime().halt( returnCode ); + kill( returnCode ); } }, systemExitTimeoutInSeconds, SECONDS ); } - private static RunResult invokeProviderInSameClassLoader( Object testSet, Object factory, - ProviderConfiguration providerConfig, - boolean insideFork, - StartupConfiguration startupConfig, - boolean restoreStreams ) + private RunResult invokeProviderInSameClassLoader( ForkingReporterFactory factory ) throws TestSetFailedException, InvocationTargetException { - final PrintStream orgSystemOut = out; - final PrintStream orgSystemErr = err; - // Note that System.out/System.err are also read in the "ReporterConfiguration" instantiation - // in createProvider below. These are the same values as here. - - try - { - return createProviderInCurrentClassloader( startupConfig, insideFork, providerConfig, factory ) - .invoke( testSet ); - } - finally - { - if ( restoreStreams && System.getSecurityManager() == null ) - { - setOut( orgSystemOut ); - setErr( orgSystemErr ); - } - } + return createProviderInCurrentClassloader( factory ) + .invoke( testSet ); } - private static SurefireProvider createProviderInCurrentClassloader( StartupConfiguration startupConfiguration, - boolean isInsideFork, - ProviderConfiguration providerConfiguration, - Object reporterManagerFactory ) + private SurefireProvider createProviderInCurrentClassloader( ForkingReporterFactory reporterManagerFactory ) { - BaseProviderFactory bpf = new BaseProviderFactory( (ReporterFactory) reporterManagerFactory, isInsideFork ); + BaseProviderFactory bpf = new BaseProviderFactory( reporterManagerFactory, true ); bpf.setTestRequest( providerConfiguration.getTestSuiteDefinition() ); bpf.setReporterConfiguration( providerConfiguration.getReporterConfiguration() ); ClassLoader classLoader = currentThread().getContextClassLoader(); @@ -426,6 +385,55 @@ public final class ForkedBooter return (SurefireProvider) instantiateOneArg( classLoader, providerClass, ProviderParameters.class, bpf ); } + /** + * This method is invoked when Surefire is forked - this method parses and organizes the arguments passed to it and + * then calls the Surefire class' run method. <br> The system exit code will be 1 if an exception is thrown. + * + * @param args Commandline arguments + */ + public static void main( String... args ) + { + ForkedBooter booter = new ForkedBooter(); + try + { + booter.setupBooter( args[0], args[1], args[2], args.length > 3 ? args[3] : null ); + booter.execute(); + } + catch ( Throwable t ) + { + DumpErrorSingleton.getSingleton().dumpException( t ); + t.printStackTrace(); + booter.cancelPingScheduler(); + booter.exit( 1 ); + } + } + + private static boolean canUseNewPingMechanism( PpidChecker pluginProcessChecker ) + { + return pluginProcessChecker != null && pluginProcessChecker.canUse(); + } + + private static boolean acquireOnePermit( Semaphore barrier, long timeoutMillis ) + { + try + { + return barrier.tryAcquire( timeoutMillis, MILLISECONDS ); + } + catch ( InterruptedException e ) + { + return true; + } + } + + private static ScheduledExecutorService createPingScheduler() + { + ThreadFactory threadFactory = newDaemonThreadFactory( "ping-" + PING_TIMEOUT_IN_SECONDS + "s" ); + ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor( 1, threadFactory ); + executor.setKeepAliveTime( 3L, SECONDS ); + executor.setMaximumPoolSize( 2 ); + return executor; + } + private static InputStream createSurefirePropertiesIfFileExists( String tmpDir, String propFileName ) throws FileNotFoundException { @@ -444,4 +452,30 @@ public final class ForkedBooter } return false; } + + private static class PingScheduler + { + private final ScheduledExecutorService pingScheduler; + private final PpidChecker pluginProcessChecker; + + PingScheduler( ScheduledExecutorService pingScheduler, PpidChecker pluginProcessChecker ) + { + this.pingScheduler = pingScheduler; + this.pluginProcessChecker = pluginProcessChecker; + } + + void shutdown() + { + pingScheduler.shutdown(); + if ( pluginProcessChecker != null ) + { + pluginProcessChecker.destroyActiveCommands(); + } + } + + boolean isShutdown() + { + return pingScheduler.isShutdown(); + } + } }
