1 package org
.argeo
.slc
.core
.execution
.tasks
;
4 import java
.io
.FileWriter
;
5 import java
.io
.IOException
;
6 import java
.io
.InputStream
;
7 import java
.io
.OutputStream
;
9 import java
.util
.ArrayList
;
10 import java
.util
.HashMap
;
11 import java
.util
.List
;
14 import org
.apache
.commons
.exec
.CommandLine
;
15 import org
.apache
.commons
.exec
.DefaultExecutor
;
16 import org
.apache
.commons
.exec
.ExecuteException
;
17 import org
.apache
.commons
.exec
.ExecuteResultHandler
;
18 import org
.apache
.commons
.exec
.ExecuteStreamHandler
;
19 import org
.apache
.commons
.exec
.ExecuteWatchdog
;
20 import org
.apache
.commons
.exec
.Executor
;
21 import org
.apache
.commons
.exec
.LogOutputStream
;
22 import org
.apache
.commons
.exec
.PumpStreamHandler
;
23 import org
.apache
.commons
.exec
.ShutdownHookProcessDestroyer
;
24 import org
.apache
.commons
.io
.FileUtils
;
25 import org
.apache
.commons
.io
.IOUtils
;
26 import org
.apache
.commons
.logging
.Log
;
27 import org
.apache
.commons
.logging
.LogFactory
;
28 import org
.argeo
.slc
.SlcException
;
29 import org
.argeo
.slc
.UnsupportedException
;
30 import org
.argeo
.slc
.core
.structure
.tree
.TreeSRelatedHelper
;
31 import org
.argeo
.slc
.core
.test
.SimpleResultPart
;
32 import org
.argeo
.slc
.test
.TestResult
;
33 import org
.argeo
.slc
.test
.TestStatus
;
34 import org
.springframework
.core
.io
.Resource
;
36 /** Execute an OS specific system call. */
37 public class SystemCall
extends TreeSRelatedHelper
implements Runnable
{
38 private final Log log
= LogFactory
.getLog(getClass());
40 private String execDir
;
42 private String cmd
= null;
43 private List
<Object
> command
= null;
45 private Boolean synchronous
= true;
47 private String stdErrLogLevel
= "ERROR";
48 private String stdOutLogLevel
= "INFO";
50 private Resource stdOutFile
= null;
51 private Resource stdErrFile
= null;
52 private Resource stdInFile
= null;
54 private Map
<String
, List
<Object
>> osCommands
= new HashMap
<String
, List
<Object
>>();
55 private Map
<String
, String
> osCmds
= new HashMap
<String
, String
>();
56 private Map
<String
, String
> environmentVariables
= new HashMap
<String
, String
>();
58 private Boolean logCommand
= false;
59 private Boolean redirectStreams
= true;
60 private Boolean exceptionOnFailed
= true;
61 private Boolean mergeEnvironmentVariables
= true;
63 private String osConsole
= null;
64 private String generateScript
= null;
66 private Long watchdogTimeout
= 24 * 60 * 60 * 1000l;
68 private TestResult testResult
;
70 /** Empty constructor */
76 * Constructor based on the provided command list.
81 public SystemCall(List
<Object
> command
) {
82 this.command
= command
;
86 * Constructor based on the provided command.
89 * the command. If the provided string contains no space a
90 * command list is initialized with the argument as first
91 * component (useful for chained construction)
93 public SystemCall(String cmd
) {
94 if (cmd
.indexOf(' ') < 0) {
95 command
= new ArrayList
<Object
>();
102 /** Executes the system call. */
105 final Writer stdOutWriter
;
106 final Writer stdErrWriter
;
107 final InputStream stdInStream
;
108 if (stdOutFile
!= null) {
109 stdOutWriter
= createWriter(stdOutFile
);
114 if (stdErrFile
!= null) {
115 stdErrWriter
= createWriter(stdErrFile
);
117 if (stdOutFile
!= null) {
118 stdErrWriter
= createWriter(stdOutFile
);
124 if (stdInFile
!= null)
126 stdInStream
= stdInFile
.getInputStream();
127 } catch (IOException e2
) {
128 throw new SlcException("Cannot open a stream for " + stdInFile
,
134 if (log
.isTraceEnabled()) {
135 log
.debug("os.name=" + System
.getProperty("os.name"));
136 log
.debug("os.arch=" + System
.getProperty("os.arch"));
137 log
.debug("os.version=" + System
.getProperty("os.version"));
140 // Execution directory
141 File dir
= new File(getExecDirToUse());
145 // Watchdog to check for lost processes
146 Executor executor
= new DefaultExecutor();
147 executor
.setWatchdog(new ExecuteWatchdog(watchdogTimeout
));
149 if (redirectStreams
) {
150 // Redirect standard streams
151 executor
.setStreamHandler(createExecuteStreamHandler(stdOutWriter
,
152 stdErrWriter
, stdInStream
));
154 // Dummy stream handler (otherwise pump is used)
155 executor
.setStreamHandler(new DummyexecuteStreamHandler());
158 executor
.setProcessDestroyer(new ShutdownHookProcessDestroyer());
159 executor
.setWorkingDirectory(dir
);
161 // Command line to use
162 final CommandLine commandLine
= createCommandLine();
164 log
.info("Execute command:\n" + commandLine
165 + "\n in working directory: \n" + dir
+ "\n");
168 Map
<String
, String
> environmentVariablesToUse
= null;
169 if (environmentVariables
.size() > 0) {
170 environmentVariablesToUse
= new HashMap
<String
, String
>();
171 if (mergeEnvironmentVariables
)
172 environmentVariablesToUse
.putAll(System
.getenv());
173 environmentVariablesToUse
.putAll(environmentVariables
);
177 ExecuteResultHandler executeResultHandler
= createExecuteResultHandler(commandLine
);
182 int exitValue
= executor
.execute(commandLine
,
183 environmentVariablesToUse
);
184 executeResultHandler
.onProcessComplete(exitValue
);
185 } catch (ExecuteException e1
) {
186 executeResultHandler
.onProcessFailed(e1
);
189 executor
.execute(commandLine
, environmentVariablesToUse
,
190 executeResultHandler
);
191 } catch (SlcException e
) {
193 } catch (Exception e
) {
194 throw new SlcException("Could not execute command " + commandLine
,
197 IOUtils
.closeQuietly(stdOutWriter
);
198 IOUtils
.closeQuietly(stdErrWriter
);
199 IOUtils
.closeQuietly(stdInStream
);
205 * Build a command line based on the properties. Can be overridden by
206 * specific command wrappers.
208 protected CommandLine
createCommandLine() {
209 // Check if an OS specific command overrides
210 String osName
= System
.getProperty("os.name");
211 List
<Object
> commandToUse
= null;
212 if (osCommands
.containsKey(osName
))
213 commandToUse
= osCommands
.get(osName
);
215 commandToUse
= command
;
216 String cmdToUse
= null;
217 if (osCmds
.containsKey(osName
))
218 cmdToUse
= osCmds
.get(osName
);
222 CommandLine commandLine
= null;
224 // Which command definition to use
225 if (commandToUse
== null && cmdToUse
== null)
226 throw new SlcException("Please specify a command.");
227 else if (commandToUse
!= null && cmdToUse
!= null)
228 throw new SlcException(
229 "Specify the command either as a line or as a list.");
230 else if (cmdToUse
!= null) {
231 commandLine
= CommandLine
.parse(cmdToUse
);
232 } else if (commandToUse
!= null) {
233 if (commandToUse
.size() == 0)
234 throw new SlcException("Command line is empty.");
236 commandLine
= new CommandLine(commandToUse
.get(0).toString());
238 for (int i
= 1; i
< commandToUse
.size(); i
++) {
239 if (log
.isTraceEnabled())
240 log
.debug(commandToUse
.get(i
));
241 commandLine
.addArgument(commandToUse
.get(i
).toString());
244 // all cases covered previously
245 throw new UnsupportedException();
248 if (generateScript
!= null) {
249 File scriptFile
= new File(getExecDirToUse() + File
.separator
252 FileUtils
.writeStringToFile(scriptFile
,
253 (osConsole
!= null ? osConsole
+ " " : "")
254 + commandLine
.toString());
255 } catch (IOException e
) {
256 throw new SlcException("Could not generate script "
259 commandLine
= new CommandLine(scriptFile
);
261 if (osConsole
!= null)
262 commandLine
= CommandLine
.parse(osConsole
+ " "
263 + commandLine
.toString());
270 * Creates a {@link PumpStreamHandler} which redirects streams to the custom
273 protected ExecuteStreamHandler
createExecuteStreamHandler(
274 final Writer stdOutWriter
, final Writer stdErrWriter
,
275 final InputStream stdInStream
) {
279 PumpStreamHandler pumpStreamHandler
= new PumpStreamHandler(
280 new LogOutputStream() {
281 protected void processLine(String line
, int level
) {
282 log(stdOutLogLevel
, line
);
283 if (stdOutWriter
!= null)
284 appendLineToFile(stdOutWriter
, line
);
286 }, new LogOutputStream() {
287 protected void processLine(String line
, int level
) {
288 log(stdErrLogLevel
, line
);
289 if (stdErrWriter
!= null)
290 appendLineToFile(stdErrWriter
, line
);
293 return pumpStreamHandler
;
296 /** Creates the default {@link ExecuteResultHandler}. */
297 protected ExecuteResultHandler
createExecuteResultHandler(
298 final CommandLine commandLine
) {
299 return new ExecuteResultHandler() {
301 public void onProcessComplete(int exitValue
) {
302 if (log
.isDebugEnabled())
304 .debug("Process " + commandLine
305 + " properly completed.");
306 if (testResult
!= null) {
307 forwardPath(testResult
, null);
308 testResult
.addResultPart(new SimpleResultPart(
309 TestStatus
.PASSED
, "Process " + commandLine
310 + " properly completed."));
314 public void onProcessFailed(ExecuteException e
) {
315 String msg
= "Process " + commandLine
+ " failed.";
316 if (testResult
!= null) {
317 forwardPath(testResult
, null);
318 testResult
.addResultPart(new SimpleResultPart(
319 TestStatus
.ERROR
, msg
, e
));
321 if (exceptionOnFailed
)
322 throw new SlcException(msg
, e
);
331 * Shortcut method getting the execDir to use
333 protected String
getExecDirToUse() {
336 if (execDir
!= null) {
337 // Replace '/' by local file separator, for portability
338 execDir
.replace('/', File
.separatorChar
);
339 dir
= new File(execDir
).getCanonicalFile();
343 return System
.getProperty("user.dir");
345 return dir
.getPath();
346 } catch (Exception e
) {
347 throw new SlcException("Cannot find exec dir", e
);
351 /** Log from the underlying streams. */
352 protected void log(String logLevel
, String line
) {
353 if ("ERROR".equals(logLevel
))
355 else if ("WARN".equals(logLevel
))
357 else if ("INFO".equals(logLevel
))
359 else if ("DEBUG".equals(logLevel
))
361 else if ("TRACE".equals(logLevel
))
363 else if ("System.out".equals(logLevel
))
364 System
.out
.println(line
);
365 else if ("System.err".equals(logLevel
))
366 System
.err
.println(line
);
368 throw new SlcException("Unknown log level " + logLevel
);
371 /** Append line to a log file. */
372 protected void appendLineToFile(Writer writer
, String line
) {
374 writer
.append(line
).append('\n');
375 } catch (IOException e
) {
376 log
.error("Cannot write to log file", e
);
380 /** Creates the writer for the log files. */
381 protected Writer
createWriter(Resource target
) {
382 FileWriter writer
= null;
384 File file
= target
.getFile();
385 writer
= new FileWriter(file
, true);
386 } catch (IOException e
) {
387 log
.error("Cannot create log file " + target
, e
);
388 IOUtils
.closeQuietly(writer
);
393 /** Append the argument (for chaining) */
394 public SystemCall
arg(String arg
) {
399 /** Append the argument (for chaining) */
400 public SystemCall
arg(String arg
, String value
) {
407 public void setCmd(String command
) {
411 public void setCommand(List
<Object
> command
) {
412 this.command
= command
;
415 public void setExecDir(String execdir
) {
416 this.execDir
= execdir
;
419 public void setStdErrLogLevel(String stdErrLogLevel
) {
420 this.stdErrLogLevel
= stdErrLogLevel
;
423 public void setStdOutLogLevel(String stdOutLogLevel
) {
424 this.stdOutLogLevel
= stdOutLogLevel
;
427 public void setSynchronous(Boolean synchronous
) {
428 this.synchronous
= synchronous
;
431 public void setOsCommands(Map
<String
, List
<Object
>> osCommands
) {
432 this.osCommands
= osCommands
;
435 public void setOsCmds(Map
<String
, String
> osCmds
) {
436 this.osCmds
= osCmds
;
439 public void setEnvironmentVariables(Map
<String
, String
> environmentVariables
) {
440 this.environmentVariables
= environmentVariables
;
443 public void setWatchdogTimeout(Long watchdogTimeout
) {
444 this.watchdogTimeout
= watchdogTimeout
;
447 public void setStdOutFile(Resource stdOutFile
) {
448 this.stdOutFile
= stdOutFile
;
451 public void setStdErrFile(Resource stdErrFile
) {
452 this.stdErrFile
= stdErrFile
;
455 public void setStdInFile(Resource stdInFile
) {
456 this.stdInFile
= stdInFile
;
459 public void setTestResult(TestResult testResult
) {
460 this.testResult
= testResult
;
463 public void setLogCommand(Boolean logCommand
) {
464 this.logCommand
= logCommand
;
467 public void setRedirectStreams(Boolean redirectStreams
) {
468 this.redirectStreams
= redirectStreams
;
471 public void setExceptionOnFailed(Boolean exceptionOnFailed
) {
472 this.exceptionOnFailed
= exceptionOnFailed
;
475 public void setMergeEnvironmentVariables(Boolean mergeEnvironmentVariables
) {
476 this.mergeEnvironmentVariables
= mergeEnvironmentVariables
;
479 public void setOsConsole(String osConsole
) {
480 this.osConsole
= osConsole
;
483 public void setGenerateScript(String generateScript
) {
484 this.generateScript
= generateScript
;
487 private class DummyexecuteStreamHandler
implements ExecuteStreamHandler
{
489 public void setProcessErrorStream(InputStream is
) throws IOException
{
492 public void setProcessInputStream(OutputStream os
) throws IOException
{
495 public void setProcessOutputStream(InputStream is
) throws IOException
{
498 public void start() throws IOException
{