From dd0d5cc801053f8c6c4c90658b342465ee94deb5 Mon Sep 17 00:00:00 2001 From: Andras Petres Date: Wed, 14 Feb 2018 17:22:58 +0200 Subject: [PATCH 1/3] Allow disabling process killing on interuption Plugins may want to do custom action when the execution of a remote process is interrupted. Currently it is not possible, because the process started on a slave is killed on interruption. My actual use case: https://groups.google.com/forum/#!msg/jenkinsci-dev/s6IkynOzdqE/2eFNKN9lAAAJ My solution is to have a flag called killWhenInterrupted, set by default to true, which prevents process killing on the slave when unset. --- core/src/main/java/hudson/Launcher.java | 33 ++++++++++++++++++------- core/src/main/java/hudson/Proc.java | 24 +++++++++++++++--- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/hudson/Launcher.java b/core/src/main/java/hudson/Launcher.java index 432a5afe249c..82da65681f7c 100644 --- a/core/src/main/java/hudson/Launcher.java +++ b/core/src/main/java/hudson/Launcher.java @@ -181,6 +181,8 @@ public final class ProcStarter { */ protected boolean reverseStdin, reverseStdout, reverseStderr; + protected boolean killWhenInterrupted; + /** * Passes a white-space separated single-string command (like "cat abc def") and parse them * as a command argument. This method also handles quotes. @@ -441,6 +443,16 @@ public ProcStarter writeStdin() { return this; } + /** + * Indicates whether the process should be killed on interruption. + * + * @return {@code this} + * @since 2.107 + */ + public ProcStarter killWhenInterrupted(boolean value) { + this.killWhenInterrupted = value; + return this; + } /** * Starts the new process as configured. @@ -926,7 +938,7 @@ public Proc launch(ProcStarter ps) throws IOException { ps.reverseStdin ?LocalProc.SELFPUMP_INPUT:ps.stdin, ps.reverseStdout?LocalProc.SELFPUMP_OUTPUT:ps.stdout, ps.reverseStderr?LocalProc.SELFPUMP_OUTPUT:ps.stderr, - toFile(ps.pwd)); + toFile(ps.pwd), ps.killWhenInterrupted); } private File toFile(FilePath f) { @@ -1049,7 +1061,7 @@ public Proc launch(ProcStarter ps) throws IOException { final String workDir = psPwd==null ? null : psPwd.getRemote(); try { - return new ProcImpl(getChannel().call(new RemoteLaunchCallable(ps.commands, ps.masks, ps.envs, in, ps.reverseStdin, out, ps.reverseStdout, err, ps.reverseStderr, ps.quiet, workDir, listener))); + return new ProcImpl(getChannel().call(new RemoteLaunchCallable(ps.commands, ps.masks, ps.envs, in, ps.reverseStdin, out, ps.reverseStdout, err, ps.reverseStderr, ps.quiet, workDir, listener, ps.killWhenInterrupted))); } catch (InterruptedException e) { throw (IOException)new InterruptedIOException().initCause(e); } @@ -1267,12 +1279,14 @@ private static class RemoteLaunchCallable extends MasterToSlaveCallable cmd, @CheckForNull boolean[] masks, @CheckForNull String[] env, - @CheckForNull InputStream in, boolean reverseStdin, - @CheckForNull OutputStream out, boolean reverseStdout, - @CheckForNull OutputStream err, boolean reverseStderr, - boolean quiet, @CheckForNull String workDir, @Nonnull TaskListener listener) { + private final boolean killWhenInterrupted; + + RemoteLaunchCallable(@Nonnull List cmd, @CheckForNull boolean[] masks, @CheckForNull String[] env, + @CheckForNull InputStream in, boolean reverseStdin, + @CheckForNull OutputStream out, boolean reverseStdout, + @CheckForNull OutputStream err, boolean reverseStderr, + boolean quiet, @CheckForNull String workDir, + @Nonnull TaskListener listener, boolean killWhenInterrupted) { this.cmd = new ArrayList<>(cmd); this.masks = masks; this.env = env; @@ -1285,12 +1299,13 @@ private static class RemoteLaunchCallable extends MasterToSlaveCallable Date: Tue, 6 Mar 2018 14:02:28 +0200 Subject: [PATCH 2/3] Invert killWhenInterrupted flag --- core/src/main/java/hudson/Launcher.java | 92 ++++++++++++------------- core/src/main/java/hudson/Proc.java | 30 ++++---- 2 files changed, 56 insertions(+), 66 deletions(-) diff --git a/core/src/main/java/hudson/Launcher.java b/core/src/main/java/hudson/Launcher.java index 82da65681f7c..bc0454bfa2a0 100644 --- a/core/src/main/java/hudson/Launcher.java +++ b/core/src/main/java/hudson/Launcher.java @@ -80,7 +80,7 @@ * * * @author Kohsuke Kawaguchi - * @see FilePath#createLauncher(TaskListener) + * @see FilePath#createLauncher(TaskListener) */ public abstract class Launcher { @@ -119,7 +119,7 @@ public VirtualChannel getChannel() { /** * Gets the {@link TaskListener} that this launcher uses to * report the commands that it's executing. - * + * * @return Task listener */ @Nonnull @@ -181,7 +181,7 @@ public final class ProcStarter { */ protected boolean reverseStdin, reverseStdout, reverseStderr; - protected boolean killWhenInterrupted; + protected boolean dontKillWhenInterrupted; /** * Passes a white-space separated single-string command (like "cat abc def") and parse them @@ -255,7 +255,7 @@ public boolean quiet() { /** * Sets the current directory. - * + * * @param workDir Work directory to be used. * If {@code null}, the default/current directory will be used by the process starter * @return {@code this} @@ -280,8 +280,8 @@ public FilePath pwd() { /** * Sets STDOUT destination. - * - * @param out Output stream. + * + * @param out Output stream. * Use {@code null} to send STDOUT to /dev/null. * @return {@code this} */ @@ -292,7 +292,7 @@ public ProcStarter stdout(@CheckForNull OutputStream out) { /** * Sends the stdout to the given {@link TaskListener}. - * + * * @param out Task listener * @return {@code this} */ @@ -302,7 +302,7 @@ public ProcStarter stdout(@Nonnull TaskListener out) { /** * Gets current STDOUT destination. - * + * * @return STDOUT output stream. {@code null} if STDOUT is suppressed or undefined. */ @CheckForNull @@ -321,7 +321,7 @@ public ProcStarter stderr(@CheckForNull OutputStream err) { /** * Gets current STDERR destination. - * + * * @return STDERR output stream. {@code null} if suppressed or undefined. */ @CheckForNull @@ -332,7 +332,7 @@ public OutputStream stderr() { /** * Controls where the stdin of the process comes from. * By default, /dev/null. - * + * * @return {@code this} */ @Nonnull @@ -343,7 +343,7 @@ public ProcStarter stdin(@CheckForNull InputStream in) { /** * Gets current STDIN destination. - * + * * @return STDIN output stream. {@code null} if suppressed or undefined. */ @CheckForNull @@ -358,7 +358,7 @@ public InputStream stdin() { * In addition to what the current process * is inherited (if this is going to be launched from a agent agent, that * becomes the "current" process), these variables will be also set. - * + * * @param overrides Environment variables to be overridden * @return {@code this} */ @@ -370,7 +370,7 @@ public ProcStarter envs(@Nonnull Map overrides) { /** * @param overrides * List of "VAR=VALUE". See {@link #envs(Map)} for the semantics. - * + * * @return {@code this} */ public ProcStarter envs(@CheckForNull String... overrides) { @@ -388,7 +388,7 @@ public ProcStarter envs(@CheckForNull String... overrides) { /** * Gets a list of environment variables to be set. * Returns an empty array if envs field has not been initialized. - * + * * @return If initialized, returns a copy of internal envs array. Otherwise - a new empty array. */ @Nonnull @@ -433,7 +433,7 @@ public ProcStarter readStderr() { * Indicates that the caller will directly write to the child process {@link #stdin()} via {@link Proc#getStdin()}. * (Whereas by default you call {@link #stdin(InputStream)} * and let Jenkins pump your {@link InputStream} of choosing to stdin.) - * + * * @return {@code this} * @since 1.399 */ @@ -443,14 +443,8 @@ public ProcStarter writeStdin() { return this; } - /** - * Indicates whether the process should be killed on interruption. - * - * @return {@code this} - * @since 2.107 - */ - public ProcStarter killWhenInterrupted(boolean value) { - this.killWhenInterrupted = value; + public ProcStarter dontKillWhenInterrupted() { + this.dontKillWhenInterrupted = true; return this; } @@ -471,7 +465,7 @@ public int join() throws IOException, InterruptedException { // The logging around procHolderForJoin prevents the preliminary object deallocation we saw in JENKINS-23271 final Proc procHolderForJoin = start(); LOGGER.log(Level.FINER, "Started the process {0}", procHolderForJoin); - + if (procHolderForJoin instanceof ProcWithJenkins23271Patch) { return procHolderForJoin.join(); } else { @@ -714,7 +708,7 @@ public Proc launch(String[] cmd, boolean[] mask, String[] env, InputStream in, O *

