File D:\maven-qcr-plugin-testsunitaires\qcr4java\jenkins-master\target\site\qc\core\src\main\java\hudson\Proc.java.html

1 /*
2 * The MIT License
3 *
4 * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, CloudBees, Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24 package hudson;
25
26 import hudson.Launcher.ProcStarter;
27 import hudson.model.TaskListener;
28 import hudson.remoting.Channel;
29 import hudson.util.DaemonThreadFactory;
30 import hudson.util.ExceptionCatchingThreadFactory;
31 import hudson.util.IOException2;
32 import hudson.util.NullStream;
33 import hudson.util.StreamCopyThread;
34 import hudson.util.ProcessTree;
35 import org.apache.commons.io.input.NullInputStream;
36
37 import java.io.File;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.io.OutputStream;
41 import java.util.Locale;
42 import java.util.Map;
43 import java.util.concurrent.CancellationException;
44 import java.util.concurrent.CountDownLatch;
45 import java.util.concurrent.ExecutionException;
46 import java.util.concurrent.ExecutorService;
47 import java.util.concurrent.Executors;
48 import java.util.concurrent.Future;
49 import java.util.concurrent.TimeUnit;
50 import java.util.logging.Level;
51 import java.util.logging.Logger;
52
53 /**
54 * External process wrapper.
55 *
56 * <p>
57 * Used for launching, monitoring, waiting for a process.
58 *
59 * @author Kohsuke Kawaguchi
60 */
61 public abstract class Proc {
62 protected Proc() {}
63
64 /**
65 * Checks if the process is still alive.
66 */
67 public abstract boolean isAlive() throws IOException, InterruptedException;
68
69 /**
70 * Terminates the process.
71 *
72 * @throws IOException
73 * if there's an error killing a process
74 * and a stack trace could help the trouble-shooting.
75 */
76 public abstract void kill() throws IOException, InterruptedException;
77
78 /**
79 * Waits for the completion of the process.
80 *
81 * Unless the caller opts to pump the streams via {@link #getStdout()} etc.,
82 * this method also blocks until we finish reading everything that the process has produced
83 * to stdout/stderr.
84 *
85 * <p>
86 * If the thread is interrupted while waiting for the completion
87 * of the process, this method terminates the process and
88 * exits with a non-zero exit code.
89 *
90 * @throws IOException
91 * if there's an error launching/joining a process
92 * and a stack trace could help the trouble-shooting.
93 */
94 public abstract int join() throws IOException, InterruptedException;
95
96 /**
97 * Returns an {@link InputStream} to read from {@code stdout} of the child process.
98 * <p>
99 * When this method returns null, {@link Proc} will internally pump the output from
100 * the child process to your {@link OutputStream} of choosing.
101 *
102 * @return
103 * null unless {@link ProcStarter#readStdout()} is used to indicate
104 * that the caller intends to pump the stream by itself.
105 * @since 1.399
106 */
107 public abstract InputStream getStdout();
108
109 /**
110 * Returns an {@link InputStream} to read from {@code stderr} of the child process.
111 * <p>
112 * When this method returns null, {@link Proc} will internally pump the output from
113 * the child process to your {@link OutputStream} of choosing.
114 *
115 * @return
116 * null unless {@link ProcStarter#readStderr()} is used to indicate
117 * that the caller intends to pump the stream by itself.
118 * @since 1.399
119 */
120 public abstract InputStream getStderr();
121
122 /**
123 * Returns an {@link OutputStream} to write to {@code stdin} of the child process.
124 * <p>
125 * When this method returns null, {@link Proc} will internally pump the {@link InputStream}
126 * of your choosing to the child process.
127 *
128 * @return
129 * null unless {@link ProcStarter#writeStdin()} is used to indicate
130 * that the caller intends to pump the stream by itself.
131 * @since 1.399
132 */
133 public abstract OutputStream getStdin();
134
135 private static final ExecutorService executor = Executors.newCachedThreadPool(new ExceptionCatchingThreadFactory(new DaemonThreadFactory()));
136
137 /**
138 * Like {@link #join} but can be given a maximum time to wait.
139 * @param timeout number of time units
140 * @param unit unit of time
141 * @param listener place to send messages if there are problems, incl. timeout
142 * @return exit code from the process
143 * @throws IOException for the same reasons as {@link #join}
144 * @throws InterruptedException for the same reasons as {@link #join}
145 * @since 1.363
146 */
147 public final int joinWithTimeout(final long timeout, final TimeUnit unit,
148 final TaskListener listener) throws IOException, InterruptedException {
149 final CountDownLatch latch = new CountDownLatch(1);
150 try {
151 executor.submit(new Runnable() {
152 public void run() {
153 try {
154 if (!latch.await(timeout, unit)) {
155 listener.error("Timeout after " + timeout + " " +
156 unit.toString().toLowerCase(Locale.ENGLISH));
157 kill();
158 }
159 } catch (InterruptedException x) {
160 x.printStackTrace(listener.error("Failed to join a process"));
161 } catch (IOException x) {
162 x.printStackTrace(listener.error("Failed to join a process"));
163 } catch (RuntimeException x) {
164 x.printStackTrace(listener.error("Failed to join a process"));
165 }
166 }
167 });
168 return join();
169 } finally {
170 latch.countDown();
171 }
172 }
173
174 /**
175 * Locally launched process.
176 */
177 public static final class LocalProc extends Proc {
178 private final Process proc;
179 private final Thread copier,copier2;
180 private final OutputStream out;
181 private final EnvVars cookie;
182 private final String name;
183
184 private final InputStream stdout,stderr;
185 private final OutputStream stdin;
186
187 public LocalProc(String cmd, Map<String,String> env, OutputStream out, File workDir) throws IOException {
188 this(cmd,Util.mapToEnv(env),out,workDir);
189 }
190
191 public LocalProc(String[] cmd, Map<String,String> env,InputStream in, OutputStream out) throws IOException {
192 this(cmd,Util.mapToEnv(env),in,out);
193 }
194
195 public LocalProc(String cmd,String[] env,OutputStream out, File workDir) throws IOException {
196 this( Util.tokenize(cmd), env, out, workDir );
197 }
198
199 public LocalProc(String[] cmd,String[] env,OutputStream out, File workDir) throws IOException {
200 this(cmd,env,null,out,workDir);
201 }
202
203 public LocalProc(String[] cmd,String[] env,InputStream in,OutputStream out) throws IOException {
204 this(cmd,env,in,out,null);
205 }
206
207 public LocalProc(String[] cmd,String[] env,InputStream in,OutputStream out, File workDir) throws IOException {
208 this(cmd,env,in,out,null,workDir);
209 }
210
211 /**
212 * @param err
213 * null to redirect stderr to stdout.
214 */
215 public LocalProc(String[] cmd,String[] env,InputStream in,OutputStream out,OutputStream err,File workDir) throws IOException {
216 this( calcName(cmd),
217 stderr(environment(new ProcessBuilder(cmd),env).directory(workDir), err==null || err== SELFPUMP_OUTPUT),
218 in, out, err );
219 }
220
221 private static ProcessBuilder stderr(ProcessBuilder pb, boolean redirectError) {
222 if(redirectError) pb.redirectErrorStream(true);
223 return pb;
224 }
225
226 private static ProcessBuilder environment(ProcessBuilder pb, String[] env) {
227 if(env!=null) {
228 Map<String, String> m = pb.environment();
229 m.clear();
230 for (String e : env) {
231 int idx = e.indexOf('=');
232 m.put(e.substring(0,idx),e.substring(idx+1,e.length()));
233 }
234 }
235 return pb;
236 }
237
238 private LocalProc( String name, ProcessBuilder procBuilder, InputStream in, OutputStream out, OutputStream err ) throws IOException {
239 Logger.getLogger(Proc.class.getName()).log(Level.FINE, "Running: {0}", name);
240 this.name = name;
241 this.out = out;
242 this.cookie = EnvVars.createCookie();
243 procBuilder.environment().putAll(cookie);
244 this.proc = procBuilder.start();
245
246 InputStream procInputStream = proc.getInputStream();
247 if (out==SELFPUMP_OUTPUT) {
248 stdout = procInputStream;
249 copier = null;
250 } else {
251 copier = new StreamCopyThread(name+": stdout copier", procInputStream, out);
252 copier.start();
253 stdout = null;
254 }
255
256 if (in == null) {
257 // nothing to feed to stdin
258 stdin = null;
259 proc.getOutputStream().close();
260 } else
261 if (in==SELFPUMP_INPUT) {
262 stdin = proc.getOutputStream();
263 } else {
264 new StdinCopyThread(name+": stdin copier",in,proc.getOutputStream()).start();
265 stdin = null;
266 }
267
268 InputStream procErrorStream = proc.getErrorStream();
269 if(err!=null) {
270 if (err==SELFPUMP_OUTPUT) {
271 stderr = procErrorStream;
272 copier2 = null;
273 } else {
274 stderr = null;
275 copier2 = new StreamCopyThread(name+": stderr copier", procErrorStream, err);
276 copier2.start();
277 }
278 } else {
279 // the javadoc is unclear about what getErrorStream() returns when ProcessBuilder.redirectErrorStream(true),
280 //
281 // according to the source code, Sun JREs still still returns a distinct reader end of a pipe that needs to be closed.
282 // but apparently at least on some IBM JDK5, returned input and error streams are the same.
283 // so try to close them smartly
284 if (procErrorStream!=procInputStream) {
285 procErrorStream.close();
286 }
287 copier2 = null;
288 stderr = null;
289 }
290 }
291
292 public InputStream getStdout() {
293 return stdout;
294 }
295
296 public InputStream getStderr() {
297 return stderr;
298 }
299
300 public OutputStream getStdin() {
301 return stdin;
302 }
303
304 /**
305 * Waits for the completion of the process.
306 */
307 @Override
308 public int join() throws InterruptedException, IOException {
309 // show what we are waiting for in the thread title
310 // since this involves some native work, let's have some soak period before enabling this by default
311 Thread t = Thread.currentThread();
312 String oldName = t.getName();
313 if (SHOW_PID) {
314 ProcessTree.OSProcess p = ProcessTree.get().get(proc);
315 t.setName(oldName+" "+(p!=null?"waiting for pid="+p.getPid():"waiting for "+name));
316 }
317
318 try {
319 int r = proc.waitFor();
320 // see http://wiki.jenkins-ci.org/display/JENKINS/Spawning+processes+from+build
321 // problems like that shows up as infinite wait in join(), which confuses great many users.
322 // So let's do a timed wait here and try to diagnose the problem
323 if (copier!=null) copier.join(10*1000);
324 if(copier2!=null) copier2.join(10*1000);
325 if((copier!=null && copier.isAlive()) || (copier2!=null && copier2.isAlive())) {
326 // looks like handles are leaking.
327 // closing these handles should terminate the threads.
328 String msg = "Process leaked file descriptors. See http://wiki.jenkins-ci.org/display/JENKINS/Spawning+processes+from+build for more information";
329 Throwable e = new Exception().fillInStackTrace();
330 LOGGER.log(Level.WARNING,msg,e);
331
332 // doing proc.getInputStream().close() hangs in FileInputStream.close0()
333 // it could be either because another thread is blocking on read, or
334 // it could be a bug in Windows JVM. Who knows.
335 // so I'm abandoning the idea of closing the stream
336 // try {
337 // proc.getInputStream().close();
338 // } catch (IOException x) {
339 // LOGGER.log(Level.FINE,"stdin termination failed",x);
340 // }
341 // try {
342 // proc.getErrorStream().close();
343 // } catch (IOException x) {
344 // LOGGER.log(Level.FINE,"stderr termination failed",x);
345 // }
346 out.write(msg.getBytes());
347 out.write('\n');
348 }
349 return r;
350 } catch (InterruptedException e) {
351 // aborting. kill the process
352 destroy();
353 throw e;
354 } finally {
355 t.setName(oldName);
356 }
357 }
358
359 @Override
360 public boolean isAlive() throws IOException, InterruptedException {
361 try {
362 proc.exitValue();
363 return false;
364 } catch (IllegalThreadStateException e) {
365 return true;
366 }
367 }
368
369 @Override
370 public void kill() throws InterruptedException, IOException {
371 destroy();
372 join();
373 }
374
375 /**
376 * Destroys the child process without join.
377 */
378 private void destroy() throws InterruptedException {
379 ProcessTree.get().killAll(proc,cookie);
380 }
381
382 /**
383 * {@link Process#getOutputStream()} is buffered, so we need to eagerly flash
384 * the stream to push bytes to the process.
385 */
386 private static class StdinCopyThread extends Thread {
387 private final InputStream in;
388 private final OutputStream out;
389
390 public StdinCopyThread(String threadName, InputStream in, OutputStream out) {
391 super(threadName);
392 this.in = in;
393 this.out = out;
394 }
395
396 @Override
397 public void run() {
398 try {
399 try {
400 byte[] buf = new byte[8192];
401 int len;
402 while ((len = in.read(buf)) > 0) {
403 out.write(buf, 0, len);
404 out.flush();
405 }
406 } finally {
407 in.close();
408 out.close();
409 }
410 } catch (IOException e) {
411 // TODO: what to do?
412 }
413 }
414 }
415
416 private static String calcName(String[] cmd) {
417 StringBuilder buf = new StringBuilder();
418 for (String token : cmd) {
419 if(buf.length()>0) buf.append(' ');
420 buf.append(token);
421 }
422 return buf.toString();
423 }
424
425 public static final InputStream SELFPUMP_INPUT = new NullInputStream(0);
426 public static final OutputStream SELFPUMP_OUTPUT = new NullStream();
427 }
428
429 /**
430 * Remotely launched process via {@link Channel}.
431 *
432 * @deprecated as of 1.399. Replaced by {@link Launcher.RemoteLauncher.ProcImpl}
433 */
434 public static final class RemoteProc extends Proc {
435 private final Future<Integer> process;
436
437 public RemoteProc(Future<Integer> process) {
438 this.process = process;
439 }
440
441 @Override
442 public void kill() throws IOException, InterruptedException {
443 process.cancel(true);
444 }
445
446 @Override
447 public int join() throws IOException, InterruptedException {
448 try {
449 return process.get();
450 } catch (InterruptedException e) {
451 // aborting. kill the process
452 process.cancel(true);
453 throw e;
454 } catch (ExecutionException e) {
455 if(e.getCause() instanceof IOException)
456 throw (IOException)e.getCause();
457 throw new IOException2("Failed to join the process",e);
458 } catch (CancellationException x) {
459 return -1;
460 }
461 }
462
463 @Override
464 public boolean isAlive() throws IOException, InterruptedException {
465 return !process.isDone();
466 }
467
468 @Override
469 public InputStream getStdout() {
470 return null;
471 }
472
473 @Override
474 public InputStream getStderr() {
475 return null;
476 }
477
478 @Override
479 public OutputStream getStdin() {
480 return null;
481 }
482 }
483
484 private static final Logger LOGGER = Logger.getLogger(Proc.class.getName());
485 /**
486 * Debug switch to have the thread display the process it's waiting for.
487 */
488 public static boolean SHOW_PID = false;
489 }
490