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