* When the returned channel is terminated, the process will be killed. * - * @param cmd + * @param cmd * The commands. * @param out * Where the stderr from the launched process will be sent. @@ -726,7 +720,7 @@ public Proc launch(String[] cmd, boolean[] mask, String[] env, InputStream in, O * is inherited (if this is going to be launched from an agent, that * becomes the "current" process), these variables will be also set. */ - public abstract Channel launchChannel(@Nonnull String[] cmd, @Nonnull OutputStream out, + public abstract Channel launchChannel(@Nonnull String[] cmd, @Nonnull OutputStream out, @CheckForNull FilePath workDir, @Nonnull Map envVars) throws IOException, InterruptedException; /** @@ -782,7 +776,7 @@ protected final void maskedPrintCommandLine(@Nonnull List cmd, @CheckFor printCommandLine(cmd.toArray(new String[cmd.size()]),workDir); return; } - + assert mask.length == cmd.size(); final String[] masked = new String[cmd.size()]; for (int i = 0; i < cmd.size(); i++) { @@ -794,14 +788,14 @@ protected final void maskedPrintCommandLine(@Nonnull List cmd, @CheckFor } printCommandLine(masked, workDir); } - + protected final void maskedPrintCommandLine(@Nonnull String[] cmd, @Nonnull boolean[] mask, @CheckForNull FilePath workDir) { maskedPrintCommandLine(Arrays.asList(cmd),mask,workDir); } /** * Returns a decorated {@link Launcher} for the given node. - * + * * @param node Node for which this launcher is created. * @return Decorated instance of the Launcher. */ @@ -828,7 +822,7 @@ public final Launcher decorateByPrefix(final String... prefix) { public boolean isUnix() { return outer.isUnix(); } - + @Override public Proc launch(ProcStarter starter) throws IOException { starter.commands.addAll(0,Arrays.asList(prefix)); @@ -938,7 +932,7 @@ public Proc launch(ProcStarter ps) throws IOException { ps.reverseStdin ?LocalProc.SELFPUMP_INPUT:ps.stdin, ps.reverseStdout?LocalProc.SELFPUMP_OUTPUT:ps.stdout, ps.reverseStderr?LocalProc.SELFPUMP_OUTPUT:ps.stderr, - toFile(ps.pwd), ps.killWhenInterrupted); + toFile(ps.pwd), ps.dontKillWhenInterrupted); } private File toFile(FilePath f) { @@ -1042,7 +1036,7 @@ public RemoteLauncher(@Nonnull TaskListener listener, @Nonnull VirtualChannel ch @Override @Nonnull - @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", + @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "We always require nonnull channel when we initialize this launcher") public VirtualChannel getChannel() { VirtualChannel vc = super.getChannel(); @@ -1056,12 +1050,13 @@ public Proc launch(ProcStarter ps) throws IOException { final OutputStream out = ps.stdout == null ? null : new RemoteOutputStream(new CloseProofOutputStream(ps.stdout)); final OutputStream err = ps.stderr==null ? null : new RemoteOutputStream(new CloseProofOutputStream(ps.stderr)); final InputStream in = (ps.stdin==null || ps.stdin==NULL_INPUT_STREAM) ? null : new RemoteInputStream(ps.stdin,false); - + final FilePath psPwd = ps.pwd; final String workDir = psPwd==null ? null : psPwd.getRemote(); try { - return new ProcImpl(getChannel().call(new RemoteLaunchCallable(ps.commands, ps.masks, ps.envs, in, ps.reverseStdin, out, ps.reverseStdout, err, ps.reverseStderr, ps.quiet, workDir, listener, ps.killWhenInterrupted))); + return new ProcImpl(getChannel().call(new RemoteLaunchCallable(ps.commands, ps.masks, ps.envs, in, ps.reverseStdin, + out, ps.reverseStdout, err, ps.reverseStderr, ps.quiet, workDir, listener, ps.dontKillWhenInterrupted))); } catch (InterruptedException e) { throw (IOException)new InterruptedIOException().initCause(e); } @@ -1165,14 +1160,14 @@ public OutputStream getStdin() { } } } - + /** - * A launcher which delegates to a provided inner launcher. + * A launcher which delegates to a provided inner launcher. * Allows subclasses to only implement methods they want to override. - * Originally, this launcher has been implemented in + * Originally, this launcher has been implemented in * * Custom Tools Plugin. - * + * * @author rcampbell * @author Oleg Nenashev, Synopsys Inc. * @since 1.568 @@ -1236,9 +1231,9 @@ public VirtualChannel getChannel() { @Override public Proc launch(String[] cmd, String[] env, InputStream in, OutputStream out, FilePath workDir) throws IOException { - return inner.launch(cmd, env, in, out, workDir); + return inner.launch(cmd, env, in, out, workDir); } - + /** * Gets nested launcher. * @return Inner launcher @@ -1246,7 +1241,7 @@ public Proc launch(String[] cmd, String[] env, InputStream in, OutputStream out, @Nonnull public Launcher getInner() { return inner; - } + } } public static class IOTriplet implements Serializable { @@ -1263,7 +1258,7 @@ public interface RemoteProcess { int join() throws InterruptedException, IOException; void kill() throws IOException, InterruptedException; boolean isAlive() throws IOException, InterruptedException; - + @Nonnull IOTriplet getIOtriplet(); } @@ -1279,14 +1274,14 @@ private static class RemoteLaunchCallable extends MasterToSlaveCallable cmd, @CheckForNull boolean[] masks, @CheckForNull String[] env, @CheckForNull InputStream in, boolean reverseStdin, @CheckForNull OutputStream out, boolean reverseStdout, @CheckForNull OutputStream err, boolean reverseStderr, boolean quiet, @CheckForNull String workDir, - @Nonnull TaskListener listener, boolean killWhenInterrupted) { + @Nonnull TaskListener listener, boolean dontKillWhenInterrupted) { this.cmd = new ArrayList<>(cmd); this.masks = masks; this.env = env; @@ -1299,17 +1294,18 @@ private static class RemoteLaunchCallable extends MasterToSlaveCallable envOverrides; - public RemoteChannelLaunchCallable(@Nonnull String[] cmd, @Nonnull Pipe out, @Nonnull OutputStream err, + public RemoteChannelLaunchCallable(@Nonnull String[] cmd, @Nonnull Pipe out, @Nonnull OutputStream err, @CheckForNull String workDir, @Nonnull Map envOverrides) { this.cmd = cmd; this.out = out; @@ -1420,7 +1416,7 @@ private static EnvVars inherit(@Nonnull Map overrides) { m.overrideExpandingAll(overrides); return m; } - + /** * Debug option to display full current path instead of just the last token. */ diff --git a/core/src/main/java/hudson/Proc.java b/core/src/main/java/hudson/Proc.java index 58bc9f742ea8..e514b4524cd6 100644 --- a/core/src/main/java/hudson/Proc.java +++ b/core/src/main/java/hudson/Proc.java @@ -65,9 +65,9 @@ public abstract class Proc { protected Proc() {} /** - * Indicates whether the process should be killed on interruption. + * Indicates that the process should not be killed on interruption. */ - protected boolean killWhenInterrupted = true; + protected boolean dontKillWhenInterrupted; /** * Checks if the process is still alive. @@ -144,7 +144,7 @@ protected Proc() {} public abstract OutputStream getStdin(); private static final ExecutorService executor = Executors.newCachedThreadPool(new ExceptionCatchingThreadFactory(new NamingThreadFactory(new DaemonThreadFactory(), "Proc.executor"))); - + /** * Like {@link #join} but can be given a maximum time to wait. * @param timeout number of time units @@ -177,7 +177,7 @@ public void run() { latch.countDown(); } } - + /** * Locally launched process. */ @@ -212,23 +212,17 @@ public LocalProc(String[] cmd,String[] env,InputStream in,OutputStream out) thro } public LocalProc(String[] cmd,String[] env,InputStream in,OutputStream out, File workDir) throws IOException { - this(cmd,env,in,out,null,workDir); + this(cmd,env,in,out,null,workDir, true); } /** * @param err * null to redirect stderr to stdout. */ - public LocalProc(String[] cmd,String[] env,InputStream in,OutputStream out,OutputStream err,File workDir) throws IOException { + public LocalProc(String[] cmd,String[] env,InputStream in,OutputStream out,OutputStream err,File workDir, boolean dontKillWhenInterrupted) throws IOException { this( calcName(cmd), stderr(environment(new ProcessBuilder(cmd),env).directory(workDir), err==null || err== SELFPUMP_OUTPUT), - in, out, err, true ); - } - - public LocalProc(String[] cmd, String[] env, InputStream in, OutputStream out, OutputStream err, File workDir, boolean killWhenInterrupted) throws IOException { - this(calcName(cmd), - stderr(environment(new ProcessBuilder(cmd), env).directory(workDir), err == null || err == SELFPUMP_OUTPUT), - in, out, err, killWhenInterrupted); + in, out, err, dontKillWhenInterrupted); } private static ProcessBuilder stderr(ProcessBuilder pb, boolean redirectError) { @@ -248,11 +242,11 @@ private static ProcessBuilder environment(ProcessBuilder pb, String[] env) { return pb; } - private LocalProc( String name, ProcessBuilder procBuilder, InputStream in, OutputStream out, OutputStream err, boolean killWhenInterrupted ) throws IOException { + private LocalProc( String name, ProcessBuilder procBuilder, InputStream in, OutputStream out, OutputStream err, boolean dontKillWhenInterrupted ) throws IOException { Logger.getLogger(Proc.class.getName()).log(Level.FINE, "Running: {0}", name); this.name = name; this.out = out; - this.killWhenInterrupted = killWhenInterrupted; + this.dontKillWhenInterrupted = dontKillWhenInterrupted; this.cookie = EnvVars.createCookie(); procBuilder.environment().putAll(cookie); if (procBuilder.directory() != null && !procBuilder.directory().exists()) { @@ -366,7 +360,7 @@ public int join() throws InterruptedException, IOException { return r; } catch (InterruptedException e) { // aborting. kill the process - if (killWhenInterrupted) { + if (!dontKillWhenInterrupted) { destroy(); } throw e; @@ -476,7 +470,7 @@ public int join() throws IOException, InterruptedException { return process.get(); } catch (InterruptedException e) { LOGGER.log(Level.FINE, String.format("Join operation has been interrupted for the process %s. Killing the process", this), e); - if (killWhenInterrupted) { + if (!dontKillWhenInterrupted) { kill(); } throw e; @@ -519,7 +513,7 @@ public OutputStream getStdin() { * Debug switch to have the thread display the process it's waiting for. */ public static boolean SHOW_PID = false; - + /** * An instance of {@link Proc}, which has an internal workaround for JENKINS-23271. * It presumes that the instance of the object is guaranteed to be used after the {@link Proc#join()} call. From e94b721208df8a0330323ae39969855b3c367ea0 Mon Sep 17 00:00:00 2001 From: Andras Petres Date: Fri, 9 Mar 2018 12:02:40 +0200 Subject: [PATCH 3/3] Add javadoc --- core/src/main/java/hudson/Launcher.java | 82 +++++++++++++++---------- core/src/main/java/hudson/Proc.java | 7 ++- 2 files changed, 53 insertions(+), 36 deletions(-) diff --git a/core/src/main/java/hudson/Launcher.java b/core/src/main/java/hudson/Launcher.java index bc0454bfa2a0..78013e9cb3ad 100644 --- a/core/src/main/java/hudson/Launcher.java +++ b/core/src/main/java/hudson/Launcher.java @@ -80,7 +80,7 @@ * * * @author Kohsuke Kawaguchi - * @see FilePath#createLauncher(TaskListener) + * @see FilePath#createLauncher(TaskListener) */ public abstract class Launcher { @@ -119,7 +119,7 @@ public VirtualChannel getChannel() { /** * Gets the {@link TaskListener} that this launcher uses to * report the commands that it's executing. - * + * * @return Task listener */ @Nonnull @@ -181,6 +181,10 @@ public final class ProcStarter { */ protected boolean reverseStdin, reverseStdout, reverseStderr; + /** + * True to prevent killing the launched process when it is interrupted + * @since TODO + */ protected boolean dontKillWhenInterrupted; /** @@ -255,7 +259,7 @@ public boolean quiet() { /** * Sets the current directory. - * + * * @param workDir Work directory to be used. * If {@code null}, the default/current directory will be used by the process starter * @return {@code this} @@ -280,8 +284,8 @@ public FilePath pwd() { /** * Sets STDOUT destination. - * - * @param out Output stream. + * + * @param out Output stream. * Use {@code null} to send STDOUT to /dev/null. * @return {@code this} */ @@ -292,7 +296,7 @@ public ProcStarter stdout(@CheckForNull OutputStream out) { /** * Sends the stdout to the given {@link TaskListener}. - * + * * @param out Task listener * @return {@code this} */ @@ -302,7 +306,7 @@ public ProcStarter stdout(@Nonnull TaskListener out) { /** * Gets current STDOUT destination. - * + * * @return STDOUT output stream. {@code null} if STDOUT is suppressed or undefined. */ @CheckForNull @@ -321,7 +325,7 @@ public ProcStarter stderr(@CheckForNull OutputStream err) { /** * Gets current STDERR destination. - * + * * @return STDERR output stream. {@code null} if suppressed or undefined. */ @CheckForNull @@ -332,7 +336,7 @@ public OutputStream stderr() { /** * Controls where the stdin of the process comes from. * By default, /dev/null. - * + * * @return {@code this} */ @Nonnull @@ -343,7 +347,7 @@ public ProcStarter stdin(@CheckForNull InputStream in) { /** * Gets current STDIN destination. - * + * * @return STDIN output stream. {@code null} if suppressed or undefined. */ @CheckForNull @@ -358,7 +362,7 @@ public InputStream stdin() { * In addition to what the current process * is inherited (if this is going to be launched from a agent agent, that * becomes the "current" process), these variables will be also set. - * + * * @param overrides Environment variables to be overridden * @return {@code this} */ @@ -370,7 +374,7 @@ public ProcStarter envs(@Nonnull Map overrides) { /** * @param overrides * List of "VAR=VALUE". See {@link #envs(Map)} for the semantics. - * + * * @return {@code this} */ public ProcStarter envs(@CheckForNull String... overrides) { @@ -388,7 +392,7 @@ public ProcStarter envs(@CheckForNull String... overrides) { /** * Gets a list of environment variables to be set. * Returns an empty array if envs field has not been initialized. - * + * * @return If initialized, returns a copy of internal envs array. Otherwise - a new empty array. */ @Nonnull @@ -433,7 +437,7 @@ public ProcStarter readStderr() { * Indicates that the caller will directly write to the child process {@link #stdin()} via {@link Proc#getStdin()}. * (Whereas by default you call {@link #stdin(InputStream)} * and let Jenkins pump your {@link InputStream} of choosing to stdin.) - * + * * @return {@code this} * @since 1.399 */ @@ -443,6 +447,18 @@ public ProcStarter writeStdin() { return this; } + /** + * Indicates that the launched process should not be killed when interrupted. + * It allows detecting the interruption on caller's side and do custom (cleanup) action while + * the launched process is still running. + * + *

+ * Note that the process can (and should) be killed + * via {@link Proc#kill()} when custom action is done. + * + * @return {@code this} + * @since TODO + */ public ProcStarter dontKillWhenInterrupted() { this.dontKillWhenInterrupted = true; return this; @@ -465,7 +481,7 @@ public int join() throws IOException, InterruptedException { // The logging around procHolderForJoin prevents the preliminary object deallocation we saw in JENKINS-23271 final Proc procHolderForJoin = start(); LOGGER.log(Level.FINER, "Started the process {0}", procHolderForJoin); - + if (procHolderForJoin instanceof ProcWithJenkins23271Patch) { return procHolderForJoin.join(); } else { @@ -708,7 +724,7 @@ public Proc launch(String[] cmd, boolean[] mask, String[] env, InputStream in, O *

* When the returned channel is terminated, the process will be killed. * - * @param cmd + * @param cmd * The commands. * @param out * Where the stderr from the launched process will be sent. @@ -720,7 +736,7 @@ public Proc launch(String[] cmd, boolean[] mask, String[] env, InputStream in, O * is inherited (if this is going to be launched from an agent, that * becomes the "current" process), these variables will be also set. */ - public abstract Channel launchChannel(@Nonnull String[] cmd, @Nonnull OutputStream out, + public abstract Channel launchChannel(@Nonnull String[] cmd, @Nonnull OutputStream out, @CheckForNull FilePath workDir, @Nonnull Map envVars) throws IOException, InterruptedException; /** @@ -776,7 +792,7 @@ protected final void maskedPrintCommandLine(@Nonnull List cmd, @CheckFor printCommandLine(cmd.toArray(new String[cmd.size()]),workDir); return; } - + assert mask.length == cmd.size(); final String[] masked = new String[cmd.size()]; for (int i = 0; i < cmd.size(); i++) { @@ -788,14 +804,14 @@ protected final void maskedPrintCommandLine(@Nonnull List cmd, @CheckFor } printCommandLine(masked, workDir); } - + protected final void maskedPrintCommandLine(@Nonnull String[] cmd, @Nonnull boolean[] mask, @CheckForNull FilePath workDir) { maskedPrintCommandLine(Arrays.asList(cmd),mask,workDir); } /** * Returns a decorated {@link Launcher} for the given node. - * + * * @param node Node for which this launcher is created. * @return Decorated instance of the Launcher. */ @@ -822,7 +838,7 @@ public final Launcher decorateByPrefix(final String... prefix) { public boolean isUnix() { return outer.isUnix(); } - + @Override public Proc launch(ProcStarter starter) throws IOException { starter.commands.addAll(0,Arrays.asList(prefix)); @@ -1036,7 +1052,7 @@ public RemoteLauncher(@Nonnull TaskListener listener, @Nonnull VirtualChannel ch @Override @Nonnull - @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", + @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "We always require nonnull channel when we initialize this launcher") public VirtualChannel getChannel() { VirtualChannel vc = super.getChannel(); @@ -1050,7 +1066,7 @@ public Proc launch(ProcStarter ps) throws IOException { final OutputStream out = ps.stdout == null ? null : new RemoteOutputStream(new CloseProofOutputStream(ps.stdout)); final OutputStream err = ps.stderr==null ? null : new RemoteOutputStream(new CloseProofOutputStream(ps.stderr)); final InputStream in = (ps.stdin==null || ps.stdin==NULL_INPUT_STREAM) ? null : new RemoteInputStream(ps.stdin,false); - + final FilePath psPwd = ps.pwd; final String workDir = psPwd==null ? null : psPwd.getRemote(); @@ -1160,14 +1176,14 @@ public OutputStream getStdin() { } } } - + /** - * A launcher which delegates to a provided inner launcher. + * A launcher which delegates to a provided inner launcher. * Allows subclasses to only implement methods they want to override. - * Originally, this launcher has been implemented in + * Originally, this launcher has been implemented in * * Custom Tools Plugin. - * + * * @author rcampbell * @author Oleg Nenashev, Synopsys Inc. * @since 1.568 @@ -1231,9 +1247,9 @@ public VirtualChannel getChannel() { @Override public Proc launch(String[] cmd, String[] env, InputStream in, OutputStream out, FilePath workDir) throws IOException { - return inner.launch(cmd, env, in, out, workDir); + return inner.launch(cmd, env, in, out, workDir); } - + /** * Gets nested launcher. * @return Inner launcher @@ -1241,7 +1257,7 @@ public Proc launch(String[] cmd, String[] env, InputStream in, OutputStream out, @Nonnull public Launcher getInner() { return inner; - } + } } public static class IOTriplet implements Serializable { @@ -1258,7 +1274,7 @@ public interface RemoteProcess { int join() throws InterruptedException, IOException; void kill() throws IOException, InterruptedException; boolean isAlive() throws IOException, InterruptedException; - + @Nonnull IOTriplet getIOtriplet(); } @@ -1364,7 +1380,7 @@ private static class RemoteChannelLaunchCallable extends MasterToSlaveCallable envOverrides; - public RemoteChannelLaunchCallable(@Nonnull String[] cmd, @Nonnull Pipe out, @Nonnull OutputStream err, + public RemoteChannelLaunchCallable(@Nonnull String[] cmd, @Nonnull Pipe out, @Nonnull OutputStream err, @CheckForNull String workDir, @Nonnull Map envOverrides) { this.cmd = cmd; this.out = out; @@ -1416,7 +1432,7 @@ private static EnvVars inherit(@Nonnull Map overrides) { m.overrideExpandingAll(overrides); return m; } - + /** * Debug option to display full current path instead of just the last token. */ diff --git a/core/src/main/java/hudson/Proc.java b/core/src/main/java/hudson/Proc.java index e514b4524cd6..f23116a02c8e 100644 --- a/core/src/main/java/hudson/Proc.java +++ b/core/src/main/java/hudson/Proc.java @@ -66,6 +66,7 @@ protected Proc() {} /** * Indicates that the process should not be killed on interruption. + * @since TODO */ protected boolean dontKillWhenInterrupted; @@ -144,7 +145,7 @@ protected Proc() {} public abstract OutputStream getStdin(); private static final ExecutorService executor = Executors.newCachedThreadPool(new ExceptionCatchingThreadFactory(new NamingThreadFactory(new DaemonThreadFactory(), "Proc.executor"))); - + /** * Like {@link #join} but can be given a maximum time to wait. * @param timeout number of time units @@ -177,7 +178,7 @@ public void run() { latch.countDown(); } } - + /** * Locally launched process. */ @@ -513,7 +514,7 @@ public OutputStream getStdin() { * Debug switch to have the thread display the process it's waiting for. */ public static boolean SHOW_PID = false; - + /** * An instance of {@link Proc}, which has an internal workaround for JENKINS-23271. * It presumes that the instance of the object is guaranteed to be used after the {@link Proc#join()} call.