1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.waarp.openr66.context.task;
21
22 import org.apache.commons.exec.CommandLine;
23 import org.apache.commons.exec.DefaultExecutor;
24 import org.apache.commons.exec.ExecuteException;
25 import org.apache.commons.exec.ExecuteWatchdog;
26 import org.apache.commons.exec.PumpStreamHandler;
27 import org.waarp.common.file.FileUtils;
28 import org.waarp.common.logging.SysErrLogger;
29 import org.waarp.common.logging.WaarpLogger;
30 import org.waarp.common.logging.WaarpLoggerFactory;
31 import org.waarp.openr66.context.ErrorCode;
32 import org.waarp.openr66.context.R66Result;
33 import org.waarp.openr66.context.R66Session;
34 import org.waarp.openr66.context.task.exception.OpenR66RunnerErrorException;
35 import org.waarp.openr66.protocol.configuration.Configuration;
36
37 import java.io.File;
38 import java.io.IOException;
39 import java.io.PipedInputStream;
40 import java.io.PipedOutputStream;
41 import java.util.regex.Pattern;
42
43
44
45
46
47
48 public abstract class AbstractExecTask extends AbstractTask {
49
50
51
52 private static final WaarpLogger logger =
53 WaarpLoggerFactory.getLogger(AbstractExecTask.class);
54 private static final Pattern COMPILE_REPLACE_ALL =
55 Pattern.compile("#([A-Z]+)#");
56
57
58
59
60
61
62
63
64
65 AbstractExecTask(final TaskType type, final int delay, final String argRule,
66 final String argTransfer, final R66Session session) {
67 super(type, delay, argRule, argTransfer, session);
68 }
69
70
71
72
73
74
75 protected final CommandLine buildCommandLine(final String line) {
76 if (line.contains(NOWAIT)) {
77 waitForValidation = false;
78 }
79 if (line.contains(LOCALEXEC)) {
80 useLocalExec = true;
81 }
82
83 final String replacedLine =
84 COMPILE_REPLACE_ALL.matcher(line).replaceAll("\\${$1}");
85
86 final CommandLine commandLine =
87 CommandLine.parse(replacedLine, getSubstitutionMap());
88
89 final File exec = new File(commandLine.getExecutable());
90 if (exec.isAbsolute() && !exec.canExecute()) {
91 logger.error("Exec command is not executable: " + line);
92 final R66Result result =
93 new R66Result(session, false, ErrorCode.CommandNotFound,
94 session.getRunner());
95 futureCompletion.setResult(result);
96 futureCompletion.cancel();
97 return null;
98 }
99
100 return commandLine;
101 }
102
103
104
105
106 static class PrepareCommandExec {
107 private final AbstractExecTask abstractTask;
108 private final boolean noOutput;
109 private final boolean waitForValidation;
110 private final String finalname;
111 private boolean myResult;
112 private CommandLine commandLine;
113 private DefaultExecutor defaultExecutor;
114 private PipedInputStream inputStream;
115 private PipedOutputStream outputStream;
116 private PumpStreamHandler pumpStreamHandler;
117 private ExecuteWatchdog watchdog;
118
119 PrepareCommandExec(final AbstractExecTask abstractTask,
120 final String finalname, final boolean noOutput,
121 final boolean waitForValidation) {
122 this.abstractTask = abstractTask;
123 this.finalname = finalname;
124 this.noOutput = noOutput;
125 this.waitForValidation = waitForValidation;
126 }
127
128 final boolean isError() {
129 return myResult;
130 }
131
132 public final CommandLine getCommandLine() {
133 return commandLine;
134 }
135
136 public final DefaultExecutor getDefaultExecutor() {
137 return defaultExecutor;
138 }
139
140 public final PipedInputStream getInputStream() {
141 return inputStream;
142 }
143
144 public final PipedOutputStream getOutputStream() {
145 return outputStream;
146 }
147
148 public final PumpStreamHandler getPumpStreamHandler() {
149 return pumpStreamHandler;
150 }
151
152 public final ExecuteWatchdog getWatchdog() {
153 return watchdog;
154 }
155
156 public final PrepareCommandExec invoke() {
157 commandLine = abstractTask.buildCommandLine(finalname);
158 if (commandLine == null) {
159 myResult = true;
160 return this;
161 }
162
163 defaultExecutor = new DefaultExecutor();
164 if (noOutput) {
165 pumpStreamHandler = new PumpStreamHandler(null, null);
166 } else {
167 inputStream = new PipedInputStream();
168 outputStream = null;
169 try {
170 outputStream = new PipedOutputStream(inputStream);
171 } catch (final IOException e1) {
172 FileUtils.close(inputStream);
173 logger.error(
174 "Exception: " + e1.getMessage() + " Exec in error with " +
175 commandLine + ": {}", e1.getMessage());
176 abstractTask.futureCompletion.setFailure(e1);
177 myResult = true;
178 return this;
179 }
180 pumpStreamHandler = new PumpStreamHandler(outputStream, null);
181 }
182 defaultExecutor.setStreamHandler(pumpStreamHandler);
183 final int[] correctValues = { 0, 1 };
184 defaultExecutor.setExitValues(correctValues);
185 watchdog = null;
186 if (abstractTask.delay > 0 && waitForValidation) {
187 watchdog = new ExecuteWatchdog(abstractTask.delay);
188 defaultExecutor.setWatchdog(watchdog);
189 }
190 myResult = false;
191 return this;
192 }
193 }
194
195
196
197
198 static class ExecuteCommand {
199 private final AbstractExecTask abstractExecTask;
200 private final CommandLine commandLine;
201 private final DefaultExecutor defaultExecutor;
202 private final PipedInputStream inputStream;
203 private final PipedOutputStream outputStream;
204 private final PumpStreamHandler pumpStreamHandler;
205 private final Thread thread;
206 private boolean myResult;
207 private int status;
208
209 ExecuteCommand(final AbstractExecTask abstractExecTask,
210 final CommandLine commandLine,
211 final DefaultExecutor defaultExecutor,
212 final PipedInputStream inputStream,
213 final PipedOutputStream outputStream,
214 final PumpStreamHandler pumpStreamHandler,
215 final Thread thread) {
216 this.abstractExecTask = abstractExecTask;
217 this.commandLine = commandLine;
218 this.defaultExecutor = defaultExecutor;
219 this.inputStream = inputStream;
220 this.outputStream = outputStream;
221 this.pumpStreamHandler = pumpStreamHandler;
222 this.thread = thread;
223 }
224
225 final boolean isError() {
226 return myResult;
227 }
228
229 public final int getStatus() {
230 return status;
231 }
232
233 public final ExecuteCommand invoke() {
234 status = -1;
235 try {
236 status = defaultExecutor.execute(commandLine);
237 } catch (final ExecuteException e) {
238 if (e.getExitValue() == -559038737) {
239
240 try {
241 Thread.sleep(Configuration.RETRYINMS);
242 } catch (final InterruptedException e1) {
243 SysErrLogger.FAKE_LOGGER.ignoreLog(e1);
244 }
245 try {
246 status = defaultExecutor.execute(commandLine);
247 } catch (final ExecuteException e1) {
248 closeAllForExecution(true);
249 abstractExecTask.finalizeFromError(thread, status, commandLine, e1);
250 myResult = true;
251 return this;
252 } catch (final IOException e1) {
253 closeAllForExecution(true);
254 logger.error(
255 "IOException: " + e.getMessage() + " . Exec in error with " +
256 commandLine);
257 abstractExecTask.futureCompletion.setFailure(e);
258 myResult = true;
259 return this;
260 }
261 } else {
262 closeAllForExecution(true);
263 abstractExecTask.finalizeFromError(thread, status, commandLine, e);
264 myResult = true;
265 return this;
266 }
267 } catch (final IOException e) {
268 closeAllForExecution(true);
269 logger.error(
270 "IOException: " + e.getMessage() + " . Exec in error with " +
271 commandLine);
272 abstractExecTask.futureCompletion.setFailure(e);
273 myResult = true;
274 return this;
275 }
276 closeAllForExecution(false);
277 if (thread != null) {
278 try {
279 if (abstractExecTask.delay > 0) {
280 thread.join(abstractExecTask.delay);
281 } else {
282 thread.join();
283 }
284 } catch (final InterruptedException e) {
285 SysErrLogger.FAKE_LOGGER.ignoreLog(e);
286 FileUtils.close(inputStream);
287 Thread.currentThread().interrupt();
288 }
289 }
290 FileUtils.close(inputStream);
291 myResult = false;
292 return this;
293 }
294
295 private void closeAllForExecution(final boolean interrupt) {
296 FileUtils.close(outputStream);
297 if (interrupt && thread != null) {
298 thread.interrupt();
299 }
300 FileUtils.close(inputStream);
301 try {
302 pumpStreamHandler.stop();
303 } catch (final IOException ignored) {
304
305 }
306 }
307 }
308
309 void finalizeFromError(final Runnable threadReader, final int status,
310 final CommandLine commandLine, final Exception e) {
311 logger.error("Status: " + status + " Exec in error with " + commandLine +
312 " returns " + e.getMessage());
313 final OpenR66RunnerErrorException exc = new OpenR66RunnerErrorException(
314 "<STATUS>" + status + "</STATUS><ERROR>" + e.getMessage() + "</ERROR>");
315 futureCompletion.setFailure(exc);
316 }
317 }