Commits

ship-it  committed 3fd4b80

FECRU-1250: add switch for disabling special treatment of processes on Windows

  • Participants
  • Parent commits a64c0f3
  • Branches 1.3.1

Comments (0)

Files changed (3)

File processutils/src/main/java/com/atlassian/utils/process/ExternalProcessBuilder.java

 package com.atlassian.utils.process;
 
+import org.apache.log4j.Logger;
+import org.apache.log4j.Priority;
+
 import java.io.File;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
-
-import org.apache.log4j.Logger;
-import org.apache.log4j.Priority;
 
 
 /**
     private Map<String, String> environment = new HashMap<String, String>();
     private File workingDir;
     private long timeout;
+    private boolean suppressSpecialWindowsBehaviour = false;
     private static ExternalProcessFactory externalProcessFactory = new ExternalProcessFactory.Default();
 
     public ExternalProcessBuilder handlers(InputHandler input, OutputHandler output, OutputHandler error) {
         environment.put(variable, value);
         return this;
     }
-    
+
     public ExternalProcessBuilder env(Map<String, String> env) {
         environment.putAll(env);
         return this;
     }
-    
+
+    /**
+     * Processes created using processutils on Windows platforms default to performing a special workaround for lossy
+     * code page conversion issues. See the implementation of {@link ExternalProcessImpl#createWinProcess(String[], String[], File)}}
+     * for the gory details. This method can be used to suppress this 'special' behaviour and behave as if it were
+     * executing on a non-Windows platform.
+     *
+     * @param suppress whether to suppress special windows behaviour
+     * @return this {@link ExternalProcessBuilder} instance
+     */
+    public ExternalProcessBuilder suppressSpecialWindowsBehaviour(boolean suppress) {
+        suppressSpecialWindowsBehaviour = suppress;
+        return this;
+    }
+
     public ExternalProcess build() {
         ProcessHandler h = this.handler;
         if (this.handler == null) {
             h = plugHandler;
         }
 
-        return externalProcessFactory.createExternalProcess(command, timeout, workingDir, monitors, environment, h);
+        return externalProcessFactory.createExternalProcess(command, timeout, workingDir, monitors, environment, suppressSpecialWindowsBehaviour, h);
     }
 
 

File processutils/src/main/java/com/atlassian/utils/process/ExternalProcessFactory.java

 
 
     ExternalProcess createExternalProcess(List<String> command, long timeout, File workingDir,
-                                          List<ProcessMonitor> monitors, Map<String,String> environment,
-                                          ProcessHandler handler);
+                                          List<ProcessMonitor> monitors, Map<String, String> environment,
+                                          boolean suppressSpecialWindowsBehaviour, ProcessHandler handler);
 
     void shutdown();
 
     public static class Default implements ExternalProcessFactory {
 
         public ExternalProcess createExternalProcess(List<String> command, long timeout, File workingDir,
-                                                     List<ProcessMonitor> monitors, Map<String,String> environment,
-                                                     ProcessHandler handler) {
+                                                     List<ProcessMonitor> monitors, Map<String, String> environment,
+                                                     boolean suppressSpecialWindowsBehaviour, ProcessHandler handler) {
             ExternalProcessImpl process =  new ExternalProcessImpl(command, handler);
-            return configureProcess(process, timeout, workingDir, monitors, environment);
+            return configureProcess(process, timeout, workingDir, monitors, environment, suppressSpecialWindowsBehaviour);
         }
 
         public void shutdown() {
         }
 
         protected ExternalProcess configureProcess(ExternalProcessImpl process, long timeout, File workingDir,
-                                                   List<ProcessMonitor> monitors, Map<String, String> environment) {
+                                                   List<ProcessMonitor> monitors, Map<String, String> environment,
+                                                   boolean suppressSpecialWindowsBehaviour) {
             if (timeout > 0L) {
                 process.setTimeout(timeout);
             }
                 }
                 process.setEnvironment(env);
             }
+            process.setSuppressSpecialWindowsBehaviour(suppressSpecialWindowsBehaviour);
             return process;
         }
 

File processutils/src/main/java/com/atlassian/utils/process/ExternalProcessImpl.java

     private String[] environment;
     private ProcessHandler handler;
     private Process process;
+    private boolean suppressSpecialWindowsBehaviour;
 
     private List<ProcessMonitor> monitors = new ArrayList<ProcessMonitor>();
 
         this.environment = environment;
     }
 
