You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by ti...@apache.org on 2017/07/01 19:58:16 UTC
maven-surefire git commit: [SUREFIRE-1302] Surefire does not wait
long enough for the forked VM and assumes it to be dead [Forced Update!]
Repository: maven-surefire
Updated Branches:
refs/heads/SUREFIRE-1302_3 b601ac02f -> 5186c43a3 (forced update)
[SUREFIRE-1302] Surefire does not wait long enough for the forked VM and assumes it to be dead
Project: http://git-wip-us.apache.org/repos/asf/maven-surefire/repo
Commit: http://git-wip-us.apache.org/repos/asf/maven-surefire/commit/5186c43a
Tree: http://git-wip-us.apache.org/repos/asf/maven-surefire/tree/5186c43a
Diff: http://git-wip-us.apache.org/repos/asf/maven-surefire/diff/5186c43a
Branch: refs/heads/SUREFIRE-1302_3
Commit: 5186c43a35a2ee9e92e94a62e51e1157a401a48e
Parents: 7176d3c
Author: Tibor17 <ti...@apache.org>
Authored: Sun Jun 25 23:16:04 2017 +0200
Committer: Tibor17 <ti...@apache.org>
Committed: Sat Jul 1 21:55:41 2017 +0200
----------------------------------------------------------------------
maven-failsafe-plugin/pom.xml | 17 --
maven-surefire-common/pom.xml | 6 +
.../surefire/report/FileReporterUtils.java | 11 +-
maven-surefire-plugin/pom.xml | 17 --
.../src/site/apt/examples/shutdown.apt.vm | 9 +
maven-surefire-plugin/src/site/apt/index.apt.vm | 3 +-
maven-surefire-report-plugin/pom.xml | 4 -
pom.xml | 17 +-
surefire-api/pom.xml | 10 +-
.../maven/surefire/booter/CommandReader.java | 4 +-
surefire-booter/pom.xml | 30 ++-
.../maven/surefire/booter/ForkedBooter.java | 67 ++++-
.../maven/surefire/booter/PpidChecker.java | 256 +++++++++++++++++++
.../maven/surefire/booter/ProcessInfo.java | 62 +++++
.../maven/surefire/booter/JUnit4SuiteTest.java | 3 +-
.../maven/surefire/booter/PpidCheckerTest.java | 116 +++++++++
...urefire1295AttributeJvmCrashesToTestsIT.java | 6 +-
17 files changed, 568 insertions(+), 70 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/5186c43a/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/5186c43a/maven-surefire-common/pom.xml
----------------------------------------------------------------------
diff --git a/maven-surefire-common/pom.xml b/maven-surefire-common/pom.xml
index 4064bc2..e911a4c 100644
--- a/maven-surefire-common/pom.xml
+++ b/maven-surefire-common/pom.xml
@@ -93,6 +93,7 @@
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.shared</groupId>
@@ -192,6 +193,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 +205,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/5186c43a/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/5186c43a/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/5186c43a/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..4c4a0a3 100644
--- a/maven-surefire-plugin/src/site/apt/examples/shutdown.apt.vm
+++ b/maven-surefire-plugin/src/site/apt/examples/shutdown.apt.vm
@@ -39,6 +39,15 @@ Shutdown of Forked JVM
* Pinging forked JVM
+ << Since ${thisPlugin} Plugin 2.20.1 ping is platform dependent and fallbacks to old mechanism if platform is
+ not recognized or native commands fail in Java. >>
+
+ Simply the mechanism checks the <<<PPID>>> still exists and it is not reused by OS in another application.
+ If parent (normally Maven) process has died, the forked JVM is killed.
+
+
+ << 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.
If the master process is killed (received SIGKILL signal) or shutdown
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/5186c43a/maven-surefire-plugin/src/site/apt/index.apt.vm
----------------------------------------------------------------------
diff --git a/maven-surefire-plugin/src/site/apt/index.apt.vm b/maven-surefire-plugin/src/site/apt/index.apt.vm
index 0ec361c..c996301 100644
--- a/maven-surefire-plugin/src/site/apt/index.apt.vm
+++ b/maven-surefire-plugin/src/site/apt/index.apt.vm
@@ -32,7 +32,8 @@ Maven ${thisPlugin} Plugin
Requirements: Maven 2.2.1 or 3.x, and JDK 1.6 or higher. Due to wrong formatting of console text messages
- in Maven Version prior to 3.1.0 it is highly recommended to use Maven 3.1.0 or higher.
+ in Maven Version prior to 3.1.0 it is highly recommended to use Maven 3.1.0 or higher. The plugin supports
+ Unix, Linux and Windows OS families but does not support Windows ME and 9x series.
#{if}(${project.artifactId}=="maven-surefire-plugin")
The Surefire Plugin is used during the <<<test>>> phase of the build
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/5186c43a/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/5186c43a/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/5186c43a/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/5186c43a/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/5186c43a/surefire-booter/pom.xml
----------------------------------------------------------------------
diff --git a/surefire-booter/pom.xml b/surefire-booter/pom.xml
index b79cceb..de6217e 100644
--- a/surefire-booter/pom.xml
+++ b/surefire-booter/pom.xml
@@ -35,6 +35,25 @@
<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.maven.shared</groupId>
+ <artifactId>maven-shared-utils</artifactId>
+ <scope>test</scope>
+ </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 +88,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/5186c43a/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..ec67faa 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
@@ -37,7 +37,9 @@ import java.lang.reflect.InvocationTargetException;
import java.security.AccessControlException;
import java.security.AccessController;
import java.security.PrivilegedAction;
+import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.Semaphore;
@@ -207,12 +209,51 @@ public final class ForkedBooter
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 );
+ Future<PpidChecker> checker = pingScheduler.submit( createProcessCheckerJob() );
+ pingScheduler.scheduleWithFixedDelay( processCheckerJob( checker ), 0L, 1L, SECONDS );
+ pingScheduler.scheduleAtFixedRate( createPingJob( pingDone, checker ), 0L, PING_TIMEOUT_IN_SECONDS, SECONDS );
return pingScheduler;
}
+ private static Runnable processCheckerJob( final Future<PpidChecker> processChecker )
+ {
+ return new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ if ( processChecker.isDone() && !processChecker.isCancelled() )
+ {
+ try
+ {
+ PpidChecker checker = processChecker.get();
+ if ( checker.canUse() && !checker.isParentProcessAlive() )
+ {
+ kill();
+ }
+ }
+ catch ( Exception e )
+ {
+ // nothing to do
+ }
+ }
+ }
+ };
+ }
+
+ private static Callable<PpidChecker> createProcessCheckerJob()
+ {
+ return new Callable<PpidChecker>()
+ {
+ @Override
+ public PpidChecker call() throws Exception
+ {
+ return new PpidChecker();
+ }
+ };
+ }
+
private static CommandListener createPingHandler( final AtomicBoolean pingDone )
{
return new CommandListener()
@@ -246,13 +287,17 @@ public final class ForkedBooter
};
}
- private static Runnable createPingJob( final AtomicBoolean pingDone )
+ private static Runnable createPingJob( final AtomicBoolean pingDone, final Future<PpidChecker> processChecker )
{
return new Runnable()
{
@Override
public void run()
{
+ if ( canUseNewPingMechanism( processChecker ) )
+ {
+ return;
+ }
boolean hasPing = pingDone.getAndSet( false );
if ( !hasPing )
{
@@ -262,6 +307,18 @@ public final class ForkedBooter
};
}
+ private static boolean canUseNewPingMechanism( Future<PpidChecker> processChecker )
+ {
+ try
+ {
+ return ( !processChecker.isDone() || processChecker.get().canUse() ) && !processChecker.isCancelled();
+ }
+ catch ( Exception e )
+ {
+ return false;
+ }
+ }
+
private static void encodeAndWriteToOutput( String string, PrintStream out )
{
byte[] encodeBytes = encodeStringForForkCommunication( string );
@@ -357,8 +414,8 @@ public final class ForkedBooter
{
ThreadFactory threadFactory = newDaemonThreadFactory( "ping-" + PING_TIMEOUT_IN_SECONDS + "s" );
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor( 1, threadFactory );
- executor.setMaximumPoolSize( 1 );
- executor.prestartCoreThread();
+ executor.setKeepAliveTime( 3L, SECONDS );
+ executor.setMaximumPoolSize( 3 );
return executor;
}
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/5186c43a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/PpidChecker.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/PpidChecker.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/PpidChecker.java
new file mode 100644
index 0000000..f7aebf8
--- /dev/null
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/PpidChecker.java
@@ -0,0 +1,256 @@
+package org.apache.maven.surefire.booter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.management.ManagementFactory;
+import java.util.Locale;
+import java.util.StringTokenizer;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static org.apache.commons.io.IOUtils.closeQuietly;
+import static org.apache.commons.lang3.SystemUtils.IS_OS_UNIX;
+import static org.apache.commons.lang3.SystemUtils.IS_OS_WINDOWS;
+import static org.apache.maven.surefire.booter.ProcessInfo.INVALID_PROCESS_INFO;
+
+/**
+ * Recognizes PPID. Determines lifetime of parent process.
+ * <br>
+ * This class cannot use maven-shared-utils because we need to run /bin/bash and not /bin/sh on Unix, and
+ * cmd /a /x /c instead of cmd /x /c on Windows.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.20.1
+ */
+final class PpidChecker
+{
+ private static final String WMIC_PPID = "ParentProcessId";
+
+ private static final String WMIC_CREATION_DATE = "CreationDate";
+
+ private static final String WINDOWS_CMD =
+ "wmic process where (ProcessId=%s) get " + WMIC_CREATION_DATE + ", " + WMIC_PPID;
+
+ private static final String[] WINDOWS_CMD_ARRAY = { "CMD", "/A", "/X", "/C", null };
+
+ private static final String[] UNIX_CMD1 =
+ { "/usr/bin/env", "bash", "-c", "/usr/bin/ps --noheaders -o etimes -p $PPID" };
+
+ private static final String[] UNIX_CMD2 =
+ { "/bin/env", "bash", "-c", "/bin/ps --noheaders -o etimes -p $PPID" };
+
+ private static final Pattern NUMBER_PATTERN = Pattern.compile( "^[\\d]+$" );
+
+ private final ProcessInfo parentProcessInfo;
+
+ PpidChecker()
+ {
+ ProcessInfo parentProcess = INVALID_PROCESS_INFO;
+ if ( IS_OS_WINDOWS )
+ {
+ String pid = pid();
+ if ( pid != null )
+ {
+ ProcessInfo currentProcessInfo = windows( pid );
+ String ppid = currentProcessInfo.getPPID();
+ parentProcess = currentProcessInfo.isValid() ? windows( ppid ) : INVALID_PROCESS_INFO;
+ }
+ }
+ else if ( IS_OS_UNIX )
+ {
+ parentProcess = unix();
+ }
+ parentProcessInfo = parentProcess.isValid() ? parentProcess : INVALID_PROCESS_INFO;
+ }
+
+ boolean canUse()
+ {
+ return parentProcessInfo.isValid();
+ }
+
+ boolean isParentProcessAlive()
+ {
+ if ( !canUse() )
+ {
+ throw new IllegalStateException();
+ }
+
+ if ( IS_OS_WINDOWS )
+ {
+ ProcessInfo pp = windows( parentProcessInfo.getPID() );
+ // let's compare creation time, should be same unless killed or PPID is reused by OS into another process
+ return pp.isValid() && parentProcessInfo.getTime().equals( pp.getTime() );
+ }
+ else if ( IS_OS_UNIX )
+ {
+ ProcessInfo pp = unix();
+ // let's compare elapsed time, should be greater or equal if parent process is the same and still alive
+ return pp.isValid() && (Long) pp.getTime() >= (Long) parentProcessInfo.getTime();
+ }
+
+ throw new IllegalStateException();
+ }
+
+ // https://www.freebsd.org/cgi/man.cgi?ps(1)
+ // etimes elapsed running time, in decimal integer seconds
+
+ // http://manpages.ubuntu.com/manpages/xenial/man1/ps.1.html
+ // etimes elapsed time since the process was started, in seconds.
+
+ // http://hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96f96a0f/test/java/lang/ProcessBuilder/Basic.java#L167
+ static ProcessInfo unix()
+ {
+ String[] cmd = new File( "/usr/bin/env" ).canExecute() ? UNIX_CMD1 : UNIX_CMD2;
+ ProcessBuilder probuilder = new ProcessBuilder( cmd );
+ Process p = null;
+ BufferedReader reader = null;
+ ProcessInfo processInfo = INVALID_PROCESS_INFO;
+ try
+ {
+ p = probuilder.start();
+ reader = new BufferedReader( new InputStreamReader( p.getInputStream() ) );
+ for ( String line = reader.readLine(); line != null; line = reader.readLine() )
+ {
+ line = line.trim();
+ if ( !line.isEmpty() )
+ {
+ Matcher matcher = NUMBER_PATTERN.matcher( line );
+ if ( matcher.matches() )
+ {
+ long pidUptime = Long.valueOf( line );
+ processInfo = new ProcessInfo( "pid not needed in unix", pidUptime, null );
+ }
+ }
+ }
+ p.waitFor();
+ return processInfo;
+ }
+ catch ( IOException e )
+ {
+ return processInfo;
+ }
+ catch ( InterruptedException e )
+ {
+ return processInfo;
+ }
+ finally
+ {
+ closeQuietly( reader );
+ if ( p != null )
+ {
+ p.destroy();
+ }
+ }
+ }
+
+ static String pid()
+ {
+ String processName = ManagementFactory.getRuntimeMXBean().getName();
+ if ( processName != null && processName.contains( "@" ) )
+ {
+ String pid = processName.substring( 0, processName.indexOf( '@' ) ).trim();
+ if ( NUMBER_PATTERN.matcher( pid ).matches() )
+ {
+ return pid;
+ }
+ }
+ return null;
+ }
+
+ static ProcessInfo windows( String pid )
+ {
+ Process p = null;
+ BufferedReader reader = null;
+ ProcessInfo processInfo = INVALID_PROCESS_INFO;
+ try
+ {
+ String[] cmd = WINDOWS_CMD_ARRAY.clone();
+ cmd[cmd.length - 1] = String.format( Locale.ROOT, WINDOWS_CMD, pid );
+ ProcessBuilder probuilder = new ProcessBuilder( cmd );
+ p = probuilder.start();
+ reader = new BufferedReader( new InputStreamReader( p.getInputStream() ) );
+ boolean hasHeader = false;
+ boolean isStartTimestampFirst = false;
+ for ( String line = reader.readLine(); line != null; line = reader.readLine() )
+ {
+ line = line.trim();
+
+ if ( line.isEmpty() )
+ {
+ continue;
+ }
+
+ if ( hasHeader )
+ {
+ StringTokenizer args = new StringTokenizer( line );
+ if ( args.countTokens() == 2 )
+ {
+ if ( isStartTimestampFirst )
+ {
+ String startTimestamp = args.nextToken();
+ String ppid = args.nextToken();
+ processInfo = new ProcessInfo( pid, startTimestamp, ppid );
+ }
+ else
+ {
+ String ppid = args.nextToken();
+ String startTimestamp = args.nextToken();
+ processInfo = new ProcessInfo( pid, startTimestamp, ppid );
+ }
+ }
+ }
+ else
+ {
+ StringTokenizer args = new StringTokenizer( line );
+ if ( args.countTokens() == 2 )
+ {
+ String arg0 = args.nextToken();
+ String arg1 = args.nextToken();
+ isStartTimestampFirst = WMIC_CREATION_DATE.equals( arg0 );
+ hasHeader = isStartTimestampFirst || WMIC_PPID.equals( arg0 );
+ hasHeader &= WMIC_CREATION_DATE.equals( arg1 ) || WMIC_PPID.equals( arg1 );
+ }
+ }
+ }
+ p.waitFor();
+ return processInfo;
+ }
+ catch ( IOException e )
+ {
+ return processInfo;
+ }
+ catch ( InterruptedException e )
+ {
+ return processInfo;
+ }
+ finally
+ {
+ closeQuietly( reader );
+ if ( p != null )
+ {
+ p.destroy();
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/5186c43a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProcessInfo.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProcessInfo.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProcessInfo.java
new file mode 100644
index 0000000..addee1d
--- /dev/null
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProcessInfo.java
@@ -0,0 +1,62 @@
+package org.apache.maven.surefire.booter;
+
+/*
+ * 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.
+ */
+
+/**
+ * PID, PPID, elapsed time (Unix) or start time (Windows).
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.20.1
+ */
+final class ProcessInfo
+{
+ static final ProcessInfo INVALID_PROCESS_INFO = new ProcessInfo( null, null, null );
+
+ private final String pid;
+ private final Object time;
+ private final String ppid;
+
+ ProcessInfo( String pid, Object time, String ppid )
+ {
+ this.pid = pid;
+ this.time = time;
+ this.ppid = ppid;
+ }
+
+ boolean isValid()
+ {
+ return pid != null && time != null;
+ }
+
+ String getPID()
+ {
+ return pid;
+ }
+
+ Object getTime()
+ {
+ return time;
+ }
+
+ String getPPID()
+ {
+ return ppid;
+ }
+}
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/5186c43a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java
index 2bdcf21..b08423f 100644
--- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java
@@ -34,7 +34,8 @@ import org.junit.runners.Suite;
ClasspathTest.class,
CommandReaderTest.class,
PropertiesWrapperTest.class,
- SurefireReflectorTest.class
+ SurefireReflectorTest.class,
+ PpidCheckerTest.class
} )
@RunWith( Suite.class )
public class JUnit4SuiteTest
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/5186c43a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/PpidCheckerTest.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/PpidCheckerTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/PpidCheckerTest.java
new file mode 100644
index 0000000..cb5fd74
--- /dev/null
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/PpidCheckerTest.java
@@ -0,0 +1,116 @@
+package org.apache.maven.surefire.booter;
+
+/*
+ * 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 java.util.concurrent.TimeUnit;
+
+import static org.apache.commons.lang3.SystemUtils.IS_OS_UNIX;
+import static org.apache.commons.lang3.SystemUtils.IS_OS_WINDOWS;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.junit.Assume.assumeTrue;
+
+/**
+ * Testing {@link PpidChecker} on a platform.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.20.1
+ */
+public class PpidCheckerTest
+{
+ @Test
+ public void shouldHavePid()
+ {
+ String pid = PpidChecker.pid();
+
+ assertThat( pid )
+ .isNotNull();
+
+ assertThat( pid )
+ .matches( "^[\\d]+$" );
+ }
+
+ @Test
+ public void shouldHavePpidAsWindows()
+ {
+ assumeTrue( IS_OS_WINDOWS );
+
+ ProcessInfo processInfo = PpidChecker.windows( PpidChecker.pid() );
+
+ assertThat( processInfo )
+ .isNotNull();
+
+ assertThat( processInfo.getPID() )
+ .isNotNull();
+
+ assertThat( processInfo.getPID() )
+ .matches( "^[\\d]+$" );
+
+ assertThat( processInfo.getTime() )
+ .isNotNull();
+
+ processInfo = PpidChecker.windows( processInfo.getPID() );
+
+ assertThat( processInfo.getPID() )
+ .isNotNull();
+
+ assertThat( processInfo.getPID() )
+ .matches( "^[\\d]+$" );
+
+ assertThat( processInfo.getTime() )
+ .isNotNull();
+ }
+
+ @Test
+ public void shouldHavePpidAsUnix()
+ {
+ assumeTrue( IS_OS_UNIX );
+
+ ProcessInfo processInfo = PpidChecker.unix();
+
+ assertThat( processInfo )
+ .isNotNull();
+
+ assertThat( processInfo.getPID() )
+ .isNotNull();
+
+ assertThat( processInfo.getPID() )
+ .isEqualTo( "pid not needed in unix" );
+
+ assertThat( processInfo.getTime() )
+ .isNotNull();
+ }
+
+ @Test
+ public void shouldFindAliveParentProcess()
+ throws InterruptedException
+ {
+ PpidChecker checker = new PpidChecker();
+
+ assertThat( checker.canUse() )
+ .isTrue();
+
+ TimeUnit.MILLISECONDS.sleep( 100L );
+
+ assertThat( checker.isParentProcessAlive() )
+ .isTrue();
+ }
+}
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/5186c43a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1295AttributeJvmCrashesToTestsIT.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1295AttributeJvmCrashesToTestsIT.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1295AttributeJvmCrashesToTestsIT.java
index 1fa88f6..f051c1c 100644
--- a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1295AttributeJvmCrashesToTestsIT.java
+++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1295AttributeJvmCrashesToTestsIT.java
@@ -27,8 +27,9 @@ import org.junit.Before;
import org.junit.Test;
import java.util.Iterator;
-import java.util.Locale;
+import static org.apache.commons.lang.SystemUtils.IS_OS_LINUX;
+import static org.apache.commons.lang.SystemUtils.IS_OS_MAC_OSX;
import static org.fest.assertions.Assertions.assertThat;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
@@ -46,8 +47,7 @@ public class Surefire1295AttributeJvmCrashesToTestsIT
@Before
public void skipWindows()
{
- String os = System.getProperty( "os.name" ).toLowerCase( Locale.ROOT );
- assumeTrue( os.equals( "mac os x" ) || os.equals( "linux" ) /*|| os.contains( "windows" )*/ );
+ assumeTrue( IS_OS_LINUX || IS_OS_MAC_OSX );
}
@Test