maven-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From tibordig...@apache.org
Subject [maven-surefire] 01/01: [SUREFIRE-1512] ProcessInfo for Windows is prone to timezone offset changes
Date Sun, 08 Apr 2018 14:57:46 GMT
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 dbe936285d06863f5b5218457e5920b198f28905
Author: Tibor17 <tibordigana@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  | 73 +++++++++++++++-------
 .../apache/maven/surefire/booter/ProcessInfo.java  |  6 +-
 2 files changed, 51 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..97ca955 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,15 @@ 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.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 +41,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 +54,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 +160,7 @@ final class PpidChecker
                                                  + fromHours( matcher )
                                                  + fromMinutes( matcher )
                                                  + fromSeconds( matcher );
-                        return ProcessInfo.unixProcessInfo( pluginPid, pidUptime );
+                        return unixProcessInfo( pluginPid, pidUptime );
                     }
                 }
                 return previousProcessInfo;
@@ -168,31 +177,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 +224,7 @@ final class PpidChecker
         return stopped;
     }
 
-    static String unixPathToPS()
+    private static String unixPathToPS()
     {
         return canExecuteLocalUnixPs() ? "/usr/bin/ps" : "/bin/ps";
     }
@@ -271,6 +285,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" );
+        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 +314,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 +340,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
tibordigana@apache.org.

Mime
View raw message