From bfeb24f7dacb153975f782373da8ac9b7609bdd2 Mon Sep 17 00:00:00 2001 From: Oleg Nenashev Date: Thu, 16 Nov 2017 10:53:18 +0100 Subject: [PATCH 1/4] [JENKINS-47896] - Introduce SerializableOnlyOverRemoting in classes. Due to the implementation specifics, some classes in the core are serializable only over Remoting. This change just marks these classes and utilizes the convenience method in the interface for serialization/deserialization operations. --- core/src/main/java/hudson/FilePath.java | 12 ++++++------ core/src/main/java/hudson/cli/CliManagerImpl.java | 12 +++++++----- core/src/main/java/hudson/util/ProcessTree.java | 9 +++++---- .../main/java/hudson/util/StreamTaskListener.java | 4 +++- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index ad13edf5f338..c9fbc4398406 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -118,6 +118,7 @@ import org.apache.tools.zip.ZipFile; import org.jenkinsci.remoting.RoleChecker; import org.jenkinsci.remoting.RoleSensitive; +import org.jenkinsci.remoting.SerializableOnlyOverRemoting; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.Stapler; @@ -193,7 +194,7 @@ * @author Kohsuke Kawaguchi * @see VirtualFile */ -public final class FilePath implements Serializable { +public final class FilePath implements SerializableOnlyOverRemoting { /** * Maximum http redirects we will follow. This defaults to the same number as Firefox/Chrome tolerates. */ @@ -2705,18 +2706,17 @@ public boolean isRemote() { } private void writeObject(ObjectOutputStream oos) throws IOException { - Channel target = Channel.current(); - - if(channel!=null && channel!=target) + Channel target = getChannelForSerialization(); + if(channel!=target) { throw new IllegalStateException("Can't send a remote FilePath to a different remote channel"); + } oos.defaultWriteObject(); oos.writeBoolean(channel==null); } private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { - Channel channel = Channel.current(); - assert channel!=null; + Channel channel = getChannelForSerialization(); ois.defaultReadObject(); if(ois.readBoolean()) { diff --git a/core/src/main/java/hudson/cli/CliManagerImpl.java b/core/src/main/java/hudson/cli/CliManagerImpl.java index 750a38d210af..6b8f606011c7 100644 --- a/core/src/main/java/hudson/cli/CliManagerImpl.java +++ b/core/src/main/java/hudson/cli/CliManagerImpl.java @@ -29,8 +29,10 @@ import org.acegisecurity.Authentication; import org.acegisecurity.context.SecurityContext; import org.acegisecurity.context.SecurityContextHolder; +import org.jenkinsci.remoting.SerializableOnlyOverRemoting; import java.io.InputStream; +import java.io.ObjectStreamException; import java.io.OutputStream; import java.io.PrintStream; import java.io.Serializable; @@ -47,7 +49,7 @@ * @deprecated Specific to Remoting-based protocol. */ @Deprecated -public class CliManagerImpl implements CliEntryPoint, Serializable { +public class CliManagerImpl implements CliEntryPoint, SerializableOnlyOverRemoting { private transient final Channel channel; private Authentication transportAuth; @@ -87,10 +89,10 @@ public int main(List args, Locale locale, InputStream stdin, OutputStrea String subCmd = args.get(0); CLICommand cmd = CLICommand.clone(subCmd); if(cmd!=null) { - cmd.channel = Channel.current(); + cmd.channel = Channel.currentOrFail(); final CLICommand old = CLICommand.setCurrent(cmd); try { - transportAuth = Channel.current().getProperty(CLICommand.TRANSPORT_AUTHENTICATION); + transportAuth = cmd.channel.getProperty(CLICommand.TRANSPORT_AUTHENTICATION); cmd.setTransportAuth(transportAuth); return cmd.main(args.subList(1,args.size()),locale, stdin, out, err); } finally { @@ -126,8 +128,8 @@ public int protocolVersion() { return VERSION; } - private Object writeReplace() { - return Channel.current().export(CliEntryPoint.class,this); + private Object writeReplace() throws ObjectStreamException { + return getChannelForSerialization().export(CliEntryPoint.class,this); } private static final Logger LOGGER = Logger.getLogger(CliManagerImpl.class.getName()); diff --git a/core/src/main/java/hudson/util/ProcessTree.java b/core/src/main/java/hudson/util/ProcessTree.java index 95fe237fc322..398e3094154f 100644 --- a/core/src/main/java/hudson/util/ProcessTree.java +++ b/core/src/main/java/hudson/util/ProcessTree.java @@ -40,6 +40,7 @@ import hudson.util.ProcessTreeRemoting.IOSProcess; import hudson.util.ProcessTreeRemoting.IProcessTree; import jenkins.security.SlaveToMasterCallable; +import org.jenkinsci.remoting.SerializableOnlyOverRemoting; import org.jvnet.winp.WinProcess; import org.jvnet.winp.WinpException; @@ -84,7 +85,7 @@ * @author Kohsuke Kawaguchi * @since 1.315 */ -public abstract class ProcessTree implements Iterable, IProcessTree, Serializable { +public abstract class ProcessTree implements Iterable, IProcessTree, SerializableOnlyOverRemoting { /** * To be filled in the constructor of the derived type. */ @@ -1317,7 +1318,7 @@ public static abstract class Local extends ProcessTree { /** * Represents a process tree over a channel. */ - public static class Remote extends ProcessTree implements Serializable { + public static class Remote extends ProcessTree { private final IProcessTree proxy; public Remote(ProcessTree proxy, Channel ch) { @@ -1388,8 +1389,8 @@ public T act(ProcessCallable callable) throws IOException, InterruptedExc /** * Use {@link Remote} as the serialized form. */ - /*package*/ Object writeReplace() { - return new Remote(this,Channel.current()); + /*package*/ Object writeReplace() throws ObjectStreamException { + return new Remote(this, getChannelForSerialization()); } // public static void main(String[] args) { diff --git a/core/src/main/java/hudson/util/StreamTaskListener.java b/core/src/main/java/hudson/util/StreamTaskListener.java index 9deea8fad0e3..2723aad5f433 100644 --- a/core/src/main/java/hudson/util/StreamTaskListener.java +++ b/core/src/main/java/hudson/util/StreamTaskListener.java @@ -48,6 +48,8 @@ import java.nio.file.StandardOpenOption; import java.util.logging.Level; import java.util.logging.Logger; + +import org.jenkinsci.remoting.SerializableOnlyOverRemoting; import org.kohsuke.stapler.framework.io.WriterOutputStream; /** @@ -58,7 +60,7 @@ * * @author Kohsuke Kawaguchi */ -public class StreamTaskListener extends AbstractTaskListener implements Serializable, Closeable { +public class StreamTaskListener extends AbstractTaskListener implements SerializableOnlyOverRemoting, Closeable { private PrintStream out; private Charset charset; From 09b9d227af324f1cad71ba39c8580abffb1a945b Mon Sep 17 00:00:00 2001 From: Oleg Nenashev Date: Mon, 28 Jan 2019 17:48:55 +0100 Subject: [PATCH 2/4] Add import java.io.ObjectStreamException; --- core/src/main/java/hudson/util/ProcessTree.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/hudson/util/ProcessTree.java b/core/src/main/java/hudson/util/ProcessTree.java index 1bf9b9038046..7397f9881549 100644 --- a/core/src/main/java/hudson/util/ProcessTree.java +++ b/core/src/main/java/hudson/util/ProcessTree.java @@ -55,6 +55,7 @@ import java.io.InputStream; import java.io.RandomAccessFile; import java.io.Serializable; +import java.io.ObjectStreamException; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; From 78423ca29290529e533b700843ae13ace3d3eb2c Mon Sep 17 00:00:00 2001 From: Oleg Nenashev Date: Thu, 31 Jan 2019 01:47:50 +0100 Subject: [PATCH 3/4] [JENKINS-47896] - Add some diagnostics for the failing channel comparison in FilePath --- core/src/main/java/hudson/FilePath.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index 9cb3835dc911..5a02e2251e45 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -2988,7 +2988,7 @@ public boolean isRemote() { private void writeObject(ObjectOutputStream oos) throws IOException { Channel target = getChannelForSerialization(); if(channel!=target) { - throw new IllegalStateException("Can't send a remote FilePath to a different remote channel"); + throw new IllegalStateException("Can't send a remote FilePath to a different remote channel (current=" + channel + ", target=" + target + ")"); } oos.defaultWriteObject(); From 34fb3c86c752d5b61f1b8c31988be31d63e5e7e2 Mon Sep 17 00:00:00 2001 From: Oleg Nenashev Date: Sun, 3 Feb 2019 02:06:41 +0100 Subject: [PATCH 4/4] [JENKINS-47896] - Allow FilePath with null channel to be serialized anywhere --- core/src/main/java/hudson/FilePath.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index 5a02e2251e45..e49a4e59b1ce 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -2987,7 +2987,7 @@ public boolean isRemote() { private void writeObject(ObjectOutputStream oos) throws IOException { Channel target = getChannelForSerialization(); - if(channel!=target) { + if (channel != null && channel != target) { throw new IllegalStateException("Can't send a remote FilePath to a different remote channel (current=" + channel + ", target=" + target + ")"); }