This is an automated email from the ASF dual-hosted git repository.

tibordigana pushed a commit to branch 1512
in repository https://gitbox.apache.org/repos/asf/maven-surefire.git

commit b67ce44f245e0418268c8d37ef4d807d28c520be
Author: Tibor17 <tibordig...@apache.org>
AuthorDate: Sun Apr 8 12:24:39 2018 +0200

    [SUREFIRE-1512] ProcessInfo for Windows is prone to timezone offset changes
---
 .../apache/maven/surefire/booter/PpidChecker.java  | 74 +++++++++++++++-------
 .../apache/maven/surefire/booter/ProcessInfo.java  |  6 +-
 2 files changed, 52 insertions(+), 28 deletions(-)

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
index 32e3ad7..491a786 100644
--- 
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
@@ -22,13 +22,16 @@ package org.apache.maven.surefire.booter;
 import java.io.File;
 import java.io.IOException;
 import java.nio.charset.Charset;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
 import java.util.Queue;
 import java.util.Scanner;
-import java.util.StringTokenizer;
+import java.util.TimeZone;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import static java.lang.Integer.parseInt;
 import static java.lang.Long.parseLong;
 import static java.util.concurrent.TimeUnit.DAYS;
 import static java.util.concurrent.TimeUnit.HOURS;
@@ -39,6 +42,8 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
 import static org.apache.commons.lang3.SystemUtils.IS_OS_HP_UX;
 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.unixProcessInfo;
+import static org.apache.maven.surefire.booter.ProcessInfo.windowsProcessInfo;
 import static org.apache.maven.surefire.booter.ProcessInfo.ERR_PROCESS_INFO;
 import static 
org.apache.maven.surefire.booter.ProcessInfo.INVALID_PROCESS_INFO;
 
@@ -50,6 +55,11 @@ import static 
org.apache.maven.surefire.booter.ProcessInfo.INVALID_PROCESS_INFO;
  */
 final class PpidChecker
 {
+    private static final int MINUTES_TO_MILLIS = 60 * 1000;
+    // 25 chars 
https://superuser.com/questions/937380/get-creation-time-of-file-in-milliseconds/937401#937401
+    private static final int WMIC_CREATION_DATE_VALUE_LENGTH = 25;
+    private static final int WMIC_CREATION_DATE_TIMESTAMP_LENGTH = 18;
+    private static final SimpleDateFormat WMIC_CREATION_DATE_FORMAT = 
createWindowsCreationDateFormat();
     private static final String WMIC_CREATION_DATE = "CreationDate";
     private static final String WINDOWS_SYSTEM_ROOT_ENV = "SystemRoot";
     private static final String RELATIVE_PATH_TO_WMIC = "System32\\Wbem";
@@ -151,7 +161,7 @@ final class PpidChecker
                                                  + fromHours( matcher )
                                                  + fromMinutes( matcher )
                                                  + fromSeconds( matcher );
-                        return ProcessInfo.unixProcessInfo( pluginPid, 
pidUptime );
+                        return unixProcessInfo( pluginPid, pidUptime );
                     }
                 }
                 return previousProcessInfo;
@@ -168,31 +178,36 @@ final class PpidChecker
             private boolean hasHeader;
 
             @Override
-            ProcessInfo consumeLine( String line, ProcessInfo 
previousProcessInfo )
+            ProcessInfo consumeLine( String line, ProcessInfo 
previousProcessInfo ) throws Exception
             {
-                if ( !previousProcessInfo.isValid() )
+                if ( !previousProcessInfo.isValid() && !line.isEmpty() )
                 {
-                    StringTokenizer args = new StringTokenizer( line );
-                    if ( args.countTokens() == 1 )
+                    if ( hasHeader )
                     {
-                        if ( hasHeader )
-                        {
-                            String startTimestamp = args.nextToken();
-                            return ProcessInfo.windowsProcessInfo( pluginPid, 
startTimestamp );
-                        }
-                        else
+                        // now the line is CreationDate, e.g. 
20180406142327.741074+120
+                        if ( line.length() != WMIC_CREATION_DATE_VALUE_LENGTH )
                         {
-                            hasHeader = WMIC_CREATION_DATE.equals( 
args.nextToken() );
+                            throw new IllegalStateException( "WMIC 
CreationDate should have 25 characters "
+                                    + line );
                         }
+                        String startTimestamp = line.substring( 0, 
WMIC_CREATION_DATE_TIMESTAMP_LENGTH );
+                        int indexOfTimeZone = WMIC_CREATION_DATE_VALUE_LENGTH 
- 4;
+                        long startTimestampMillisUTC =
+                                WMIC_CREATION_DATE_FORMAT.parse( 
startTimestamp ).getTime()
+                                        - parseInt( line.substring( 
indexOfTimeZone ) ) * MINUTES_TO_MILLIS;
+                        return windowsProcessInfo( pluginPid, 
startTimestampMillisUTC );
+                    }
+                    else
+                    {
+                        hasHeader = WMIC_CREATION_DATE.equals( line );
                     }
                 }
                 return previousProcessInfo;
             }
         };
