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=5046abb0a3bd2f47d178488ab777fe5dbd356829;hb=e05000e1e8d950dfb2b25984b171105510c6ab38;hp=8b0f3d8e80f324f726c82bac8a4ee39778bb75b4;hpb=dad97a87545102becea1beb153baa677a2dcd4d8;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 8b0f3d8e8..5046abb0a 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,12 +1,30 @@ +/* + * 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; +import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Writer; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -27,14 +45,16 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.argeo.slc.SlcException; import org.argeo.slc.UnsupportedException; -import org.argeo.slc.core.structure.tree.TreeSRelatedHelper; +import org.argeo.slc.core.execution.ExecutionResources; 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; @@ -49,6 +69,11 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { private Resource stdOutFile = null; private Resource stdErrFile = null; + private Resource stdInFile = null; + private Boolean redirectStdOut = false; + + private List outputListeners = Collections + .synchronizedList(new ArrayList()); private Map> osCommands = new HashMap>(); private Map osCmds = new HashMap(); @@ -66,6 +91,8 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { private TestResult testResult; + private ExecutionResources executionResources; + /** Empty constructor */ public SystemCall() { @@ -100,21 +127,32 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { /** Executes the system call. */ public void run() { - final Writer stdOutWriter; - final Writer stdErrWriter; - if (stdOutFile != null) { - stdOutWriter = createWriter(stdOutFile); - } else - stdOutWriter = null; + // Manage streams + Writer stdOutWriter = null; + OutputStream stdOutputStream = null; + Writer stdErrWriter = null; + InputStream stdInStream = null; + if (stdOutFile != null) + if (redirectStdOut) + stdOutputStream = createOutputStream(stdOutFile); + else + stdOutWriter = createWriter(stdOutFile, true); + if (stdErrFile != null) { - stdErrWriter = createWriter(stdErrFile); + stdErrWriter = createWriter(stdErrFile, true); } else { - if (stdOutFile != null) { - stdErrWriter = createWriter(stdOutFile); - } else - stdErrWriter = null; + if (stdOutFile != null && !redirectStdOut) + stdErrWriter = createWriter(stdOutFile, true); } + if (stdInFile != null) + try { + stdInStream = stdInFile.getInputStream(); + } catch (IOException e2) { + throw new SlcException("Cannot open a stream for " + stdInFile, + e2); + } + if (log.isTraceEnabled()) { log.debug("os.name=" + System.getProperty("os.name")); log.debug("os.arch=" + System.getProperty("os.arch")); @@ -133,7 +171,7 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { if (redirectStreams) { // Redirect standard streams executor.setStreamHandler(createExecuteStreamHandler(stdOutWriter, - stdErrWriter)); + stdOutputStream, stdErrWriter, stdInStream)); } else { // Dummy stream handler (otherwise pump is used) executor.setStreamHandler(new DummyexecuteStreamHandler()); @@ -160,6 +198,9 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { // Execute ExecuteResultHandler executeResultHandler = createExecuteResultHandler(commandLine); + // + // THE EXECUTION PROPER + // try { if (synchronous) try { @@ -167,6 +208,10 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { environmentVariablesToUse); executeResultHandler.onProcessComplete(exitValue); } catch (ExecuteException e1) { + if (e1.getExitValue() == Executor.INVALID_EXITVALUE) { + Thread.currentThread().interrupt(); + return; + } executeResultHandler.onProcessFailed(e1); } else @@ -180,10 +225,35 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { } finally { IOUtils.closeQuietly(stdOutWriter); IOUtils.closeQuietly(stdErrWriter); + IOUtils.closeQuietly(stdInStream); } } + 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. @@ -254,23 +324,28 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { * logging mechanism. */ protected ExecuteStreamHandler createExecuteStreamHandler( - final Writer stdOutWriter, final Writer stdErrWriter) { + final Writer stdOutWriter, final OutputStream stdOutputStream, + final Writer stdErrWriter, final InputStream stdInStream) { + // Log writers PumpStreamHandler pumpStreamHandler = new PumpStreamHandler( - new LogOutputStream() { + stdOutputStream != null ? stdOutputStream + : new LogOutputStream() { + protected void processLine(String line, int level) { + if (line != null && !line.trim().equals("")) + logStdOut(line); + if (stdOutWriter != null) + appendLineToFile(stdOutWriter, line); + } + }, 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 (line != null && !line.trim().equals("")) + logStdErr(line); if (stdErrWriter != null) appendLineToFile(stdErrWriter, line); } - }, null); + }, stdInStream); return pumpStreamHandler; } @@ -280,22 +355,21 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { return new ExecuteResultHandler() { public void onProcessComplete(int exitValue) { - if (log.isDebugEnabled()) - log - .debug("Process " + commandLine - + " properly completed."); + String msg = "System call '" + commandLine + + "' properly completed."; + if (log.isTraceEnabled()) + log.trace(msg); if (testResult != null) { - forwardPath(testResult, null); + forwardPath(testResult); testResult.addResultPart(new SimpleResultPart( - TestStatus.PASSED, "Process " + commandLine - + " properly completed.")); + TestStatus.PASSED, msg)); } } public void onProcessFailed(ExecuteException e) { - String msg = "Process " + commandLine + " failed."; + String msg = "System call '" + commandLine + "' failed."; if (testResult != null) { - forwardPath(testResult, null); + forwardPath(testResult); testResult.addResultPart(new SimpleResultPart( TestStatus.ERROR, msg, e)); } else { @@ -308,6 +382,10 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { }; } + protected void forwardPath(TestResult testResult) { + // TODO: allocate a TreeSPath + } + /** * Shortcut method getting the execDir to use */ @@ -329,6 +407,18 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { } } + 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)) @@ -341,7 +431,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); @@ -358,27 +448,54 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { } } - /** Creates the writer for the log files. */ - protected Writer createWriter(Resource target) { + /** Creates the writer for the output/err files. */ + protected Writer createWriter(Resource target, Boolean append) { FileWriter writer = null; try { - File file = target.getFile(); - writer = new FileWriter(file, true); + + final File file; + if (executionResources != null) + file = new File(executionResources.getAsOsPath(target, true)); + else + file = target.getFile(); + writer = new FileWriter(file, append); } catch (IOException e) { - log.error("Cannot create log file " + target, e); + log.error("Cannot get file for " + target, e); IOUtils.closeQuietly(writer); } return writer; } + /** Creates an outputstream for the output/err files. */ + protected OutputStream createOutputStream(Resource target) { + FileOutputStream out = null; + try { + + final File file; + if (executionResources != null) + file = new File(executionResources.getAsOsPath(target, true)); + else + file = target.getFile(); + out = new FileOutputStream(file, false); + } catch (IOException e) { + log.error("Cannot get file for " + target, e); + IOUtils.closeQuietly(out); + } + 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; @@ -433,6 +550,10 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { this.stdErrFile = stdErrFile; } + public void setStdInFile(Resource stdInFile) { + this.stdInFile = stdInFile; + } + public void setTestResult(TestResult testResult) { this.testResult = testResult; } @@ -461,6 +582,27 @@ public class SystemCall extends TreeSRelatedHelper implements Runnable { this.generateScript = generateScript; } + public void setExecutionResources(ExecutionResources executionResources) { + this.executionResources = executionResources; + } + + public void setRedirectStdOut(Boolean redirectStdOut) { + 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; + } + private class DummyexecuteStreamHandler implements ExecuteStreamHandler { public void setProcessErrorStream(InputStream is) throws IOException {