hudson.remoting.MultiClassLoaderSerializer Maven / Gradle / Ivy
package hudson.remoting;
import hudson.remoting.RemoteClassLoader.IClassLoader;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.OutputStream;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* {@link ObjectInputStream}/{@link ObjectOutputStream} pair that can handle object graph that spans across
* multiple classloaders.
*
*
* To pass around ClassLoaders, this class uses OID instead of {@link IClassLoader}, since doing so
* can results in recursive class resolution that may end up with NPE in ObjectInputStream.defaultReadFields like
* described in the comment from huybrechts in HUDSON-4293.
*
* @author Kohsuke Kawaguchi
* @see Capability#supportsMultiClassLoaderRPC()
*/
class MultiClassLoaderSerializer {
static final class Output extends ObjectOutputStream {
private final Channel channel;
/**
* Encountered Classloaders, to their indices.
*/
private final Map classLoaders = new HashMap();
Output(Channel channel, OutputStream out) throws IOException {
super(out);
this.channel = channel;
}
@Override
protected void annotateClass(Class> c) throws IOException {
ClassLoader cl = c.getClassLoader();
if (cl==null) {// bootstrap classloader. no need to export.
writeInt(TAG_SYSTEMCLASSLOADER);
return;
}
Integer idx = classLoaders.get(cl);
if (idx==null) {
classLoaders.put(cl,classLoaders.size());
if (cl instanceof RemoteClassLoader) {
int oid = ((RemoteClassLoader) cl).getOid(channel);
if (oid>=0) {
// this classloader came from where we are sending this classloader to.
writeInt(TAG_LOCAL_CLASSLOADER);
writeInt(oid);
return;
}
}
// tell the receiving side that they need to import a new classloader
writeInt(TAG_EXPORTED_CLASSLOADER);
writeInt(RemoteClassLoader.exportId(cl,channel));
} else {// reference to a classloader that's already written
writeInt(idx);
}
}
@Override
protected void annotateProxyClass(Class> cl) throws IOException {
annotateClass(cl);
}
}
static final class Input extends ObjectInputStream {
private final Channel channel;
private final List classLoaders = new ArrayList();
Input(Channel channel, InputStream in) throws IOException {
super(in);
this.channel = channel;
}
private ClassLoader readClassLoader() throws IOException, ClassNotFoundException {
ClassLoader cl;
int code = readInt();
switch (code) {
case TAG_SYSTEMCLASSLOADER:
return null;
case TAG_LOCAL_CLASSLOADER:
cl = ((RemoteClassLoader.ClassLoaderProxy)channel.getExportedObject(readInt())).cl;
classLoaders.add(cl);
return cl;
case TAG_EXPORTED_CLASSLOADER:
cl = channel.importedClassLoaders.get(readInt());
classLoaders.add(cl);
return cl;
default:
return classLoaders.get(code);
}
}
@Override
protected Class> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
String name = desc.getName();
try {
ClassLoader cl = readClassLoader();
Class> c = Class.forName(name, false, cl);
return c;
} catch (ClassNotFoundException ex) {
return super.resolveClass(desc);
}
}
@Override
protected Class> resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException {
ClassLoader cl = readClassLoader();
Class[] classes = new Class[interfaces.length];
for (int i = 0; i < interfaces.length; i++)
classes[i] = Class.forName(interfaces[i], false, cl);
return Proxy.getProxyClass(cl, classes);
}
}
/**
* Indicates that the class being sent should be loaded from the system classloader.
*/
private static final int TAG_SYSTEMCLASSLOADER = -3;
/**
* Indicates that the class being sent originates from the sender side. The sender exports this classloader
* and sends its OID in the following int. The receiver will import this classloader to resolve the class.
*/
private static final int TAG_EXPORTED_CLASSLOADER = -2;
/**
* Indicates that the class being sent originally came from the receiver. The following int indicates
* the OID of the classloader exported from the receiver, which the sender used.
*/
private static final int TAG_LOCAL_CLASSLOADER = -1;
}