+    public void setSuppressSpecialWindowsBehaviour(final boolean suppressSpecialWindowsBehaviour) {
+        this.suppressSpecialWindowsBehaviour = suppressSpecialWindowsBehaviour;
+    }
+
     private boolean arePumpsRunning() {
         return outputPump.isRunning() || errorPump.isRunning()
                 || (inputPump != null && inputPump.isRunning());
     public Long getStartTime() {
         return this.startTime;
     }
-    
+
     public void addMonitor(ProcessMonitor monitor) {
         this.monitors.add(monitor);
     }
-    
+
     public void removeMonitor(ProcessMonitor monitor) {
         this.monitors.remove(monitor);
     }
-    
+
     private boolean isWindows() {
     	return System.getProperty("os.name").toLowerCase().contains("windows");
     }
-    
+
     private String quoteString(String value) {
         StringBuilder builder = new StringBuilder()
             .append("\"")
             .append("\"");
         return builder.toString();
     }
-    
+
     /*
       * This method provides a workaround for a JVM bug on windows (see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4947220). The bug
       * is that the Sun/Oracle JVM uses the (8 bit) OEM codepage for encoding commandline arguments that are passed to an external process.  Any
       * characters in a command line argument that can't be represented in the OEM codepage will be replaced by the '?' character, which will probably
       * cause the command that's being called to fail.
-      * 
-      * A bit of background information is helpful to understand what's going on. Windows uses 2 code pages: the OEM code page and the ANSI code page 
+      *
+      * A bit of background information is helpful to understand what's going on. Windows uses 2 code pages: the OEM code page and the ANSI code page
       * (or 'Windows code page'). The OEM code page is always limited to 8 bit character encodings, whereas some windows code pages are 8 bit and some
       * are larger. The OEM code page is typically used for console applications, whereas the windows code pages are used for native non-Unicode application
       * using a GUI on Windows systems.  The system-wide settings can be found in the windows registry:
-      * 
-      * 
+      *
+      *
       * More info about the history of OEM vs ANSI code pages: http://blogs.msdn.com/b/michkap/archive/2005/02/08/369197.aspx
-      * 
+      *
       * The workaround is to store the command-line arguments in environment variables and refer to the environment vars on the command line. Windows
-      * cmd will expand the command line arguments without using to the OEM code page, which usually has more correlation with the locale of the 
+      * cmd will expand the command line arguments without using to the OEM code page, which usually has more correlation with the locale of the
       * producer of the hardware (it's derived from the BIOS code page) than with the regional settings configured in Windows. The ANSI code page is derived from
       * the regional settings.
-      * 
+      *
       * When cmd expands the %JENV_XXX% vars on the command line it uses the ANSI code page instead of the OEM code page (or that is what testing
-      * seems to indicate, can't find any definitive answer in the cmd docos). While this still isn't a true fix to the problem, for most situations this will be sufficient 
+      * seems to indicate, can't find any definitive answer in the cmd docos). While this still isn't a true fix to the problem, for most situations this will be sufficient
       * as the user typically won't use characters that aren't defined for his locale. But then again, they might..
      */
     private Process createWinProcess(String[] cmdArray, String[] environment, File workingDir) throws IOException {
         int extraArgs = 3;
         String[] i18n = new String[cmdArray.length + extraArgs];
         i18n[0] = "cmd";
-        i18n[1] = "/A"; // use ANSI encoding 
+        i18n[1] = "/A"; // use ANSI encoding
         i18n[2] = "/C";
         i18n[extraArgs] = cmdArray[0];
         for (int counter = 1; counter < cmdArray.length; counter++) {
     }
 
     protected Process createProcess(String[] cmdArray, String[] environment, File workingDir) throws IOException {
-        if (isWindows()) {
+        if (isWindows() && !suppressSpecialWindowsBehaviour) {
             return createWinProcess(cmdArray, environment, workingDir);
         } else {
             return Runtime.getRuntime().exec(cmdArray, environment, workingDir);
             handler.complete(-1, processException);
         }
     }
-    
+
     /**
      * Notifies all ProcessMonitors of the 'beforeStart' event.
      */
             }
         }
     }
-    
+
     /**
      * Execute the external command. When this method returns, the process handler
      * provided at construction time should be consulted to collect exit code, exceptions,
     public void setTimeout(long timeout) {
         this.timeout = timeout;
     }
-    
+
     public static void shutdown() {
         if (pumpThreadPool != null) {
             pumpThreadPool.shutdown();