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;
53 private Map
<String
, List
<Object
>> osCommands
= new HashMap
<String
, List
<Object
>>();
54 private Map
<String
, String
> osCmds
= new HashMap
<String
, String
>();
55 private Map
<String
, String
> environmentVariables
= new HashMap
<String
, String
>();
57 private Boolean logCommand
= false;
58 private Boolean redirectStreams
= true;
59 private Boolean exceptionOnFailed
= true;
60 private Boolean mergeEnvironmentVariables
= true;
62 private String osConsole
= null;
63 private String generateScript
= null;
65 private Long watchdogTimeout
= 24 * 60 * 60 * 1000l;
67 private TestResult testResult
;
69 /** Empty constructor */
75 * Constructor based on the provided command list.
80 public SystemCall(List
<Object
> command
) {
81 this.command
= command
;
85 * Constructor based on the provided command.
88 * the command. If the provided string contains no space a
89 * command list is initialized with the argument as first
90 * component (useful for chained construction)
92 public SystemCall(String cmd
) {
93 if (cmd
.indexOf(' ') < 0) {
94 command
= new ArrayList
<Object
>();
101 /** Executes the system call. */
103 final Writer stdOutWriter
;
104 final Writer stdErrWriter
;
105 if (stdOutFile
!= null) {
106 stdOutWriter
= createWriter(stdOutFile
);
109 if (stdErrFile
!= null) {
110 stdErrWriter
= createWriter(stdErrFile
);
112 if (stdOutFile
!= null) {
113 stdErrWriter
= createWriter(stdOutFile
);
118 if (log
.isTraceEnabled()) {
119 log
.debug("os.name=" + System
.getProperty("os.name"));
120 log
.debug("os.arch=" + System
.getProperty("os.arch"));
121 log
.debug("os.version=" + System
.getProperty("os.version"));
124 // Execution directory
125 File dir
= new File(getExecDirToUse());
129 // Watchdog to check for lost processes
130 Executor executor
= new DefaultExecutor();
131 executor
.setWatchdog(new ExecuteWatchdog(watchdogTimeout
));
133 if (redirectStreams
) {
134 // Redirect standard streams
135 executor
.setStreamHandler(createExecuteStreamHandler(stdOutWriter
,
138 // Dummy stream handler (otherwise pump is used)
139 executor
.setStreamHandler(new DummyexecuteStreamHandler());
142 executor
.setProcessDestroyer(new ShutdownHookProcessDestroyer());
143 executor
.setWorkingDirectory(dir
);
145 // Command line to use
146 final CommandLine commandLine
= createCommandLine();
148 log
.info("Execute command:\n" + commandLine
149 + "\n in working directory: \n" + dir
+ "\n");
152 Map
<String
, String
> environmentVariablesToUse
= null;
153 if (environmentVariables
.size() > 0) {
154 environmentVariablesToUse
= new HashMap
<String
, String
>();
155 if (mergeEnvironmentVariables
)
156 environmentVariablesToUse
.putAll(System
.getenv());
157 environmentVariablesToUse
.putAll(environmentVariables
);
161 ExecuteResultHandler executeResultHandler
= createExecuteResultHandler(commandLine
);
166 int exitValue
= executor
.execute(commandLine
,
167 environmentVariablesToUse
);
168 executeResultHandler
.onProcessComplete(exitValue
);
169 } catch (ExecuteException e1
) {
170 executeResultHandler
.onProcessFailed(e1
);
173 executor
.execute(commandLine
, environmentVariablesToUse
,
174 executeResultHandler
);
175 } catch (SlcException e
) {
177 } catch (Exception e
) {
178 throw new SlcException("Could not execute command " + commandLine
,
181 IOUtils
.closeQuietly(stdOutWriter
);
182 IOUtils
.closeQuietly(stdErrWriter
);
188 * Build a command line based on the properties. Can be overridden by
189 * specific command wrappers.
191 protected CommandLine
createCommandLine() {
192 // Check if an OS specific command overrides
193 String osName
= System
.getProperty("os.name");
194 List
<Object
> commandToUse
= null;
195 if (osCommands
.containsKey(osName
))
196 commandToUse
= osCommands
.get(osName
);
198 commandToUse
= command
;
199 String cmdToUse
= null;
200 if (osCmds
.containsKey(osName
))
201 cmdToUse
= osCmds
.get(osName
);
205 CommandLine commandLine
= null;
207 // Which command definition to use
208 if (commandToUse
== null && cmdToUse
== null)
209 throw new SlcException("Please specify a command.");
210 else if (commandToUse
!= null && cmdToUse
!= null)
211 throw new SlcException(
212 "Specify the command either as a line or as a list.");
213 else if (cmdToUse
!= null) {
214 commandLine
= CommandLine
.parse(cmdToUse
);
215 } else if (commandToUse
!= null) {
216 if (commandToUse
.size() == 0)
217 throw new SlcException("Command line is empty.");
219 commandLine
= new CommandLine(commandToUse
.get(0).toString());
221 for (int i
= 1; i
< commandToUse
.size(); i
++) {
222 if (log
.isTraceEnabled())
223 log
.debug(commandToUse
.get(i
));
224 commandLine
.addArgument(commandToUse
.get(i
).toString());
227 // all cases covered previously
228 throw new UnsupportedException();
231 if (generateScript
!= null) {
232 File scriptFile
= new File(getExecDirToUse() + File
.separator
235 FileUtils
.writeStringToFile(scriptFile
,
236 (osConsole
!= null ? osConsole
+ " " : "")
237 + commandLine
.toString());
238 } catch (IOException e
) {
239 throw new SlcException("Could not generate script "
242 commandLine
= new CommandLine(scriptFile
);
244 if (osConsole
!= null)
245 commandLine
= CommandLine
.parse(osConsole
+ " "
246 + commandLine
.toString());
253 * Creates a {@link PumpStreamHandler} which redirects streams to the custom
256 protected ExecuteStreamHandler
createExecuteStreamHandler(
257 final Writer stdOutWriter
, final Writer stdErrWriter
) {
260 PumpStreamHandler pumpStreamHandler
= new PumpStreamHandler(
261 new LogOutputStream() {
262 protected void processLine(String line
, int level
) {
263 log(stdOutLogLevel
, line
);
264 if (stdOutWriter
!= null)
265 appendLineToFile(stdOutWriter
, line
);
267 }, new LogOutputStream() {
268 protected void processLine(String line
, int level
) {
269 log(stdErrLogLevel
, line
);
270 if (stdErrWriter
!= null)
271 appendLineToFile(stdErrWriter
, line
);
274 return pumpStreamHandler
;
277 /** Creates the default {@link ExecuteResultHandler}. */
278 protected ExecuteResultHandler
createExecuteResultHandler(
279 final CommandLine commandLine
) {
280 return new ExecuteResultHandler() {
282 public void onProcessComplete(int exitValue
) {
283 if (log
.isDebugEnabled())
285 .debug("Process " + commandLine
286 + " properly completed.");
287 if (testResult
!= null) {
288 forwardPath(testResult
, null);
289 testResult
.addResultPart(new SimpleResultPart(
290 TestStatus
.PASSED
, "Process " + commandLine
291 + " properly completed."));
295 public void onProcessFailed(ExecuteException e
) {
296 String msg
= "Process " + commandLine
+ " failed.";
297 if (testResult
!= null) {
298 forwardPath(testResult
, null);
299 testResult
.addResultPart(new SimpleResultPart(
300 TestStatus
.ERROR
, msg
, e
));
302 if (exceptionOnFailed
)
303 throw new SlcException(msg
, e
);
312 * Shortcut method getting the execDir to use
314 protected String
getExecDirToUse() {
317 if (execDir
!= null) {
318 // Replace '/' by local file separator, for portability
319 execDir
.replace('/', File
.separatorChar
);
320 dir
= new File(execDir
).getCanonicalFile();
324 return System
.getProperty("user.dir");
326 return dir
.getPath();
327 } catch (Exception e
) {
328 throw new SlcException("Cannot find exec dir", e
);
332 /** Log from the underlying streams. */
333 protected void log(String logLevel
, String line
) {
334 if ("ERROR".equals(logLevel
))
336 else if ("WARN".equals(logLevel
))
338 else if ("INFO".equals(logLevel
))
340 else if ("DEBUG".equals(logLevel
))
342 else if ("TRACE".equals(logLevel
))
344 else if ("System.out".equals(logLevel
))
345 System
.out
.println(line
);
346 else if ("System.err".equals(logLevel
))
347 System
.err
.println(line
);
349 throw new SlcException("Unknown log level " + logLevel
);
352 /** Append line to a log file. */
353 protected void appendLineToFile(Writer writer
, String line
) {
355 writer
.append(line
).append('\n');
356 } catch (IOException e
) {
357 log
.error("Cannot write to log file", e
);
361 /** Creates the writer for the log files. */
362 protected Writer
createWriter(Resource target
) {
363 FileWriter writer
= null;
365 File file
= target
.getFile();
366 writer
= new FileWriter(file
, true);
367 } catch (IOException e
) {
368 log
.error("Cannot create log file " + target
, e
);
369 IOUtils
.closeQuietly(writer
);
374 /** Append the argument (for chaining) */
375 public SystemCall
arg(String arg
) {
380 /** Append the argument (for chaining) */
381 public SystemCall
arg(String arg
, String value
) {
388 public void setCmd(String command
) {
392 public void setCommand(List
<Object
> command
) {
393 this.command
= command
;
396 public void setExecDir(String execdir
) {
397 this.execDir
= execdir
;
400 public void setStdErrLogLevel(String stdErrLogLevel
) {
401 this.stdErrLogLevel
= stdErrLogLevel
;
404 public void setStdOutLogLevel(String stdOutLogLevel
) {
405 this.stdOutLogLevel
= stdOutLogLevel
;
408 public void setSynchronous(Boolean synchronous
) {
409 this.synchronous
= synchronous
;
412 public void setOsCommands(Map
<String
, List
<Object
>> osCommands
) {
413 this.osCommands
= osCommands
;
416 public void setOsCmds(Map
<String
, String
> osCmds
) {
417 this.osCmds
= osCmds
;
420 public void setEnvironmentVariables(Map
<String
, String
> environmentVariables
) {
421 this.environmentVariables
= environmentVariables
;
424 public void setWatchdogTimeout(Long watchdogTimeout
) {
425 this.watchdogTimeout
= watchdogTimeout
;
428 public void setStdOutFile(Resource stdOutFile
) {
429 this.stdOutFile
= stdOutFile
;
432 public void setStdErrFile(Resource stdErrFile
) {
433 this.stdErrFile
= stdErrFile
;
436 public void setTestResult(TestResult testResult
) {
437 this.testResult
= testResult
;
440 public void setLogCommand(Boolean logCommand
) {
441 this.logCommand
= logCommand
;
444 public void setRedirectStreams(Boolean redirectStreams
) {
445 this.redirectStreams
= redirectStreams
;
448 public void setExceptionOnFailed(Boolean exceptionOnFailed
) {
449 this.exceptionOnFailed
= exceptionOnFailed
;
452 public void setMergeEnvironmentVariables(Boolean mergeEnvironmentVariables
) {
453 this.mergeEnvironmentVariables
= mergeEnvironmentVariables
;
456 public void setOsConsole(String osConsole
) {
457 this.osConsole
= osConsole
;
460 public void setGenerateScript(String generateScript
) {
461 this.generateScript
= generateScript
;
464 private class DummyexecuteStreamHandler
implements ExecuteStreamHandler
{
466 public void setProcessErrorStream(InputStream is
) throws IOException
{
469 public void setProcessInputStream(OutputStream os
) throws IOException
{
472 public void setProcessOutputStream(InputStream is
) throws IOException
{
475 public void start() throws IOException
{