1 package org
.argeo
.slc
.core
.execution
.tasks
;
4 import java
.io
.FileOutputStream
;
5 import java
.io
.FileWriter
;
6 import java
.io
.IOException
;
7 import java
.io
.InputStream
;
8 import java
.io
.OutputStream
;
10 import java
.util
.ArrayList
;
11 import java
.util
.Collections
;
12 import java
.util
.HashMap
;
13 import java
.util
.List
;
16 import org
.apache
.commons
.exec
.CommandLine
;
17 import org
.apache
.commons
.exec
.DefaultExecutor
;
18 import org
.apache
.commons
.exec
.ExecuteException
;
19 import org
.apache
.commons
.exec
.ExecuteResultHandler
;
20 import org
.apache
.commons
.exec
.ExecuteStreamHandler
;
21 import org
.apache
.commons
.exec
.ExecuteWatchdog
;
22 import org
.apache
.commons
.exec
.Executor
;
23 import org
.apache
.commons
.exec
.LogOutputStream
;
24 import org
.apache
.commons
.exec
.PumpStreamHandler
;
25 import org
.apache
.commons
.exec
.ShutdownHookProcessDestroyer
;
26 import org
.apache
.commons
.io
.FileUtils
;
27 import org
.apache
.commons
.io
.IOUtils
;
28 import org
.apache
.commons
.logging
.Log
;
29 import org
.apache
.commons
.logging
.LogFactory
;
30 import org
.argeo
.slc
.SlcException
;
31 import org
.argeo
.slc
.UnsupportedException
;
32 import org
.argeo
.slc
.core
.execution
.ExecutionResources
;
33 import org
.argeo
.slc
.core
.structure
.tree
.TreeSRelatedHelper
;
34 import org
.argeo
.slc
.core
.test
.SimpleResultPart
;
35 import org
.argeo
.slc
.test
.TestResult
;
36 import org
.argeo
.slc
.test
.TestStatus
;
37 import org
.springframework
.core
.io
.Resource
;
39 /** Execute an OS specific system call. */
40 public class SystemCall
extends TreeSRelatedHelper
implements Runnable
{
41 public final static String LOG_STDOUT
= "System.out";
43 private final Log log
= LogFactory
.getLog(getClass());
45 private String execDir
;
47 private String cmd
= null;
48 private List
<Object
> command
= null;
50 private Boolean synchronous
= true;
52 private String stdErrLogLevel
= "ERROR";
53 private String stdOutLogLevel
= "INFO";
55 private Resource stdOutFile
= null;
56 private Resource stdErrFile
= null;
57 private Resource stdInFile
= null;
58 private Boolean redirectStdOut
= false;
60 private List
<SystemCallOutputListener
> outputListeners
= Collections
61 .synchronizedList(new ArrayList
<SystemCallOutputListener
>());
63 private Map
<String
, List
<Object
>> osCommands
= new HashMap
<String
, List
<Object
>>();
64 private Map
<String
, String
> osCmds
= new HashMap
<String
, String
>();
65 private Map
<String
, String
> environmentVariables
= new HashMap
<String
, String
>();
67 private Boolean logCommand
= false;
68 private Boolean redirectStreams
= true;
69 private Boolean exceptionOnFailed
= true;
70 private Boolean mergeEnvironmentVariables
= true;
72 private String osConsole
= null;
73 private String generateScript
= null;
75 private Long watchdogTimeout
= 24 * 60 * 60 * 1000l;
77 private TestResult testResult
;
79 private ExecutionResources executionResources
;
81 /** Empty constructor */
87 * Constructor based on the provided command list.
92 public SystemCall(List
<Object
> command
) {
93 this.command
= command
;
97 * Constructor based on the provided command.
100 * the command. If the provided string contains no space a
101 * command list is initialized with the argument as first
102 * component (useful for chained construction)
104 public SystemCall(String cmd
) {
105 if (cmd
.indexOf(' ') < 0) {
106 command
= new ArrayList
<Object
>();
113 /** Executes the system call. */
116 Writer stdOutWriter
= null;
117 OutputStream stdOutputStream
= null;
118 Writer stdErrWriter
= null;
119 InputStream stdInStream
= null;
120 if (stdOutFile
!= null)
122 stdOutputStream
= createOutputStream(stdOutFile
);
124 stdOutWriter
= createWriter(stdOutFile
, true);
126 if (stdErrFile
!= null) {
127 stdErrWriter
= createWriter(stdErrFile
, true);
129 if (stdOutFile
!= null && !redirectStdOut
)
130 stdErrWriter
= createWriter(stdOutFile
, true);
133 if (stdInFile
!= null)
135 stdInStream
= stdInFile
.getInputStream();
136 } catch (IOException e2
) {
137 throw new SlcException("Cannot open a stream for " + stdInFile
,
141 if (log
.isTraceEnabled()) {
142 log
.debug("os.name=" + System
.getProperty("os.name"));
143 log
.debug("os.arch=" + System
.getProperty("os.arch"));
144 log
.debug("os.version=" + System
.getProperty("os.version"));
147 // Execution directory
148 File dir
= new File(getExecDirToUse());
152 // Watchdog to check for lost processes
153 Executor executor
= new DefaultExecutor();
154 executor
.setWatchdog(new ExecuteWatchdog(watchdogTimeout
));
156 if (redirectStreams
) {
157 // Redirect standard streams
158 executor
.setStreamHandler(createExecuteStreamHandler(stdOutWriter
,
159 stdOutputStream
, stdErrWriter
, stdInStream
));
161 // Dummy stream handler (otherwise pump is used)
162 executor
.setStreamHandler(new DummyexecuteStreamHandler());
165 executor
.setProcessDestroyer(new ShutdownHookProcessDestroyer());
166 executor
.setWorkingDirectory(dir
);
168 // Command line to use
169 final CommandLine commandLine
= createCommandLine();
171 log
.info("Execute command:\n" + commandLine
172 + "\n in working directory: \n" + dir
+ "\n");
175 Map
<String
, String
> environmentVariablesToUse
= null;
176 if (environmentVariables
.size() > 0) {
177 environmentVariablesToUse
= new HashMap
<String
, String
>();
178 if (mergeEnvironmentVariables
)
179 environmentVariablesToUse
.putAll(System
.getenv());
180 environmentVariablesToUse
.putAll(environmentVariables
);
184 ExecuteResultHandler executeResultHandler
= createExecuteResultHandler(commandLine
);
187 // THE EXECUTION PROPER
192 int exitValue
= executor
.execute(commandLine
,
193 environmentVariablesToUse
);
194 executeResultHandler
.onProcessComplete(exitValue
);
195 } catch (ExecuteException e1
) {
196 executeResultHandler
.onProcessFailed(e1
);
199 executor
.execute(commandLine
, environmentVariablesToUse
,
200 executeResultHandler
);
201 } catch (SlcException e
) {
203 } catch (Exception e
) {
204 throw new SlcException("Could not execute command " + commandLine
,
207 IOUtils
.closeQuietly(stdOutWriter
);
208 IOUtils
.closeQuietly(stdErrWriter
);
209 IOUtils
.closeQuietly(stdInStream
);
214 public synchronized String
function() {
215 final StringBuffer buf
= new StringBuffer("");
216 SystemCallOutputListener tempOutputListener
= new SystemCallOutputListener() {
217 public void newLine(SystemCall systemCall
, String line
,
223 addOutputListener(tempOutputListener
);
225 removeOutputListener(tempOutputListener
);
226 return buf
.toString();
229 public String
asCommand() {
230 return createCommandLine().toString();
234 public String
toString() {
239 * Build a command line based on the properties. Can be overridden by
240 * specific command wrappers.
242 protected CommandLine
createCommandLine() {
243 // Check if an OS specific command overrides
244 String osName
= System
.getProperty("os.name");
245 List
<Object
> commandToUse
= null;
246 if (osCommands
.containsKey(osName
))
247 commandToUse
= osCommands
.get(osName
);
249 commandToUse
= command
;
250 String cmdToUse
= null;
251 if (osCmds
.containsKey(osName
))
252 cmdToUse
= osCmds
.get(osName
);
256 CommandLine commandLine
= null;
258 // Which command definition to use
259 if (commandToUse
== null && cmdToUse
== null)
260 throw new SlcException("Please specify a command.");
261 else if (commandToUse
!= null && cmdToUse
!= null)
262 throw new SlcException(
263 "Specify the command either as a line or as a list.");
264 else if (cmdToUse
!= null) {
265 commandLine
= CommandLine
.parse(cmdToUse
);
266 } else if (commandToUse
!= null) {
267 if (commandToUse
.size() == 0)
268 throw new SlcException("Command line is empty.");
270 commandLine
= new CommandLine(commandToUse
.get(0).toString());
272 for (int i
= 1; i
< commandToUse
.size(); i
++) {
273 if (log
.isTraceEnabled())
274 log
.debug(commandToUse
.get(i
));
275 commandLine
.addArgument(commandToUse
.get(i
).toString());
278 // all cases covered previously
279 throw new UnsupportedException();
282 if (generateScript
!= null) {
283 File scriptFile
= new File(getExecDirToUse() + File
.separator
286 FileUtils
.writeStringToFile(scriptFile
,
287 (osConsole
!= null ? osConsole
+ " " : "")
288 + commandLine
.toString());
289 } catch (IOException e
) {
290 throw new SlcException("Could not generate script "
293 commandLine
= new CommandLine(scriptFile
);
295 if (osConsole
!= null)
296 commandLine
= CommandLine
.parse(osConsole
+ " "
297 + commandLine
.toString());
304 * Creates a {@link PumpStreamHandler} which redirects streams to the custom
307 protected ExecuteStreamHandler
createExecuteStreamHandler(
308 final Writer stdOutWriter
, final OutputStream stdOutputStream
,
309 final Writer stdErrWriter
, final InputStream stdInStream
) {
313 PumpStreamHandler pumpStreamHandler
= new PumpStreamHandler(
314 stdOutputStream
!= null ? stdOutputStream
315 : new LogOutputStream() {
316 protected void processLine(String line
, int level
) {
317 if (line
!= null && !line
.trim().equals(""))
319 if (stdOutWriter
!= null)
320 appendLineToFile(stdOutWriter
, line
);
322 }, new LogOutputStream() {
323 protected void processLine(String line
, int level
) {
324 if (line
!= null && !line
.trim().equals(""))
326 if (stdErrWriter
!= null)
327 appendLineToFile(stdErrWriter
, line
);
330 return pumpStreamHandler
;
333 /** Creates the default {@link ExecuteResultHandler}. */
334 protected ExecuteResultHandler
createExecuteResultHandler(
335 final CommandLine commandLine
) {
336 return new ExecuteResultHandler() {
338 public void onProcessComplete(int exitValue
) {
339 String msg
= "System call '" + commandLine
340 + "' properly completed.";
341 if (log
.isTraceEnabled())
343 if (testResult
!= null) {
344 forwardPath(testResult
, null);
345 testResult
.addResultPart(new SimpleResultPart(
346 TestStatus
.PASSED
, msg
));
350 public void onProcessFailed(ExecuteException e
) {
351 String msg
= "System call '" + commandLine
+ "' failed.";
352 if (testResult
!= null) {
353 forwardPath(testResult
, null);
354 testResult
.addResultPart(new SimpleResultPart(
355 TestStatus
.ERROR
, msg
, e
));
357 if (exceptionOnFailed
)
358 throw new SlcException(msg
, e
);
367 * Shortcut method getting the execDir to use
369 protected String
getExecDirToUse() {
372 if (execDir
!= null) {
373 // Replace '/' by local file separator, for portability
374 execDir
.replace('/', File
.separatorChar
);
375 dir
= new File(execDir
).getCanonicalFile();
379 return System
.getProperty("user.dir");
381 return dir
.getPath();
382 } catch (Exception e
) {
383 throw new SlcException("Cannot find exec dir", e
);
387 protected void logStdOut(String line
) {
388 for (SystemCallOutputListener outputListener
: outputListeners
)
389 outputListener
.newLine(this, line
, false);
390 log(stdOutLogLevel
, line
);
393 protected void logStdErr(String line
) {
394 for (SystemCallOutputListener outputListener
: outputListeners
)
395 outputListener
.newLine(this, line
, true);
396 log(stdErrLogLevel
, line
);
399 /** Log from the underlying streams. */
400 protected void log(String logLevel
, String line
) {
401 if ("ERROR".equals(logLevel
))
403 else if ("WARN".equals(logLevel
))
405 else if ("INFO".equals(logLevel
))
407 else if ("DEBUG".equals(logLevel
))
409 else if ("TRACE".equals(logLevel
))
411 else if (LOG_STDOUT
.equals(logLevel
))
412 System
.out
.println(line
);
413 else if ("System.err".equals(logLevel
))
414 System
.err
.println(line
);
416 throw new SlcException("Unknown log level " + logLevel
);
419 /** Append line to a log file. */
420 protected void appendLineToFile(Writer writer
, String line
) {
422 writer
.append(line
).append('\n');
423 } catch (IOException e
) {
424 log
.error("Cannot write to log file", e
);
428 /** Creates the writer for the output/err files. */
429 protected Writer
createWriter(Resource target
, Boolean append
) {
430 FileWriter writer
= null;
434 if (executionResources
!= null)
435 file
= new File(executionResources
.getAsOsPath(target
, true));
437 file
= target
.getFile();
438 writer
= new FileWriter(file
, append
);
439 } catch (IOException e
) {
440 log
.error("Cannot get file for " + target
, e
);
441 IOUtils
.closeQuietly(writer
);
446 /** Creates an outputstream for the output/err files. */
447 protected OutputStream
createOutputStream(Resource target
) {
448 FileOutputStream out
= null;
452 if (executionResources
!= null)
453 file
= new File(executionResources
.getAsOsPath(target
, true));
455 file
= target
.getFile();
456 out
= new FileOutputStream(file
, false);
457 } catch (IOException e
) {
458 log
.error("Cannot get file for " + target
, e
);
459 IOUtils
.closeQuietly(out
);
464 /** Append the argument (for chaining) */
465 public SystemCall
arg(String arg
) {
470 /** Append the argument (for chaining) */
471 public SystemCall
arg(String arg
, String value
) {
478 public void setCmd(String command
) {
482 public void setCommand(List
<Object
> command
) {
483 this.command
= command
;
486 public void setExecDir(String execdir
) {
487 this.execDir
= execdir
;
490 public void setStdErrLogLevel(String stdErrLogLevel
) {
491 this.stdErrLogLevel
= stdErrLogLevel
;
494 public void setStdOutLogLevel(String stdOutLogLevel
) {
495 this.stdOutLogLevel
= stdOutLogLevel
;
498 public void setSynchronous(Boolean synchronous
) {
499 this.synchronous
= synchronous
;
502 public void setOsCommands(Map
<String
, List
<Object
>> osCommands
) {
503 this.osCommands
= osCommands
;
506 public void setOsCmds(Map
<String
, String
> osCmds
) {
507 this.osCmds
= osCmds
;
510 public void setEnvironmentVariables(Map
<String
, String
> environmentVariables
) {
511 this.environmentVariables
= environmentVariables
;
514 public void setWatchdogTimeout(Long watchdogTimeout
) {
515 this.watchdogTimeout
= watchdogTimeout
;
518 public void setStdOutFile(Resource stdOutFile
) {
519 this.stdOutFile
= stdOutFile
;
522 public void setStdErrFile(Resource stdErrFile
) {
523 this.stdErrFile
= stdErrFile
;
526 public void setStdInFile(Resource stdInFile
) {
527 this.stdInFile
= stdInFile
;
530 public void setTestResult(TestResult testResult
) {
531 this.testResult
= testResult
;
534 public void setLogCommand(Boolean logCommand
) {
535 this.logCommand
= logCommand
;
538 public void setRedirectStreams(Boolean redirectStreams
) {
539 this.redirectStreams
= redirectStreams
;
542 public void setExceptionOnFailed(Boolean exceptionOnFailed
) {
543 this.exceptionOnFailed
= exceptionOnFailed
;
546 public void setMergeEnvironmentVariables(Boolean mergeEnvironmentVariables
) {
547 this.mergeEnvironmentVariables
= mergeEnvironmentVariables
;
550 public void setOsConsole(String osConsole
) {
551 this.osConsole
= osConsole
;
554 public void setGenerateScript(String generateScript
) {
555 this.generateScript
= generateScript
;
558 public void setExecutionResources(ExecutionResources executionResources
) {
559 this.executionResources
= executionResources
;
562 public void setRedirectStdOut(Boolean redirectStdOut
) {
563 this.redirectStdOut
= redirectStdOut
;
566 public void addOutputListener(SystemCallOutputListener outputListener
) {
567 outputListeners
.add(outputListener
);
570 public void removeOutputListener(SystemCallOutputListener outputListener
) {
571 outputListeners
.remove(outputListener
);
574 public void setOutputListeners(
575 List
<SystemCallOutputListener
> outputListeners
) {
576 this.outputListeners
= outputListeners
;
579 private class DummyexecuteStreamHandler
implements ExecuteStreamHandler
{
581 public void setProcessErrorStream(InputStream is
) throws IOException
{
584 public void setProcessInputStream(OutputStream os
) throws IOException
{
587 public void setProcessOutputStream(InputStream is
) throws IOException
{
590 public void start() throws IOException
{