]> git.argeo.org Git - gpl/argeo-slc.git/blob - runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/core/execution/tasks/SystemCall.java
Introduce revision build numbers
[gpl/argeo-slc.git] / runtime / org.argeo.slc.support.simple / 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.Writer;
7 import java.util.HashMap;
8 import java.util.List;
9 import java.util.Map;
10
11 import org.apache.commons.exec.CommandLine;
12 import org.apache.commons.exec.DefaultExecutor;
13 import org.apache.commons.exec.ExecuteException;
14 import org.apache.commons.exec.ExecuteResultHandler;
15 import org.apache.commons.exec.ExecuteWatchdog;
16 import org.apache.commons.exec.Executor;
17 import org.apache.commons.exec.LogOutputStream;
18 import org.apache.commons.exec.PumpStreamHandler;
19 import org.apache.commons.exec.ShutdownHookProcessDestroyer;
20 import org.apache.commons.io.IOUtils;
21 import org.apache.commons.lang.NotImplementedException;
22 import org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
24 import org.argeo.slc.SlcException;
25 import org.argeo.slc.core.structure.tree.TreeSPath;
26 import org.argeo.slc.core.structure.tree.TreeSRelatedHelper;
27 import org.argeo.slc.core.test.SimpleResultPart;
28 import org.argeo.slc.structure.StructureAware;
29 import org.argeo.slc.test.TestResult;
30 import org.argeo.slc.test.TestStatus;
31 import org.springframework.core.io.Resource;
32
33 /** Execute and OS system call. */
34 public class SystemCall extends TreeSRelatedHelper implements Runnable,
35 StructureAware<TreeSPath> {
36 private final Log log = LogFactory.getLog(getClass());
37
38 private String execDir;
39
40 private String cmd = null;
41 private List<Object> command = null;
42
43 private Boolean synchronous = true;
44
45 private String stdErrLogLevel = "ERROR";
46 private String stdOutLogLevel = "INFO";
47
48 private Resource stdOutFile = null;
49 private Resource stdErrFile = null;
50
51 private Map<String, List<Object>> osCommands = new HashMap<String, List<Object>>();
52 private Map<String, String> osCmds = new HashMap<String, String>();
53 private Map<String, String> environmentVariables = new HashMap<String, String>();
54
55 private Long watchdogTimeout = 24 * 60 * 60 * 1000l;
56
57 private TestResult testResult;
58
59 public SystemCall() {
60
61 }
62
63 public SystemCall(List<Object> command) {
64 super();
65 this.command = command;
66 }
67
68 public void run() {
69 // Log writers
70 final Writer stdOutWriter;
71 final Writer stdErrWriter;
72 if (stdOutFile != null) {
73 stdOutWriter = createWriter(stdOutFile);
74 } else
75 stdOutWriter = null;
76 if (stdErrFile != null) {
77 stdErrWriter = createWriter(stdErrFile);
78 } else {
79 if (stdOutFile != null) {
80 stdErrWriter = createWriter(stdOutFile);
81 } else
82 stdErrWriter = null;
83 }
84
85 try {
86 if (log.isTraceEnabled()) {
87 log.debug("os.name=" + System.getProperty("os.name"));
88 log.debug("os.arch=" + System.getProperty("os.arch"));
89 log.debug("os.version=" + System.getProperty("os.version"));
90 }
91
92 // Execution directory
93 File dir = null;
94 if (execDir != null) {
95 // Replace '/' by local file separator, for portability
96 execDir.replace('/', File.separatorChar);
97 dir = new File(execDir).getCanonicalFile();
98 }
99
100 // Check if an OS specific command overrides
101 String osName = System.getProperty("os.name");
102 List<Object> commandToUse = null;
103 if (osCommands.containsKey(osName))
104 commandToUse = osCommands.get(osName);
105 else
106 commandToUse = command;
107 String cmdToUse = null;
108 if (osCmds.containsKey(osName))
109 cmdToUse = osCmds.get(osName);
110 else
111 cmdToUse = cmd;
112
113 // Prepare executor
114 if (dir == null)
115 dir = new File(getUsedDir(dir));
116 if (!dir.exists())
117 dir.mkdirs();
118
119 Executor executor = new DefaultExecutor();
120 executor.setWatchdog(new ExecuteWatchdog(watchdogTimeout));
121
122 PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(
123 new LogOutputStream() {
124 protected void processLine(String line, int level) {
125 log(stdOutLogLevel, line);
126 if (stdOutWriter != null)
127 appendLineToFile(stdOutWriter, line);
128 }
129 }, new LogOutputStream() {
130 protected void processLine(String line, int level) {
131 log(stdErrLogLevel, line);
132 if (stdErrWriter != null)
133 appendLineToFile(stdErrWriter, line);
134 }
135 }, null);
136 executor.setStreamHandler(pumpStreamHandler);
137 executor.setProcessDestroyer(new ShutdownHookProcessDestroyer());
138 executor.setWorkingDirectory(dir);
139 final CommandLine commandLine;
140
141 // Which command definition to use
142 if (commandToUse == null && cmdToUse == null)
143 throw new SlcException("Please specify a command.");
144 else if (commandToUse != null && cmdToUse != null)
145 throw new SlcException(
146 "Specify the command either as a line or as a list.");
147 else if (cmdToUse != null) {
148 if (log.isTraceEnabled())
149 log.trace("Execute '" + cmdToUse + "' in "
150 + getUsedDir(dir));
151
152 commandLine = CommandLine.parse(cmdToUse);
153 } else if (commandToUse != null) {
154 if (log.isTraceEnabled())
155 log.trace("Execute '" + commandToUse + "' in "
156 + getUsedDir(dir));
157 if (commandToUse.size() == 0)
158 throw new SlcException("Command line is empty.");
159
160 commandLine = new CommandLine(commandToUse.get(0).toString());
161 for (int i = 1; i < commandToUse.size(); i++)
162 commandLine.addArgument(commandToUse.get(i).toString());
163 } else {
164 // all cases covered previously
165 throw new NotImplementedException();
166 }
167
168 // Env variables
169 Map<String, String> environmentVariablesToUse = environmentVariables
170 .size() > 0 ? environmentVariables : null;
171
172 // Execute
173 ExecuteResultHandler executeResultHandler = new ExecuteResultHandler() {
174
175 public void onProcessComplete(int exitValue) {
176 if (log.isDebugEnabled())
177 log.debug("Process " + commandLine
178 + " properly completed.");
179 if (testResult != null) {
180 forwardPath(testResult, null);
181 testResult.addResultPart(new SimpleResultPart(
182 TestStatus.PASSED, "Process " + commandLine
183 + " properly completed."));
184 }
185 }
186
187 public void onProcessFailed(ExecuteException e) {
188 if (testResult != null) {
189 forwardPath(testResult, null);
190 testResult.addResultPart(new SimpleResultPart(
191 TestStatus.ERROR, "Process " + commandLine
192 + " failed.", e));
193 } else {
194 throw new SlcException("Process " + commandLine
195 + " failed.", e);
196 }
197 }
198 };
199
200 if (synchronous)
201 try {
202 int exitValue = executor.execute(commandLine,
203 environmentVariablesToUse);
204 executeResultHandler.onProcessComplete(exitValue);
205 } catch (ExecuteException e1) {
206 executeResultHandler.onProcessFailed(e1);
207 }
208 else
209 executor.execute(commandLine, environmentVariablesToUse,
210 executeResultHandler);
211 } catch (Exception e) {
212 throw new SlcException("Could not execute command " + cmd, e);
213 } finally {
214 IOUtils.closeQuietly(stdOutWriter);
215 IOUtils.closeQuietly(stdErrWriter);
216 }
217
218 }
219
220 /**
221 * Shortcut method returning the current exec dir if the specified one is
222 * null.
223 */
224 private String getUsedDir(File dir) {
225 if (dir == null)
226 return System.getProperty("user.dir");
227 else
228 return dir.getPath();
229 }
230
231 protected void log(String logLevel, String line) {
232 if ("ERROR".equals(logLevel))
233 log.error(line);
234 else if ("WARN".equals(logLevel))
235 log.warn(line);
236 else if ("INFO".equals(logLevel))
237 log.info(line);
238 else if ("DEBUG".equals(logLevel))
239 log.debug(line);
240 else if ("TRACE".equals(logLevel))
241 log.trace(line);
242 else
243 throw new SlcException("Unknown log level " + logLevel);
244 }
245
246 protected void appendLineToFile(Writer writer, String line) {
247 try {
248 writer.append(line).append('\n');
249 } catch (IOException e) {
250 log.error("Cannot write to log file", e);
251 }
252 }
253
254 protected Writer createWriter(Resource target) {
255 FileWriter writer = null;
256 try {
257 File file = target.getFile();
258 writer = new FileWriter(file, true);
259 } catch (IOException e) {
260 log.error("Cannot create log file " + target, e);
261 IOUtils.closeQuietly(writer);
262 }
263 return writer;
264 }
265
266 public void setCmd(String command) {
267 this.cmd = command;
268 }
269
270 public void setExecDir(String execdir) {
271 this.execDir = execdir;
272 }
273
274 public void setStdErrLogLevel(String stdErrLogLevel) {
275 this.stdErrLogLevel = stdErrLogLevel;
276 }
277
278 public void setStdOutLogLevel(String stdOutLogLevel) {
279 this.stdOutLogLevel = stdOutLogLevel;
280 }
281
282 public void setSynchronous(Boolean synchronous) {
283 this.synchronous = synchronous;
284 }
285
286 public void setCommand(List<Object> command) {
287 this.command = command;
288 }
289
290 public void setOsCommands(Map<String, List<Object>> osCommands) {
291 this.osCommands = osCommands;
292 }
293
294 public void setOsCmds(Map<String, String> osCmds) {
295 this.osCmds = osCmds;
296 }
297
298 public void setEnvironmentVariables(Map<String, String> environmentVariables) {
299 this.environmentVariables = environmentVariables;
300 }
301
302 public void setWatchdogTimeout(Long watchdogTimeout) {
303 this.watchdogTimeout = watchdogTimeout;
304 }
305
306 public void setStdOutFile(Resource stdOutFile) {
307 this.stdOutFile = stdOutFile;
308 }
309
310 public void setStdErrFile(Resource stdErrFile) {
311 this.stdErrFile = stdErrFile;
312 }
313
314 public void setTestResult(TestResult testResult) {
315 this.testResult = testResult;
316 }
317
318 }