-        String pid = String.valueOf( pluginPid );
         String wmicPath = hasWmicStandardSystemPath() ? SYSTEM_PATH_TO_WMIC : 
"";
         return reader.execute( "CMD", "/A", "/X", "/C",
-                wmicPath + "wmic process where (ProcessId=" + pid + ") get " + 
WMIC_CREATION_DATE
+                wmicPath + "wmic process where (ProcessId=" + pluginPid + ") 
get " + WMIC_CREATION_DATE
         );
     }
 
@@ -210,7 +225,7 @@ final class PpidChecker
         return stopped;
     }
 
-    static String unixPathToPS()
+    private static String unixPathToPS()
     {
         return canExecuteLocalUnixPs() ? "/usr/bin/ps" : "/bin/ps";
     }
@@ -271,6 +286,20 @@ final class PpidChecker
     }
 
     /**
+     * The beginning part of Windows WMIC format yyyymmddHHMMSS.xxx <br>
+     * https://technet.microsoft.com/en-us/library/ee198928.aspx <br>
+     * We use UTC time zone which avoids DST changes, see SUREFIRE-1512.
+     *
+     * @return Windows WMIC format yyyymmddHHMMSS.xxx
+     */
+    private static SimpleDateFormat createWindowsCreationDateFormat()
+    {
+        SimpleDateFormat formatter = new SimpleDateFormat( 
"yyyyMMddHHmmss'.'SSS", Locale.ENGLISH );
+        formatter.setTimeZone( TimeZone.getTimeZone( "UTC" ) );
+        return formatter;
+    }
+
+    /**
      * Reads standard output from {@link Process}.
      * <br>
      * The artifact maven-shared-utils has non-daemon Threads which is an 
issue in Surefire to satisfy System.exit.
@@ -286,7 +315,7 @@ final class PpidChecker
             this.charset = charset;
         }
 
-        abstract ProcessInfo consumeLine( String line, ProcessInfo 
previousProcessInfo );
+        abstract ProcessInfo consumeLine( String line, ProcessInfo 
previousProcessInfo ) throws Exception;
 
         ProcessInfo execute( String... command )
         {
@@ -312,14 +341,11 @@ final class PpidChecker
                 int exitCode = process.waitFor();
                 return exitCode == 0 ? processInfo : INVALID_PROCESS_INFO;
             }
-            catch ( IOException e )
+            catch ( Exception e )
             {
-                DumpErrorSingleton.getSingleton().dumpException( e );
-                return ERR_PROCESS_INFO;
-            }
-            catch ( InterruptedException e )
-            {
-                DumpErrorSingleton.getSingleton().dumpException( e );
+                DumpErrorSingleton.getSingleton()
+                        .dumpException( e );
+
                 return ERR_PROCESS_INFO;
             }
             finally
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
index 212e221..2e48599 100644
--- 
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
@@ -19,8 +19,6 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
-import static 
org.apache.maven.surefire.util.internal.ObjectUtils.requireNonNull;
-
 /**
  * Immutable object which encapsulates PID and elapsed time (Unix) or start 
time (Windows).
  * <br>
@@ -47,9 +45,9 @@ final class ProcessInfo
         return new ProcessInfo( pid, etime );
     }
 
-    static ProcessInfo windowsProcessInfo( long pid, String startTimestamp )
+    static ProcessInfo windowsProcessInfo( long pid, long startTimestamp )
     {
-        return new ProcessInfo( pid, requireNonNull( startTimestamp, 
"startTimestamp is NULL" ) );
+        return new ProcessInfo( pid, startTimestamp );
     }
 
     private final Long pid;

-- 
To stop receiving notification emails like this one, please contact
tibordig...@apache.org.

Reply via email to