X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;f=runtime%2Forg.argeo.slc.core%2Fsrc%2Fmain%2Fjava%2Forg%2Fargeo%2Fslc%2Fcore%2Fexecution%2Ftasks%2FSystemCall.java;h=b631276176c492fbc5181d460ed93d5f006d01c7;hb=60677ee743e6d54ceda3187824cef28cf844ccc0;hp=e5df71ab1ff291de369a271c6e75650888f606b9;hpb=c092f2c4564f7238f2960bc005c5a2202732f44e;p=gpl%2Fargeo-slc.git diff --git a/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/tasks/SystemCall.java b/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/tasks/SystemCall.java index e5df71ab1..b63127617 100644 --- a/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/tasks/SystemCall.java +++ b/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/tasks/SystemCall.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2010 Mathieu Baudier + * + * Licensed 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. + */ + package org.argeo.slc.core.execution.tasks; import java.io.File; @@ -6,8 +22,11 @@ import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; import java.io.Writer; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -29,14 +48,15 @@ import org.apache.commons.logging.LogFactory; import org.argeo.slc.SlcException; import org.argeo.slc.UnsupportedException; import org.argeo.slc.core.execution.ExecutionResources; -import org.argeo.slc.core.structure.tree.TreeSRelatedHelper; import org.argeo.slc.core.test.SimpleResultPart; import org.argeo.slc.test.TestResult; import org.argeo.slc.test.TestStatus; import org.springframework.core.io.Resource; /** Execute an OS specific system call. */ -public class SystemCall extends TreeSRelatedHelper implements Runnable { +public class SystemCall implements Runnable { + public final static String LOG_STDOUT = "System.out"; + private final Log log = LogFactory.getLog(getClass()); private String execDir; @@ -44,6 +64,7 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { private String cmd = null; private List command = null; + private Executor executor = new DefaultExecutor(); private Boolean synchronous = true; private String stdErrLogLevel = "ERROR"; @@ -51,9 +72,19 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { private Resource stdOutFile = null; private Resource stdErrFile = null; + private Resource stdInFile = null; + /** + * If no {@link #stdInFile} provided, writing to this stream will write to + * the stdin of the process. + */ + private OutputStream stdInSink = null; + private Boolean redirectStdOut = false; + private List outputListeners = Collections + .synchronizedList(new ArrayList()); + private Map> osCommands = new HashMap>(); private Map osCmds = new HashMap(); private Map environmentVariables = new HashMap(); @@ -66,6 +97,7 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { private String osConsole = null; private String generateScript = null; + /** 24 hours */ private Long watchdogTimeout = 24 * 60 * 60 * 1000l; private TestResult testResult; @@ -124,13 +156,17 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { stdErrWriter = createWriter(stdOutFile, true); } - if (stdInFile != null) - try { + try { + if (stdInFile != null) stdInStream = stdInFile.getInputStream(); - } catch (IOException e2) { - throw new SlcException("Cannot open a stream for " + stdInFile, - e2); + else { + stdInStream = new PipedInputStream(); + stdInSink = new PipedOutputStream( + (PipedInputStream) stdInStream); } + } catch (IOException e2) { + throw new SlcException("Cannot open a stream for " + stdInFile, e2); + } if (log.isTraceEnabled()) { log.debug("os.name=" + System.getProperty("os.name")); @@ -140,24 +176,28 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { // Execution directory File dir = new File(getExecDirToUse()); - if (!dir.exists()) - dir.mkdirs(); + // if (!dir.exists()) + // dir.mkdirs(); // Watchdog to check for lost processes - Executor executor = new DefaultExecutor(); - executor.setWatchdog(new ExecuteWatchdog(watchdogTimeout)); + Executor executorToUse; + if (executor != null) + executorToUse = executor; + else + executorToUse = new DefaultExecutor(); + executorToUse.setWatchdog(new ExecuteWatchdog(watchdogTimeout)); if (redirectStreams) { // Redirect standard streams - executor.setStreamHandler(createExecuteStreamHandler(stdOutWriter, - stdOutputStream, stdErrWriter, stdInStream)); + executorToUse.setStreamHandler(createExecuteStreamHandler( + stdOutWriter, stdOutputStream, stdErrWriter, stdInStream)); } else { // Dummy stream handler (otherwise pump is used) - executor.setStreamHandler(new DummyexecuteStreamHandler()); + executorToUse.setStreamHandler(new DummyexecuteStreamHandler()); } - executor.setProcessDestroyer(new ShutdownHookProcessDestroyer()); - executor.setWorkingDirectory(dir); + executorToUse.setProcessDestroyer(new ShutdownHookProcessDestroyer()); + executorToUse.setWorkingDirectory(dir); // Command line to use final CommandLine commandLine = createCommandLine(); @@ -167,27 +207,33 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { // Env variables Map environmentVariablesToUse = null; - if (environmentVariables.size() > 0) { - environmentVariablesToUse = new HashMap(); - if (mergeEnvironmentVariables) - environmentVariablesToUse.putAll(System.getenv()); + environmentVariablesToUse = new HashMap(); + if (mergeEnvironmentVariables) + environmentVariablesToUse.putAll(System.getenv()); + if (environmentVariables.size() > 0) environmentVariablesToUse.putAll(environmentVariables); - } // Execute ExecuteResultHandler executeResultHandler = createExecuteResultHandler(commandLine); + // + // THE EXECUTION PROPER + // try { if (synchronous) try { - int exitValue = executor.execute(commandLine, + int exitValue = executorToUse.execute(commandLine, environmentVariablesToUse); executeResultHandler.onProcessComplete(exitValue); } catch (ExecuteException e1) { + if (e1.getExitValue() == Executor.INVALID_EXITVALUE) { + Thread.currentThread().interrupt(); + return; + } executeResultHandler.onProcessFailed(e1); } else - executor.execute(commandLine, environmentVariablesToUse, + executorToUse.execute(commandLine, environmentVariablesToUse, executeResultHandler); } catch (SlcException e) { throw e; @@ -198,10 +244,35 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { IOUtils.closeQuietly(stdOutWriter); IOUtils.closeQuietly(stdErrWriter); IOUtils.closeQuietly(stdInStream); + IOUtils.closeQuietly(stdInSink); } } + public synchronized String function() { + final StringBuffer buf = new StringBuffer(""); + SystemCallOutputListener tempOutputListener = new SystemCallOutputListener() { + public void newLine(SystemCall systemCall, String line, + Boolean isError) { + if (!isError) + buf.append(line); + } + }; + addOutputListener(tempOutputListener); + run(); + removeOutputListener(tempOutputListener); + return buf.toString(); + } + + public String asCommand() { + return createCommandLine().toString(); + } + + @Override + public String toString() { + return asCommand(); + } + /** * Build a command line based on the properties. Can be overridden by * specific command wrappers. @@ -282,18 +353,28 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { : new LogOutputStream() { protected void processLine(String line, int level) { if (line != null && !line.trim().equals("")) - log(stdOutLogLevel, line); + logStdOut(line); if (stdOutWriter != null) appendLineToFile(stdOutWriter, line); } }, new LogOutputStream() { protected void processLine(String line, int level) { if (line != null && !line.trim().equals("")) - log(stdErrLogLevel, line); + logStdErr(line); if (stdErrWriter != null) appendLineToFile(stdErrWriter, line); } - }, stdInStream); + }, stdInStream) { + + @Override + public void stop() { + // prevents the method to block when joining stdin + if (stdInSink != null) + IOUtils.closeQuietly(stdInSink); + + super.stop(); + } + }; return pumpStreamHandler; } @@ -305,10 +386,10 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { public void onProcessComplete(int exitValue) { String msg = "System call '" + commandLine + "' properly completed."; - if (log.isDebugEnabled()) - log.debug(msg); + if (log.isTraceEnabled()) + log.trace(msg); if (testResult != null) { - forwardPath(testResult, null); + forwardPath(testResult); testResult.addResultPart(new SimpleResultPart( TestStatus.PASSED, msg)); } @@ -317,7 +398,7 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { public void onProcessFailed(ExecuteException e) { String msg = "System call '" + commandLine + "' failed."; if (testResult != null) { - forwardPath(testResult, null); + forwardPath(testResult); testResult.addResultPart(new SimpleResultPart( TestStatus.ERROR, msg, e)); } else { @@ -330,27 +411,36 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { }; } + protected void forwardPath(TestResult testResult) { + // TODO: allocate a TreeSPath + } + /** * Shortcut method getting the execDir to use */ protected String getExecDirToUse() { try { - File dir = null; if (execDir != null) { - // Replace '/' by local file separator, for portability - execDir.replace('/', File.separatorChar); - dir = new File(execDir).getCanonicalFile(); + return execDir; } - - if (dir == null) - return System.getProperty("user.dir"); - else - return dir.getPath(); + return System.getProperty("user.dir"); } catch (Exception e) { throw new SlcException("Cannot find exec dir", e); } } + protected void logStdOut(String line) { + for (SystemCallOutputListener outputListener : outputListeners) + outputListener.newLine(this, line, false); + log(stdOutLogLevel, line); + } + + protected void logStdErr(String line) { + for (SystemCallOutputListener outputListener : outputListeners) + outputListener.newLine(this, line, true); + log(stdErrLogLevel, line); + } + /** Log from the underlying streams. */ protected void log(String logLevel, String line) { if ("ERROR".equals(logLevel)) @@ -363,7 +453,7 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { log.debug(line); else if ("TRACE".equals(logLevel)) log.trace(line); - else if ("System.out".equals(logLevel)) + else if (LOG_STDOUT.equals(logLevel)) System.out.println(line); else if ("System.err".equals(logLevel)) System.err.println(line); @@ -400,7 +490,7 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { /** Creates an outputstream for the output/err files. */ protected OutputStream createOutputStream(Resource target) { - FileOutputStream OutputStream = null; + FileOutputStream out = null; try { final File file; @@ -408,22 +498,26 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { file = new File(executionResources.getAsOsPath(target, true)); else file = target.getFile(); - OutputStream = new FileOutputStream(file, false); + out = new FileOutputStream(file, false); } catch (IOException e) { log.error("Cannot get file for " + target, e); - IOUtils.closeQuietly(OutputStream); + IOUtils.closeQuietly(out); } - return OutputStream; + return out; } /** Append the argument (for chaining) */ public SystemCall arg(String arg) { + if (command == null) + command = new ArrayList(); command.add(arg); return this; } /** Append the argument (for chaining) */ public SystemCall arg(String arg, String value) { + if (command == null) + command = new ArrayList(); command.add(arg); command.add(value); return this; @@ -518,6 +612,23 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { this.redirectStdOut = redirectStdOut; } + public void addOutputListener(SystemCallOutputListener outputListener) { + outputListeners.add(outputListener); + } + + public void removeOutputListener(SystemCallOutputListener outputListener) { + outputListeners.remove(outputListener); + } + + public void setOutputListeners( + List outputListeners) { + this.outputListeners = outputListeners; + } + + public void setExecutor(Executor executor) { + this.executor = executor; + } + private class DummyexecuteStreamHandler implements ExecuteStreamHandler { public void setProcessErrorStream(InputStream is) throws IOException { @@ -536,5 +647,4 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { } } - }