]> git.argeo.org Git - gpl/argeo-slc.git/blob - runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/tasks/SystemCall.java
4d7e5d05dbc8013d7b8d829787bec8cc0b7f3ded
[gpl/argeo-slc.git] / runtime / org.argeo.slc.core / src / main / java / org / argeo / slc / core / execution / tasks / SystemCall.java
1 package org.argeo.slc.core.execution.tasks;
2
3 import java.io.File;
4 import java.io.FileWriter;
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.io.OutputStream;
8 import java.io.Writer;
9 import java.util.HashMap;
10 import java.util.List;
11 import java.util.Map;
12
13 import org.apache.commons.exec.CommandLine;
14 import org.apache.commons.exec.DefaultExecutor;
15 import org.apache.commons.exec.ExecuteException;
16 import org.apache.commons.exec.ExecuteResultHandler;
17 import org.apache.commons.exec.ExecuteStreamHandler;
18 import org.apache.commons.exec.ExecuteWatchdog;
19 import org.apache.commons.exec.Executor;
20 import org.apache.commons.exec.LogOutputStream;
21 import org.apache.commons.exec.PumpStreamHandler;
22 import org.apache.commons.exec.ShutdownHookProcessDestroyer;
23 import org.apache.commons.io.FileUtils;
24 import org.apache.commons.io.IOUtils;
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27 import org.argeo.slc.SlcException;
28 import org.argeo.slc.UnsupportedException;
29 import org.argeo.slc.core.structure.tree.TreeSPath;
30 import org.argeo.slc.core.structure.tree.TreeSRelatedHelper;
31 import org.argeo.slc.core.test.SimpleResultPart;
32 import org.argeo.slc.structure.StructureAware;
33 import org.argeo.slc.test.TestResult;
34 import org.argeo.slc.test.TestStatus;
35 import org.springframework.core.io.Resource;
36
37 /** Execute and OS system call. */
38 public class SystemCall extends TreeSRelatedHelper implements Runnable,
39 StructureAware<TreeSPath> {
40 private final Log log = LogFactory.getLog(getClass());
41
42 private String execDir;
43
44 private String cmd = null;
45 private List<Object> command = null;
46
47 private Boolean synchronous = true;
48
49 private String stdErrLogLevel = "ERROR";
50 private String stdOutLogLevel = "INFO";
51
52 private Resource stdOutFile = null;
53 private Resource stdErrFile = null;
54
55 private Map<String, List<Object>> osCommands = new HashMap<String, List<Object>>();
56 private Map<String, String> osCmds = new HashMap<String, String>();
57 private Map<String, String> environmentVariables = new HashMap<String, String>();
58
59 private Boolean logCommand = false;
60 private Boolean redirectStreams = true;
61 private String osConsole = null;
62 private String generateScript = null;
63
64 private Long watchdogTimeout = 24 * 60 * 60 * 1000l;
65
66 private TestResult testResult;
67
68 // Internal use
69
70 public SystemCall() {
71
72 }
73
74 public SystemCall(List<Object> command) {
75 super();
76 this.command = command;
77 }
78
79 public void run() {
80 final Writer stdOutWriter;
81 final Writer stdErrWriter;
82 if (stdOutFile != null) {
83 stdOutWriter = createWriter(stdOutFile);
84 } else
85 stdOutWriter = null;
86 if (stdErrFile != null) {
87 stdErrWriter = createWriter(stdErrFile);
88 } else {
89 if (stdOutFile != null) {
90 stdErrWriter = createWriter(stdOutFile);
91 } else
92 stdErrWriter = null;
93 }
94
95 try {
96 if (log.isTraceEnabled()) {
97 log.debug("os.name=" + System.getProperty("os.name"));
98 log.debug("os.arch=" + System.getProperty("os.arch"));
99 log.debug("os.version=" + System.getProperty("os.version"));
100 }
101
102 // Execution directory
103 File dir = new File(getExecDirToUse());
104 if (!dir.exists())
105 dir.mkdirs();
106
107 // Watchdog to check for lost processes
108 Executor executor = new DefaultExecutor();
109 executor.setWatchdog(new ExecuteWatchdog(watchdogTimeout));
110
111 if (redirectStreams) {
112 // Redirect standard streams
113 executor.setStreamHandler(createExecuteStreamHandler(
114 stdOutWriter, stdErrWriter));
115 } else {
116 // Dummy stream handler (otherwise pump is used)
117 executor.setStreamHandler(new DummyexecuteStreamHandler());
118 }
119
120 executor.setProcessDestroyer(new ShutdownHookProcessDestroyer());
121 executor.setWorkingDirectory(dir);
122
123 // Command line to use
124 final CommandLine commandLine = createCommandLine();
125 if (logCommand)
126 log.info("Execute command:\n" + commandLine + "\n");
127
128 // Env variables
129 Map<String, String> environmentVariablesToUse = environmentVariables
130 .size() > 0 ? environmentVariables : null;
131
132 // Execute
133 ExecuteResultHandler executeResultHandler = createExecuteResultHandler(commandLine);
134
135 if (synchronous)
136 try {
137 int exitValue = executor.execute(commandLine,
138 environmentVariablesToUse);
139 executeResultHandler.onProcessComplete(exitValue);
140 } catch (ExecuteException e1) {
141 executeResultHandler.onProcessFailed(e1);
142 }
143 else
144 executor.execute(commandLine, environmentVariablesToUse,
145 executeResultHandler);
146 } catch (Exception e) {
147 throw new SlcException("Could not execute command " + cmd, e);
148 } finally {
149 IOUtils.closeQuietly(stdOutWriter);
150 IOUtils.closeQuietly(stdErrWriter);
151 }
152
153 }
154
155 /** Can be overridden by specific command wrapper */
156 protected CommandLine createCommandLine() {
157 // Check if an OS specific command overrides
158 String osName = System.getProperty("os.name");
159 List<Object> commandToUse = null;
160 if (osCommands.containsKey(osName))
161 commandToUse = osCommands.get(osName);
162 else
163 commandToUse = command;
164 String cmdToUse = null;
165 if (osCmds.containsKey(osName))
166 cmdToUse = osCmds.get(osName);
167 else
168 cmdToUse = cmd;
169
170 CommandLine commandLine = null;
171
172 // Which command definition to use
173 if (commandToUse == null && cmdToUse == null)
174 throw new SlcException("Please specify a command.");
175 else if (commandToUse != null && cmdToUse != null)
176 throw new SlcException(
177 "Specify the command either as a line or as a list.");
178 else if (cmdToUse != null) {
179 commandLine = CommandLine.parse(cmdToUse);
180 } else if (commandToUse != null) {
181 if (commandToUse.size() == 0)
182 throw new SlcException("Command line is empty.");
183
184 commandLine = new CommandLine(commandToUse.get(0).toString());
185
186 for (int i = 1; i < commandToUse.size(); i++) {
187 if (log.isTraceEnabled())
188 log.debug(commandToUse.get(i));
189 commandLine.addArgument(commandToUse.get(i).toString());
190 }
191 } else {
192 // all cases covered previously
193 throw new UnsupportedException();
194 }
195
196 if (generateScript != null) {
197 File scriptFile = new File(getExecDirToUse() + File.separator
198 + generateScript);
199 try {
200 FileUtils.writeStringToFile(scriptFile,
201 (osConsole != null ? osConsole + " " : "")
202 + commandLine.toString());
203 } catch (IOException e) {
204 throw new SlcException("Could not generate script "
205 + scriptFile, e);
206 }
207 commandLine = new CommandLine(scriptFile);
208 } else {
209 if (osConsole != null)
210 commandLine = CommandLine.parse(osConsole + " "
211 + commandLine.toString());
212 }
213
214 return commandLine;
215 }
216
217 protected ExecuteStreamHandler createExecuteStreamHandler(
218 final Writer stdOutWriter, final Writer stdErrWriter) {
219 // Log writers
220
221 PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(
222 new LogOutputStream() {
223 protected void processLine(String line, int level) {
224 log(stdOutLogLevel, line);
225 if (stdOutWriter != null)
226 appendLineToFile(stdOutWriter, line);
227 }
228 }, new LogOutputStream() {
229 protected void processLine(String line, int level) {
230 log(stdErrLogLevel, line);
231 if (stdErrWriter != null)
232 appendLineToFile(stdErrWriter, line);
233 }
234 }, null);
235 return pumpStreamHandler;
236 }
237
238 protected ExecuteResultHandler createExecuteResultHandler(
239 final CommandLine commandLine) {
240 return new ExecuteResultHandler() {
241
242 public void onProcessComplete(int exitValue) {
243 if (log.isDebugEnabled())
244 log
245 .debug("Process " + commandLine
246 + " properly completed.");
247 if (testResult != null) {
248 forwardPath(testResult, null);
249 testResult.addResultPart(new SimpleResultPart(
250 TestStatus.PASSED, "Process " + commandLine
251 + " properly completed."));
252 }
253 }
254
255 public void onProcessFailed(ExecuteException e) {
256 if (testResult != null) {
257 forwardPath(testResult, null);
258 testResult.addResultPart(new SimpleResultPart(
259 TestStatus.ERROR, "Process " + commandLine
260 + " failed.", e));
261 } else {
262 throw new SlcException("Process " + commandLine
263 + " failed.", e);
264 }
265 }
266 };
267 }
268
269 /**
270 * Shortcut method getting the execDir to use
271 */
272 protected String getExecDirToUse() {
273 try {
274 File dir = null;
275 if (execDir != null) {
276 // Replace '/' by local file separator, for portability
277 execDir.replace('/', File.separatorChar);
278 dir = new File(execDir).getCanonicalFile();
279 }
280
281 if (dir == null)
282 return System.getProperty("user.dir");
283 else
284 return dir.getPath();
285 } catch (Exception e) {
286 throw new SlcException("Cannot find exec dir", e);
287 }
288 }
289
290 protected void log(String logLevel, String line) {
291 if ("ERROR".equals(logLevel))
292 log.error(line);
293 else if ("WARN".equals(logLevel))
294 log.warn(line);
295 else if ("INFO".equals(logLevel))
296 log.info(line);
297 else if ("DEBUG".equals(logLevel))
298 log.debug(line);
299 else if ("TRACE".equals(logLevel))
300 log.trace(line);
301 else
302 throw new SlcException("Unknown log level " + logLevel);
303 }
304
305 protected void appendLineToFile(Writer writer, String line) {
306 try {
307 writer.append(line).append('\n');
308 } catch (IOException e) {
309 log.error("Cannot write to log file", e);
310 }
311 }
312
313 protected Writer createWriter(Resource target) {
314 FileWriter writer = null;
315 try {
316 File file = target.getFile();
317 writer = new FileWriter(file, true);
318 } catch (IOException e) {
319 log.error("Cannot create log file " + target, e);
320 IOUtils.closeQuietly(writer);
321 }
322 return writer;
323 }
324
325 public void setCmd(String command) {
326 this.cmd = command;
327 }
328
329 public void setExecDir(String execdir) {
330 this.execDir = execdir;
331 }
332
333 public void setStdErrLogLevel(String stdErrLogLevel) {
334 this.stdErrLogLevel = stdErrLogLevel;
335 }
336
337 public void setStdOutLogLevel(String stdOutLogLevel) {
338 this.stdOutLogLevel = stdOutLogLevel;
339 }
340
341 public void setSynchronous(Boolean synchronous) {
342 this.synchronous = synchronous;
343 }
344
345 public void setCommand(List<Object> command) {
346 this.command = command;
347 }
348
349 public void setOsCommands(Map<String, List<Object>> osCommands) {
350 this.osCommands = osCommands;
351 }
352
353 public void setOsCmds(Map<String, String> osCmds) {
354 this.osCmds = osCmds;
355 }
356
357 public void setEnvironmentVariables(Map<String, String> environmentVariables) {
358 this.environmentVariables = environmentVariables;
359 }
360
361 public void setWatchdogTimeout(Long watchdogTimeout) {
362 this.watchdogTimeout = watchdogTimeout;
363 }
364
365 public void setStdOutFile(Resource stdOutFile) {
366 this.stdOutFile = stdOutFile;
367 }
368
369 public void setStdErrFile(Resource stdErrFile) {
370 this.stdErrFile = stdErrFile;
371 }
372
373 public void setTestResult(TestResult testResult) {
374 this.testResult = testResult;
375 }
376
377 public void setLogCommand(Boolean logCommand) {
378 this.logCommand = logCommand;
379 }
380
381 public void setRedirectStreams(Boolean redirectStreams) {
382 this.redirectStreams = redirectStreams;
383 }
384
385 public void setOsConsole(String osConsole) {
386 this.osConsole = osConsole;
387 }
388
389 public void setGenerateScript(String generateScript) {
390 this.generateScript = generateScript;
391 }
392
393 private class DummyexecuteStreamHandler implements ExecuteStreamHandler {
394
395 public void setProcessErrorStream(InputStream is) throws IOException {
396 }
397
398 public void setProcessInputStream(OutputStream os) throws IOException {
399 }
400
401 public void setProcessOutputStream(InputStream is) throws IOException {
402 }
403
404 public void start() throws IOException {
405 }
406
407 public void stop() {
408 }
409
410 }
411
412 }