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
);
206 * Build a command line based on the properties. Can be overridden by
207 * specific command wrappers.
209 protected CommandLine
createCommandLine() {
210 // Check if an OS specific command overrides
211 String osName
= System
.getProperty("os.name");
212 List
<Object
> commandToUse
= null;
213 if (osCommands
.containsKey(osName
))
214 commandToUse
= osCommands
.get(osName
);
216 commandToUse
= command
;
217 String cmdToUse
= null;
218 if (osCmds
.containsKey(osName
))
219 cmdToUse
= osCmds
.get(osName
);
223 CommandLine commandLine
= null;
225 // Which command definition to use
226 if (commandToUse
== null && cmdToUse
== null)
227 throw new SlcException("Please specify a command.");
228 else if (commandToUse
!= null && cmdToUse
!= null)
229 throw new SlcException(
230 "Specify the command either as a line or as a list.");
231 else if (cmdToUse
!= null) {
232 commandLine
= CommandLine
.parse(cmdToUse
);
233 } else if (commandToUse
!= null) {
234 if (commandToUse
.size() == 0)
235 throw new SlcException("Command line is empty.");
237 commandLine
= new CommandLine(commandToUse
.get(0).toString());
239 for (int i
= 1; i
< commandToUse
.size(); i
++) {
240 if (log
.isTraceEnabled())
241 log
.debug(commandToUse
.get(i
));
242 commandLine
.addArgument(commandToUse
.get(i
).toString());
245 // all cases covered previously
246 throw new UnsupportedException();
249 if (generateScript
!= null) {
250 File scriptFile
= new File(getExecDirToUse() + File
.separator
253 FileUtils
.writeStringToFile(scriptFile
,
254 (osConsole
!= null ? osConsole
+ " " : "")
255 + commandLine
.toString());
256 } catch (IOException e
) {
257 throw new SlcException("Could not generate script "
260 commandLine
= new CommandLine(scriptFile
);
262 if (osConsole
!= null)
263 commandLine
= CommandLine
.parse(osConsole
+ " "
264 + commandLine
.toString());
271 * Creates a {@link PumpStreamHandler} which redirects streams to the custom
274 protected ExecuteStreamHandler
createExecuteStreamHandler(
275 final Writer stdOutWriter
, final OutputStream stdOutputStream
,
276 final Writer stdErrWriter
, final InputStream stdInStream
) {
280 PumpStreamHandler pumpStreamHandler
= new PumpStreamHandler(
281 stdOutputStream
!= null ? stdOutputStream
282 : new LogOutputStream() {
283 protected void processLine(String line
, int level
) {
284 log(stdOutLogLevel
, line
);
285 if (stdOutWriter
!= null)
286 appendLineToFile(stdOutWriter
, line
);
288 }, new LogOutputStream() {
289 protected void processLine(String line
, int level
) {
290 log(stdErrLogLevel
, line
);
291 if (stdErrWriter
!= null)
292 appendLineToFile(stdErrWriter
, line
);
295 return pumpStreamHandler
;
298 /** Creates the default {@link ExecuteResultHandler}. */
299 protected ExecuteResultHandler
createExecuteResultHandler(
300 final CommandLine commandLine
) {
301 return new ExecuteResultHandler() {
303 public void onProcessComplete(int exitValue
) {
304 if (log
.isDebugEnabled())
306 .debug("Process " + commandLine
307 + " properly completed.");
308 if (testResult
!= null) {
309 forwardPath(testResult
, null);
310 testResult
.addResultPart(new SimpleResultPart(
311 TestStatus
.PASSED
, "Process " + commandLine
312 + " properly completed."));
316 public void onProcessFailed(ExecuteException e
) {
317 String msg
= "Process " + commandLine
+ " failed.";
318 if (testResult
!= null) {
319 forwardPath(testResult
, null);
320 testResult
.addResultPart(new SimpleResultPart(
321 TestStatus
.ERROR
, msg
, e
));
323 if (exceptionOnFailed
)
324 throw new SlcException(msg
, e
);
333 * Shortcut method getting the execDir to use
335 protected String
getExecDirToUse() {
338 if (execDir
!= null) {
339 // Replace '/' by local file separator, for portability
340 execDir
.replace('/', File
.separatorChar
);
341 dir
= new File(execDir
).getCanonicalFile();
345 return System
.getProperty("user.dir");
347 return dir
.getPath();
348 } catch (Exception e
) {
349 throw new SlcException("Cannot find exec dir", e
);
353 /** Log from the underlying streams. */
354 protected void log(String logLevel
, String line
) {
355 if ("ERROR".equals(logLevel
))
357 else if ("WARN".equals(logLevel
))
359 else if ("INFO".equals(logLevel
))
361 else if ("DEBUG".equals(logLevel
))
363 else if ("TRACE".equals(logLevel
))
365 else if ("System.out".equals(logLevel
))
366 System
.out
.println(line
);
367 else if ("System.err".equals(logLevel
))
368 System
.err
.println(line
);
370 throw new SlcException("Unknown log level " + logLevel
);
373 /** Append line to a log file. */
374 protected void appendLineToFile(Writer writer
, String line
) {
376 writer
.append(line
).append('\n');
377 } catch (IOException e
) {
378 log
.error("Cannot write to log file", e
);
382 /** Creates the writer for the output/err files. */
383 protected Writer
createWriter(Resource target
, Boolean append
) {
384 FileWriter writer
= null;
388 if (executionResources
!= null)
389 file
= new File(executionResources
.getAsOsPath(target
, true));
391 file
= target
.getFile();
392 writer
= new FileWriter(file
, append
);
393 } catch (IOException e
) {
394 log
.error("Cannot get file for " + target
, e
);
395 IOUtils
.closeQuietly(writer
);
400 /** Creates an outputstream for the output/err files. */
401 protected OutputStream
createOutputStream(Resource target
) {
402 FileOutputStream OutputStream
= null;
406 if (executionResources
!= null)
407 file
= new File(executionResources
.getAsOsPath(target
, true));
409 file
= target
.getFile();
410 OutputStream
= new FileOutputStream(file
, false);
411 } catch (IOException e
) {
412 log
.error("Cannot get file for " + target
, e
);
413 IOUtils
.closeQuietly(OutputStream
);
418 /** Append the argument (for chaining) */
419 public SystemCall
arg(String arg
) {
424 /** Append the argument (for chaining) */
425 public SystemCall
arg(String arg
, String value
) {
432 public void setCmd(String command
) {
436 public void setCommand(List
<Object
> command
) {
437 this.command
= command
;
440 public void setExecDir(String execdir
) {
441 this.execDir
= execdir
;
444 public void setStdErrLogLevel(String stdErrLogLevel
) {
445 this.stdErrLogLevel
= stdErrLogLevel
;
448 public void setStdOutLogLevel(String stdOutLogLevel
) {
449 this.stdOutLogLevel
= stdOutLogLevel
;
452 public void setSynchronous(Boolean synchronous
) {
453 this.synchronous
= synchronous
;
456 public void setOsCommands(Map
<String
, List
<Object
>> osCommands
) {
457 this.osCommands
= osCommands
;
460 public void setOsCmds(Map
<String
, String
> osCmds
) {
461 this.osCmds
= osCmds
;
464 public void setEnvironmentVariables(Map
<String
, String
> environmentVariables
) {
465 this.environmentVariables
= environmentVariables
;
468 public void setWatchdogTimeout(Long watchdogTimeout
) {
469 this.watchdogTimeout
= watchdogTimeout
;
472 public void setStdOutFile(Resource stdOutFile
) {
473 this.stdOutFile
= stdOutFile
;
476 public void setStdErrFile(Resource stdErrFile
) {
477 this.stdErrFile
= stdErrFile
;
480 public void setStdInFile(Resource stdInFile
) {
481 this.stdInFile
= stdInFile
;
484 public void setTestResult(TestResult testResult
) {
485 this.testResult
= testResult
;
488 public void setLogCommand(Boolean logCommand
) {
489 this.logCommand
= logCommand
;
492 public void setRedirectStreams(Boolean redirectStreams
) {
493 this.redirectStreams
= redirectStreams
;
496 public void setExceptionOnFailed(Boolean exceptionOnFailed
) {
497 this.exceptionOnFailed
= exceptionOnFailed
;
500 public void setMergeEnvironmentVariables(Boolean mergeEnvironmentVariables
) {
501 this.mergeEnvironmentVariables
= mergeEnvironmentVariables
;
504 public void setOsConsole(String osConsole
) {
505 this.osConsole
= osConsole
;
508 public void setGenerateScript(String generateScript
) {
509 this.generateScript
= generateScript
;
512 public void setExecutionResources(ExecutionResources executionResources
) {
513 this.executionResources
= executionResources
;
516 public void setRedirectStdOut(Boolean redirectStdOut
) {
517 this.redirectStdOut
= redirectStdOut
;
520 private class DummyexecuteStreamHandler
implements ExecuteStreamHandler
{
522 public void setProcessErrorStream(InputStream is
) throws IOException
{
525 public void setProcessInputStream(OutputStream os
) throws IOException
{
528 public void setProcessOutputStream(InputStream is
) throws IOException
{
531 public void start() throws IOException
{