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
.HashMap
;
12 import java
.util
.List
;
15 import org
.apache
.commons
.exec
.CommandLine
;
16 import org
.apache
.commons
.exec
.DefaultExecutor
;
17 import org
.apache
.commons
.exec
.ExecuteException
;
18 import org
.apache
.commons
.exec
.ExecuteResultHandler
;
19 import org
.apache
.commons
.exec
.ExecuteStreamHandler
;
20 import org
.apache
.commons
.exec
.ExecuteWatchdog
;
21 import org
.apache
.commons
.exec
.Executor
;
22 import org
.apache
.commons
.exec
.LogOutputStream
;
23 import org
.apache
.commons
.exec
.PumpStreamHandler
;
24 import org
.apache
.commons
.exec
.ShutdownHookProcessDestroyer
;
25 import org
.apache
.commons
.io
.FileUtils
;
26 import org
.apache
.commons
.io
.IOUtils
;
27 import org
.apache
.commons
.logging
.Log
;
28 import org
.apache
.commons
.logging
.LogFactory
;
29 import org
.argeo
.slc
.SlcException
;
30 import org
.argeo
.slc
.UnsupportedException
;
31 import org
.argeo
.slc
.core
.execution
.ExecutionResources
;
32 import org
.argeo
.slc
.core
.structure
.tree
.TreeSRelatedHelper
;
33 import org
.argeo
.slc
.core
.test
.SimpleResultPart
;
34 import org
.argeo
.slc
.test
.TestResult
;
35 import org
.argeo
.slc
.test
.TestStatus
;
36 import org
.springframework
.core
.io
.Resource
;
38 /** Execute an OS specific system call. */
39 public class SystemCall
extends TreeSRelatedHelper
implements Runnable
{
40 private final Log log
= LogFactory
.getLog(getClass());
42 private String execDir
;
44 private String cmd
= null;
45 private List
<Object
> command
= null;
47 private Boolean synchronous
= true;
49 private String stdErrLogLevel
= "ERROR";
50 private String stdOutLogLevel
= "INFO";
52 private Resource stdOutFile
= null;
53 private Resource stdErrFile
= null;
54 private Resource stdInFile
= null;
55 private Boolean redirectStdOut
= false;
57 private Map
<String
, List
<Object
>> osCommands
= new HashMap
<String
, List
<Object
>>();
58 private Map
<String
, String
> osCmds
= new HashMap
<String
, String
>();
59 private Map
<String
, String
> environmentVariables
= new HashMap
<String
, String
>();
61 private Boolean logCommand
= false;
62 private Boolean redirectStreams
= true;
63 private Boolean exceptionOnFailed
= true;
64 private Boolean mergeEnvironmentVariables
= true;
66 private String osConsole
= null;
67 private String generateScript
= null;
69 private Long watchdogTimeout
= 24 * 60 * 60 * 1000l;
71 private TestResult testResult
;
73 private ExecutionResources executionResources
;
75 /** Empty constructor */
81 * Constructor based on the provided command list.
86 public SystemCall(List
<Object
> command
) {
87 this.command
= command
;
91 * Constructor based on the provided command.
94 * the command. If the provided string contains no space a
95 * command list is initialized with the argument as first
96 * component (useful for chained construction)
98 public SystemCall(String cmd
) {
99 if (cmd
.indexOf(' ') < 0) {
100 command
= new ArrayList
<Object
>();
107 /** Executes the system call. */
110 Writer stdOutWriter
= null;
111 OutputStream stdOutputStream
= null;
112 Writer stdErrWriter
= null;
113 InputStream stdInStream
= null;
114 if (stdOutFile
!= null)
116 stdOutputStream
= createOutputStream(stdOutFile
);
118 stdOutWriter
= createWriter(stdOutFile
, true);
120 if (stdErrFile
!= null) {
121 stdErrWriter
= createWriter(stdErrFile
, true);
123 if (stdOutFile
!= null && !redirectStdOut
)
124 stdErrWriter
= createWriter(stdOutFile
, true);
127 if (stdInFile
!= null)
129 stdInStream
= stdInFile
.getInputStream();
130 } catch (IOException e2
) {
131 throw new SlcException("Cannot open a stream for " + stdInFile
,
135 if (log
.isTraceEnabled()) {
136 log
.debug("os.name=" + System
.getProperty("os.name"));
137 log
.debug("os.arch=" + System
.getProperty("os.arch"));
138 log
.debug("os.version=" + System
.getProperty("os.version"));
141 // Execution directory
142 File dir
= new File(getExecDirToUse());
146 // Watchdog to check for lost processes
147 Executor executor
= new DefaultExecutor();
148 executor
.setWatchdog(new ExecuteWatchdog(watchdogTimeout
));
150 if (redirectStreams
) {
151 // Redirect standard streams
152 executor
.setStreamHandler(createExecuteStreamHandler(stdOutWriter
,
153 stdOutputStream
, stdErrWriter
, stdInStream
));
155 // Dummy stream handler (otherwise pump is used)
156 executor
.setStreamHandler(new DummyexecuteStreamHandler());
159 executor
.setProcessDestroyer(new ShutdownHookProcessDestroyer());
160 executor
.setWorkingDirectory(dir
);
162 // Command line to use
163 final CommandLine commandLine
= createCommandLine();
165 log
.info("Execute command:\n" + commandLine
166 + "\n in working directory: \n" + dir
+ "\n");
169 Map
<String
, String
> environmentVariablesToUse
= null;
170 if (environmentVariables
.size() > 0) {
171 environmentVariablesToUse
= new HashMap
<String
, String
>();
172 if (mergeEnvironmentVariables
)
173 environmentVariablesToUse
.putAll(System
.getenv());
174 environmentVariablesToUse
.putAll(environmentVariables
);
178 ExecuteResultHandler executeResultHandler
= createExecuteResultHandler(commandLine
);
183 int exitValue
= executor
.execute(commandLine
,
184 environmentVariablesToUse
);
185 executeResultHandler
.onProcessComplete(exitValue
);
186 } catch (ExecuteException e1
) {
187 executeResultHandler
.onProcessFailed(e1
);
190 executor
.execute(commandLine
, environmentVariablesToUse
,
191 executeResultHandler
);
192 } catch (SlcException e
) {
194 } catch (Exception e
) {
195 throw new SlcException("Could not execute command " + commandLine
,
198 IOUtils
.closeQuietly(stdOutWriter
);
199 IOUtils
.closeQuietly(stdErrWriter
);
200 IOUtils
.closeQuietly(stdInStream
);
205 public String
asCommand() {
206 return createCommandLine().toString();
210 public String
toString() {
215 * Build a command line based on the properties. Can be overridden by
216 * specific command wrappers.
218 protected CommandLine
createCommandLine() {
219 // Check if an OS specific command overrides
220 String osName
= System
.getProperty("os.name");
221 List
<Object
> commandToUse
= null;
222 if (osCommands
.containsKey(osName
))
223 commandToUse
= osCommands
.get(osName
);
225 commandToUse
= command
;
226 String cmdToUse
= null;
227 if (osCmds
.containsKey(osName
))
228 cmdToUse
= osCmds
.get(osName
);
232 CommandLine commandLine
= null;
234 // Which command definition to use
235 if (commandToUse
== null && cmdToUse
== null)
236 throw new SlcException("Please specify a command.");
237 else if (commandToUse
!= null && cmdToUse
!= null)
238 throw new SlcException(
239 "Specify the command either as a line or as a list.");
240 else if (cmdToUse
!= null) {
241 commandLine
= CommandLine
.parse(cmdToUse
);
242 } else if (commandToUse
!= null) {
243 if (commandToUse
.size() == 0)
244 throw new SlcException("Command line is empty.");
246 commandLine
= new CommandLine(commandToUse
.get(0).toString());
248 for (int i
= 1; i
< commandToUse
.size(); i
++) {
249 if (log
.isTraceEnabled())
250 log
.debug(commandToUse
.get(i
));
251 commandLine
.addArgument(commandToUse
.get(i
).toString());
254 // all cases covered previously
255 throw new UnsupportedException();
258 if (generateScript
!= null) {
259 File scriptFile
= new File(getExecDirToUse() + File
.separator
262 FileUtils
.writeStringToFile(scriptFile
,
263 (osConsole
!= null ? osConsole
+ " " : "")
264 + commandLine
.toString());
265 } catch (IOException e
) {
266 throw new SlcException("Could not generate script "
269 commandLine
= new CommandLine(scriptFile
);
271 if (osConsole
!= null)
272 commandLine
= CommandLine
.parse(osConsole
+ " "
273 + commandLine
.toString());
280 * Creates a {@link PumpStreamHandler} which redirects streams to the custom
283 protected ExecuteStreamHandler
createExecuteStreamHandler(
284 final Writer stdOutWriter
, final OutputStream stdOutputStream
,
285 final Writer stdErrWriter
, final InputStream stdInStream
) {
289 PumpStreamHandler pumpStreamHandler
= new PumpStreamHandler(
290 stdOutputStream
!= null ? stdOutputStream
291 : new LogOutputStream() {
292 protected void processLine(String line
, int level
) {
293 if (line
!= null && !line
.trim().equals(""))
294 log(stdOutLogLevel
, line
);
295 if (stdOutWriter
!= null)
296 appendLineToFile(stdOutWriter
, line
);
298 }, new LogOutputStream() {
299 protected void processLine(String line
, int level
) {
300 if (line
!= null && !line
.trim().equals(""))
301 log(stdErrLogLevel
, line
);
302 if (stdErrWriter
!= null)
303 appendLineToFile(stdErrWriter
, line
);
306 return pumpStreamHandler
;
309 /** Creates the default {@link ExecuteResultHandler}. */
310 protected ExecuteResultHandler
createExecuteResultHandler(
311 final CommandLine commandLine
) {
312 return new ExecuteResultHandler() {
314 public void onProcessComplete(int exitValue
) {
315 String msg
= "System call '" + commandLine
316 + "' properly completed.";
317 if (log
.isDebugEnabled())
319 if (testResult
!= null) {
320 forwardPath(testResult
, null);
321 testResult
.addResultPart(new SimpleResultPart(
322 TestStatus
.PASSED
, msg
));
326 public void onProcessFailed(ExecuteException e
) {
327 String msg
= "System call '" + commandLine
+ "' failed.";
328 if (testResult
!= null) {
329 forwardPath(testResult
, null);
330 testResult
.addResultPart(new SimpleResultPart(
331 TestStatus
.ERROR
, msg
, e
));
333 if (exceptionOnFailed
)
334 throw new SlcException(msg
, e
);
343 * Shortcut method getting the execDir to use
345 protected String
getExecDirToUse() {
348 if (execDir
!= null) {
349 // Replace '/' by local file separator, for portability
350 execDir
.replace('/', File
.separatorChar
);
351 dir
= new File(execDir
).getCanonicalFile();
355 return System
.getProperty("user.dir");
357 return dir
.getPath();
358 } catch (Exception e
) {
359 throw new SlcException("Cannot find exec dir", e
);
363 /** Log from the underlying streams. */
364 protected void log(String logLevel
, String line
) {
365 if ("ERROR".equals(logLevel
))
367 else if ("WARN".equals(logLevel
))
369 else if ("INFO".equals(logLevel
))
371 else if ("DEBUG".equals(logLevel
))
373 else if ("TRACE".equals(logLevel
))
375 else if ("System.out".equals(logLevel
))
376 System
.out
.println(line
);
377 else if ("System.err".equals(logLevel
))
378 System
.err
.println(line
);
380 throw new SlcException("Unknown log level " + logLevel
);
383 /** Append line to a log file. */
384 protected void appendLineToFile(Writer writer
, String line
) {
386 writer
.append(line
).append('\n');
387 } catch (IOException e
) {
388 log
.error("Cannot write to log file", e
);
392 /** Creates the writer for the output/err files. */
393 protected Writer
createWriter(Resource target
, Boolean append
) {
394 FileWriter writer
= null;
398 if (executionResources
!= null)
399 file
= new File(executionResources
.getAsOsPath(target
, true));
401 file
= target
.getFile();
402 writer
= new FileWriter(file
, append
);
403 } catch (IOException e
) {
404 log
.error("Cannot get file for " + target
, e
);
405 IOUtils
.closeQuietly(writer
);
410 /** Creates an outputstream for the output/err files. */
411 protected OutputStream
createOutputStream(Resource target
) {
412 FileOutputStream OutputStream
= null;
416 if (executionResources
!= null)
417 file
= new File(executionResources
.getAsOsPath(target
, true));
419 file
= target
.getFile();
420 OutputStream
= new FileOutputStream(file
, false);
421 } catch (IOException e
) {
422 log
.error("Cannot get file for " + target
, e
);
423 IOUtils
.closeQuietly(OutputStream
);
428 /** Append the argument (for chaining) */
429 public SystemCall
arg(String arg
) {
434 /** Append the argument (for chaining) */
435 public SystemCall
arg(String arg
, String value
) {
442 public void setCmd(String command
) {
446 public void setCommand(List
<Object
> command
) {
447 this.command
= command
;
450 public void setExecDir(String execdir
) {
451 this.execDir
= execdir
;
454 public void setStdErrLogLevel(String stdErrLogLevel
) {
455 this.stdErrLogLevel
= stdErrLogLevel
;
458 public void setStdOutLogLevel(String stdOutLogLevel
) {
459 this.stdOutLogLevel
= stdOutLogLevel
;
462 public void setSynchronous(Boolean synchronous
) {
463 this.synchronous
= synchronous
;
466 public void setOsCommands(Map
<String
, List
<Object
>> osCommands
) {
467 this.osCommands
= osCommands
;
470 public void setOsCmds(Map
<String
, String
> osCmds
) {
471 this.osCmds
= osCmds
;
474 public void setEnvironmentVariables(Map
<String
, String
> environmentVariables
) {
475 this.environmentVariables
= environmentVariables
;
478 public void setWatchdogTimeout(Long watchdogTimeout
) {
479 this.watchdogTimeout
= watchdogTimeout
;
482 public void setStdOutFile(Resource stdOutFile
) {
483 this.stdOutFile
= stdOutFile
;
486 public void setStdErrFile(Resource stdErrFile
) {
487 this.stdErrFile
= stdErrFile
;
490 public void setStdInFile(Resource stdInFile
) {
491 this.stdInFile
= stdInFile
;
494 public void setTestResult(TestResult testResult
) {
495 this.testResult
= testResult
;
498 public void setLogCommand(Boolean logCommand
) {
499 this.logCommand
= logCommand
;
502 public void setRedirectStreams(Boolean redirectStreams
) {
503 this.redirectStreams
= redirectStreams
;
506 public void setExceptionOnFailed(Boolean exceptionOnFailed
) {
507 this.exceptionOnFailed
= exceptionOnFailed
;
510 public void setMergeEnvironmentVariables(Boolean mergeEnvironmentVariables
) {
511 this.mergeEnvironmentVariables
= mergeEnvironmentVariables
;
514 public void setOsConsole(String osConsole
) {
515 this.osConsole
= osConsole
;
518 public void setGenerateScript(String generateScript
) {
519 this.generateScript
= generateScript
;
522 public void setExecutionResources(ExecutionResources executionResources
) {
523 this.executionResources
= executionResources
;
526 public void setRedirectStdOut(Boolean redirectStdOut
) {
527 this.redirectStdOut
= redirectStdOut
;
530 private class DummyexecuteStreamHandler
implements ExecuteStreamHandler
{
532 public void setProcessErrorStream(InputStream is
) throws IOException
{
535 public void setProcessInputStream(OutputStream os
) throws IOException
{
538 public void setProcessOutputStream(InputStream is
) throws IOException
{
541 public void start() throws IOException
{