]> git.argeo.org Git - gpl/argeo-slc.git/blobdiff - runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/core/execution/tasks/SystemCall.java
Introduce revision build numbers
[gpl/argeo-slc.git] / runtime / org.argeo.slc.support.simple / src / main / java / org / argeo / slc / core / execution / tasks / SystemCall.java
index c0316128549e1be498836f9e08ed0c34bd2fe597..e45ae540d12d74c36d38d4890621da660cd5e32f 100644 (file)
@@ -1,43 +1,87 @@
 package org.argeo.slc.core.execution.tasks;
 
-import java.io.BufferedReader;
 import java.io.File;
+import java.io.FileWriter;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
 import java.io.Writer;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.commons.exec.CommandLine;
+import org.apache.commons.exec.DefaultExecutor;
+import org.apache.commons.exec.ExecuteException;
+import org.apache.commons.exec.ExecuteResultHandler;
+import org.apache.commons.exec.ExecuteWatchdog;
+import org.apache.commons.exec.Executor;
+import org.apache.commons.exec.LogOutputStream;
+import org.apache.commons.exec.PumpStreamHandler;
+import org.apache.commons.exec.ShutdownHookProcessDestroyer;
 import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.NotImplementedException;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.argeo.slc.SlcException;
-import org.argeo.slc.execution.Executable;
-
-public class SystemCall implements Executable {
-       // TODO: specify environment variables
-
+import org.argeo.slc.core.structure.tree.TreeSPath;
+import org.argeo.slc.core.structure.tree.TreeSRelatedHelper;
+import org.argeo.slc.core.test.SimpleResultPart;
+import org.argeo.slc.structure.StructureAware;
+import org.argeo.slc.test.TestResult;
+import org.argeo.slc.test.TestStatus;
+import org.springframework.core.io.Resource;
+
+/** Execute and OS system call. */
+public class SystemCall extends TreeSRelatedHelper implements Runnable,
+               StructureAware<TreeSPath> {
        private final Log log = LogFactory.getLog(getClass());
 
        private String execDir;
 
        private String cmd = null;
-       private List<String> command = null;
+       private List<Object> command = null;
 
        private Boolean synchronous = true;
-       private Boolean captureStdIn = false;
 
        private String stdErrLogLevel = "ERROR";
        private String stdOutLogLevel = "INFO";
 
-       private Map<String, List<String>> osCommands = new HashMap<String, List<String>>();
+       private Resource stdOutFile = null;
+       private Resource stdErrFile = null;
+
+       private Map<String, List<Object>> osCommands = new HashMap<String, List<Object>>();
        private Map<String, String> osCmds = new HashMap<String, String>();
+       private Map<String, String> environmentVariables = new HashMap<String, String>();
+
+       private Long watchdogTimeout = 24 * 60 * 60 * 1000l;
+
+       private TestResult testResult;
+
+       public SystemCall() {
+
+       }
+
+       public SystemCall(List<Object> command) {
+               super();
+               this.command = command;
+       }
+
+       public void run() {
+               // Log writers
+               final Writer stdOutWriter;
+               final Writer stdErrWriter;
+               if (stdOutFile != null) {
+                       stdOutWriter = createWriter(stdOutFile);
+               } else
+                       stdOutWriter = null;
+               if (stdErrFile != null) {
+                       stdErrWriter = createWriter(stdErrFile);
+               } else {
+                       if (stdOutFile != null) {
+                               stdErrWriter = createWriter(stdOutFile);
+                       } else
+                               stdErrWriter = null;
+               }
 
-       public void execute() {
                try {
                        if (log.isTraceEnabled()) {
                                log.debug("os.name=" + System.getProperty("os.name"));
@@ -48,16 +92,14 @@ public class SystemCall implements Executable {
                        // Execution directory
                        File dir = null;
                        if (execDir != null) {
-                               // Replace '/' by local file separator, for portabiliy
+                               // Replace '/' by local file separator, for portability
                                execDir.replace('/', File.separatorChar);
                                dir = new File(execDir).getCanonicalFile();
                        }
 
-                       Process process = null;
-
                        // Check if an OS specific command overrides
                        String osName = System.getProperty("os.name");
-                       List<String> commandToUse = null;
+                       List<Object> commandToUse = null;
                        if (osCommands.containsKey(osName))
                                commandToUse = osCommands.get(osName);
                        else
@@ -68,6 +110,34 @@ public class SystemCall implements Executable {
                        else
                                cmdToUse = cmd;
 
+                       // Prepare executor
+                       if (dir == null)
+                               dir = new File(getUsedDir(dir));
+                       if (!dir.exists())
+                               dir.mkdirs();
+
+                       Executor executor = new DefaultExecutor();
+                       executor.setWatchdog(new ExecuteWatchdog(watchdogTimeout));
+
+                       PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(
+                                       new LogOutputStream() {
+                                               protected void processLine(String line, int level) {
+                                                       log(stdOutLogLevel, line);
+                                                       if (stdOutWriter != null)
+                                                               appendLineToFile(stdOutWriter, line);
+                                               }
+                                       }, new LogOutputStream() {
+                                               protected void processLine(String line, int level) {
+                                                       log(stdErrLogLevel, line);
+                                                       if (stdErrWriter != null)
+                                                               appendLineToFile(stdErrWriter, line);
+                                               }
+                                       }, null);
+                       executor.setStreamHandler(pumpStreamHandler);
+                       executor.setProcessDestroyer(new ShutdownHookProcessDestroyer());
+                       executor.setWorkingDirectory(dir);
+                       final CommandLine commandLine;
+
                        // Which command definition to use
                        if (commandToUse == null && cmdToUse == null)
                                throw new SlcException("Please specify a command.");
@@ -78,52 +148,77 @@ public class SystemCall implements Executable {
                                if (log.isTraceEnabled())
                                        log.trace("Execute '" + cmdToUse + "' in "
                                                        + getUsedDir(dir));
-                               process = Runtime.getRuntime().exec(cmdToUse, null, dir);
+
+                               commandLine = CommandLine.parse(cmdToUse);
                        } else if (commandToUse != null) {
                                if (log.isTraceEnabled())
                                        log.trace("Execute '" + commandToUse + "' in "
                                                        + getUsedDir(dir));
-                               ProcessBuilder processBuilder = new ProcessBuilder(commandToUse);
-                               processBuilder.directory(dir);
-                               process = processBuilder.start();
+                               if (commandToUse.size() == 0)
+                                       throw new SlcException("Command line is empty.");
+
+                               commandLine = new CommandLine(commandToUse.get(0).toString());
+                               for (int i = 1; i < commandToUse.size(); i++)
+                                       commandLine.addArgument(commandToUse.get(i).toString());
                        } else {
                                // all cases covered previously
+                               throw new NotImplementedException();
                        }
 
-                       // Manage standard streams
-                       StreamReaderThread stdOutThread = new StreamReaderThread(process
-                                       .getInputStream()) {
-                               protected void callback(String line) {
-                                       stdOutCallback(line);
+                       // Env variables
+                       Map<String, String> environmentVariablesToUse = environmentVariables
+                                       .size() > 0 ? environmentVariables : null;
+
+                       // Execute
+                       ExecuteResultHandler executeResultHandler = new ExecuteResultHandler() {
+
+                               public void onProcessComplete(int exitValue) {
+                                       if (log.isDebugEnabled())
+                                               log.debug("Process " + commandLine
+                                                               + " properly completed.");
+                                       if (testResult != null) {
+                                               forwardPath(testResult, null);
+                                               testResult.addResultPart(new SimpleResultPart(
+                                                               TestStatus.PASSED, "Process " + commandLine
+                                                                               + " properly completed."));
+                                       }
                                }
-                       };
-                       stdOutThread.start();
-                       StreamReaderThread stdErrThread = new StreamReaderThread(process
-                                       .getInputStream()) {
-                               protected void callback(String line) {
-                                       stdErrCallback(line);
+
+                               public void onProcessFailed(ExecuteException e) {
+                                       if (testResult != null) {
+                                               forwardPath(testResult, null);
+                                               testResult.addResultPart(new SimpleResultPart(
+                                                               TestStatus.ERROR, "Process " + commandLine
+                                                                               + " failed.", e));
+                                       } else {
+                                               throw new SlcException("Process " + commandLine
+                                                               + " failed.", e);
+                                       }
                                }
                        };
-                       stdErrThread.start();
-                       if (captureStdIn)
-                               new StdInThread(process.getOutputStream()).start();
-
-                       // Wait for the end of the process
-                       if (synchronous) {
-                               Integer exitCode = process.waitFor();
-                               if (exitCode != 0)
-                                       log.warn("Process return exit code " + exitCode);
-                       } else {
-                               // asynchronous: return
-                       }
+
+                       if (synchronous)
+                               try {
+                                       int exitValue = executor.execute(commandLine,
+                                                       environmentVariablesToUse);
+                                       executeResultHandler.onProcessComplete(exitValue);
+                               } catch (ExecuteException e1) {
+                                       executeResultHandler.onProcessFailed(e1);
+                               }
+                       else
+                               executor.execute(commandLine, environmentVariablesToUse,
+                                               executeResultHandler);
                } catch (Exception e) {
                        throw new SlcException("Could not execute command " + cmd, e);
+               } finally {
+                       IOUtils.closeQuietly(stdOutWriter);
+                       IOUtils.closeQuietly(stdErrWriter);
                }
 
        }
 
        /**
-        * Shortcut method returning teh current exec dir if the specified one is
+        * Shortcut method returning the current exec dir if the specified one is
         * null.
         */
        private String getUsedDir(File dir) {
@@ -133,14 +228,6 @@ public class SystemCall implements Executable {
                        return dir.getPath();
        }
 
-       protected void stdOutCallback(String line) {
-               log(stdOutLogLevel, line);
-       }
-
-       protected void stdErrCallback(String line) {
-               log(stdErrLogLevel, line);
-       }
-
        protected void log(String logLevel, String line) {
                if ("ERROR".equals(logLevel))
                        log.error(line);
@@ -156,6 +243,26 @@ public class SystemCall implements Executable {
                        throw new SlcException("Unknown log level " + logLevel);
        }
 
+       protected void appendLineToFile(Writer writer, String line) {
+               try {
+                       writer.append(line).append('\n');
+               } catch (IOException e) {
+                       log.error("Cannot write to log file", e);
+               }
+       }
+
+       protected Writer createWriter(Resource target) {
+               FileWriter writer = null;
+               try {
+                       File file = target.getFile();
+                       writer = new FileWriter(file, true);
+               } catch (IOException e) {
+                       log.error("Cannot create log file " + target, e);
+                       IOUtils.closeQuietly(writer);
+               }
+               return writer;
+       }
+
        public void setCmd(String command) {
                this.cmd = command;
        }
@@ -176,15 +283,11 @@ public class SystemCall implements Executable {
                this.synchronous = synchronous;
        }
 
-       public void setCaptureStdIn(Boolean captureStdIn) {
-               this.captureStdIn = captureStdIn;
-       }
-
-       public void setCommand(List<String> command) {
+       public void setCommand(List<Object> command) {
                this.command = command;
        }
 
-       public void setOsCommands(Map<String, List<String>> osCommands) {
+       public void setOsCommands(Map<String, List<Object>> osCommands) {
                this.osCommands = osCommands;
        }
 
@@ -192,69 +295,24 @@ public class SystemCall implements Executable {
                this.osCmds = osCmds;
        }
 
-       protected abstract class StreamReaderThread extends Thread {
-               private final InputStream stream;
-
-               public StreamReaderThread(InputStream stream) {
-                       this.stream = stream;
-               }
-
-               @Override
-               public void run() {
-                       BufferedReader in = null;
-                       try {
-                               in = new BufferedReader(new InputStreamReader(stream));
-                               String line = null;
-                               while ((line = in.readLine()) != null) {
-                                       stdOutCallback(line);
-                               }
-                       } catch (IOException e) {
-                               if (log.isTraceEnabled()) {
-                                       log.trace("Could not read stream", e);
-                                       // catch silently
-                                       // because the other methods
-                                       // to check whether the stream
-                                       // is closed would probably
-                                       // be to costly
-                               }
-                       } finally {
-                               if (synchronous)
-                                       IOUtils.closeQuietly(in);
-                       }
-               }
-
-               protected abstract void callback(String line);
+       public void setEnvironmentVariables(Map<String, String> environmentVariables) {
+               this.environmentVariables = environmentVariables;
        }
 
-       protected class StdInThread extends Thread {
-               private final OutputStream stream;
+       public void setWatchdogTimeout(Long watchdogTimeout) {
+               this.watchdogTimeout = watchdogTimeout;
+       }
 
-               public StdInThread(OutputStream stream) {
-                       this.stream = stream;
-               }
+       public void setStdOutFile(Resource stdOutFile) {
+               this.stdOutFile = stdOutFile;
+       }
 
-               @Override
-               public void run() {
-                       BufferedReader in = null;
-                       Writer out = null;
-                       try {
-                               out = new OutputStreamWriter(stream);
-                               in = new BufferedReader(new InputStreamReader(System.in));
-                               String line = null;
-                               while ((line = in.readLine()) != null) {
-                                       out.write(line);
-                                       out.write("\n");
-                                       out.flush();
-                               }
-                       } catch (IOException e) {
-                               throw new SlcException("Could not write to stdin stream", e);
-                       } finally {
-                               if (synchronous) {
-                                       IOUtils.closeQuietly(in);
-                                       IOUtils.closeQuietly(out);
-                               }
-                       }
-               }
+       public void setStdErrFile(Resource stdErrFile) {
+               this.stdErrFile = stdErrFile;
+       }
 
+       public void setTestResult(TestResult testResult) {
+               this.testResult = testResult;
        }
+
 }