提交 5768da22 编写于 作者: K kohsuke

Fixed a NoClassDefFoundError problem that happens in remoting+maven+3rd plugin combo.

    <a href="http://www.nabble.com/NoClassDefFoundError%3A-hudson-maven-MavenBuildProxy%24BuildCallable-td24719002.html#a24719002">report</a>


git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@21002 71c3de6d-444a-0410-be80-ed276b4c234a
上级 3a86ab8c
package hudson.remoting;
import java.io.Serializable;
/**
* Represents additional features implemented on {@link Channel}.
*
* <p>
* Each {@link Channel} exposes its capability to {@link Channel#getProperty(Object)}.
*
* <p>
* This mechanism allows two different versions of <tt>remoting.jar</tt> to talk to each other.
*
* @author Kohsuke Kawaguchi
* @see Channel#remoteCapability
*/
final class Capability implements Serializable {
/**
* Bit mask of optional capabilities.
*/
private final long mask;
Capability(long mask) {
this.mask = mask;
}
Capability() {
this(1);
}
/**
* Does this implementation supports multi-classloader serialization in
* {@link UserRequest}?
*
* @see MultiClassLoaderSerializer
*/
boolean supportsMultiClassLoaderRPC() {
return (mask&1)!=0;
}
private static final long serialVersionUID = 1L;
}
......@@ -184,6 +184,11 @@ public class Channel implements VirtualChannel, IChannel {
*/
private IChannel remoteChannel;
/**
* Capability of the remote {@link Channel}.
*/
final Capability remoteCapability;
/**
* Communication mode.
* @since 1.161
......@@ -285,18 +290,20 @@ public class Channel implements VirtualChannel, IChannel {
this.name = name;
this.executor = exec;
this.isRestricted = restricted;
ObjectOutputStream oos = null;
if(export(this,false)!=1)
throw new AssertionError(); // export number 1 is reserved for the channel itself
remoteChannel = RemoteInvocationHandler.wrap(this,1,IChannel.class,false,false);
properties.put(Capability.class,new Capability()); // export our capability
// write the magic preamble.
// certain communication channel, such as forking JVM via ssh,
// may produce some garbage at the beginning (for example a remote machine
// might print some warning before the program starts outputting its own data.)
//
// so use magic preamble and discard all the data up to that to improve robustness.
ObjectOutputStream oos = null;
if(mode!= Mode.NEGOTIATE) {
os.write(mode.preamble);
oos = new ObjectOutputStream(mode.wrap(os));
......@@ -331,6 +338,11 @@ public class Channel implements VirtualChannel, IChannel {
this.ois = new ObjectInputStream(mode.wrap(is));
new ReaderThread(name).start();
Capability rc = (Capability) getRemoteProperty(Capability.class);
if (rc==null) rc = new Capability(0); // assume no capability
this.remoteCapability = rc;
return;
}
} else {
......
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.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* {@link ObjectInputStream}/{@link ObjectOutputStream} pair that can handle object graph that spans across
* multiple classloaders.
*
* @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<ClassLoader,Integer> classLoaders = new HashMap<ClassLoader, Integer>();
Output(Channel channel, OutputStream out) throws IOException {
super(out);
this.channel = channel;
}
protected void annotateClass(Class<?> c) throws IOException {
ClassLoader cl = c.getClassLoader();
if (cl==null) {// bootstrap classloader. no need to export.
writeInt(-2);
return;
}
Integer idx = classLoaders.get(cl);
if (idx==null) {
classLoaders.put(cl,classLoaders.size());
writeInt(-1);
writeObject(RemoteClassLoader.export(cl,channel));
} else {
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<ClassLoader> classLoaders = new ArrayList<ClassLoader>();
Input(Channel channel, InputStream in) throws IOException {
super(in);
this.channel = channel;
}
private ClassLoader readClassLoader() throws IOException, ClassNotFoundException {
int code = readInt();
switch (code) {
case -2:
return null;
case -1:
// fill the entry with some value in preparation of recursive readObject below.
// this is actually only necessary for classLoader[0].
classLoaders.add(Channel.class.getClassLoader());
ClassLoader cl = channel.importedClassLoaders.get((IClassLoader) readObject());
classLoaders.set(classLoaders.size()-1,cl);
return cl;
default:
return classLoaders.get(code);
}
}
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
String name = desc.getName();
try {
return Class.forName(name, false, readClassLoader());
} 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);
}
}
}
......@@ -29,13 +29,11 @@ import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.HashMap;
/**
* {@link ObjectInputStream} that uses a specific class loader.
*/
final class ObjectInputStreamEx extends ObjectInputStream {
class ObjectInputStreamEx extends ObjectInputStream {
private final ClassLoader cl;
public ObjectInputStreamEx(InputStream in, ClassLoader cl) throws IOException {
......
......@@ -33,6 +33,7 @@ import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.io.NotSerializableException;
import java.io.ObjectInputStream;
/**
* {@link Request} that can take {@link Callable} whose actual implementation
......@@ -84,7 +85,7 @@ final class UserRequest<RSP,EXC extends Throwable> extends Request<UserResponse<
try {
Object o;
try {
o = new ObjectInputStreamEx(new ByteArrayInputStream(request), cl).readObject();
o = deserialize(channel,request,cl);
} catch (ClassNotFoundException e) {
throw new ClassNotFoundException("Failed to deserialize the Callable object. Perhaps you needed to implement DelegatingCallable?",e);
}
......@@ -127,11 +128,17 @@ final class UserRequest<RSP,EXC extends Throwable> extends Request<UserResponse<
}
}
private byte[] _serialize(Object o, Channel localChannel) throws IOException {
Channel old = Channel.setCurrent(localChannel);
private byte[] _serialize(Object o, final Channel channel) throws IOException {
Channel old = Channel.setCurrent(channel);
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
new ObjectOutputStream(baos).writeObject(o);
ObjectOutputStream oos;
if (channel.remoteCapability.supportsMultiClassLoaderRPC())
oos = new MultiClassLoaderSerializer.Output(channel,baos);
else
oos = new ObjectOutputStream(baos);
oos.writeObject(o);
return baos.toByteArray();
} finally {
Channel.setCurrent(old);
......@@ -148,6 +155,19 @@ final class UserRequest<RSP,EXC extends Throwable> extends Request<UserResponse<
}
}
/*package*/ static Object deserialize(final Channel channel, byte[] data, ClassLoader defaultClassLoader) throws IOException, ClassNotFoundException {
ByteArrayInputStream in = new ByteArrayInputStream(data);
ObjectInputStream ois;
if (channel.remoteCapability.supportsMultiClassLoaderRPC()) {
// this code is coupled with the ObjectOutputStream subtype above
ois = new MultiClassLoaderSerializer.Input(channel, in);
} else {
ois = new ObjectInputStreamEx(in, defaultClassLoader);
}
return ois.readObject();
}
public void releaseExports() {
exports.release();
}
......@@ -174,7 +194,7 @@ final class UserResponse<RSP,EXC extends Throwable> implements Serializable {
public RSP retrieve(Channel channel, ClassLoader cl) throws IOException, ClassNotFoundException, EXC {
Channel old = Channel.setCurrent(channel);
try {
Object o = new ObjectInputStreamEx(new ByteArrayInputStream(response), cl).readObject();
Object o = UserRequest.deserialize(channel,response,cl);
if(isException)
throw (EXC)o;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册