diff --git a/remoting/src/main/java/hudson/remoting/Channel.java b/remoting/src/main/java/hudson/remoting/Channel.java index 6fa1395f22584869d5963eb6a89381bb0d375612..54d77b50ae4cfc4e8413be08a4c1a9295896786d 100644 --- a/remoting/src/main/java/hudson/remoting/Channel.java +++ b/remoting/src/main/java/hudson/remoting/Channel.java @@ -91,6 +91,8 @@ public class Channel { * same method on the given instance object. */ /*package*/ synchronized T export(Class type, T instance) { + if(instance==null) + return null; // TODO: unexport final int id = exportedObjects.intern(instance); @@ -154,6 +156,10 @@ public class Channel { /** * Notifies the remote peer that we are closing down. + * + * Execution of this command also triggers the {@link ReaderThread} to shut down + * and quit. The {@link CloseCommand} is always the last command to be sent on + * {@link ObjectOutputStream}, and it's the last command to be read. */ private static final class CloseCommand extends Command { protected void execute(Channel channel) { @@ -175,15 +181,19 @@ public class Channel { public void close() throws IOException { if(closed) return; - send(new CloseCommand()); - - // TODO: would be nice if we can wait for the completion of pending requests - terminate(null); + // make sure no other commands get executed in between. + synchronized(this) { + send(new CloseCommand()); + oos.close(); + + // TODO: would be nice if we can wait for the completion of pending requests + terminate(null); + } } private final class ReaderThread extends Thread { public ReaderThread(String name) { - super("DataChannel reader thread: "+name); + super("Channel reader thread: "+name); } public void run() { @@ -204,9 +214,8 @@ public class Channel { } } ois.close(); - oos.close(); } catch (IOException e) { - logger.log(Level.SEVERE, "I/O error in DataChannel",e); + logger.log(Level.SEVERE, "I/O error in channel",e); terminate(e); } } diff --git a/remoting/src/main/java/hudson/remoting/CloseCommand.java b/remoting/src/main/java/hudson/remoting/CloseCommand.java deleted file mode 100644 index 8bf59fa14139c6981f2e992de93b48c0db5c9245..0000000000000000000000000000000000000000 --- a/remoting/src/main/java/hudson/remoting/CloseCommand.java +++ /dev/null @@ -1,5 +0,0 @@ -package hudson.remoting; - -/** - * @author Kohsuke Kawaguchi - */ diff --git a/remoting/src/main/java/hudson/remoting/ExportTable.java b/remoting/src/main/java/hudson/remoting/ExportTable.java index 8dd7bc8d2dc5bfaedb36ce35a90a2712fe8d6017..eb22248c6b76564da9b3a261e19895932f25109e 100644 --- a/remoting/src/main/java/hudson/remoting/ExportTable.java +++ b/remoting/src/main/java/hudson/remoting/ExportTable.java @@ -1,39 +1,41 @@ package hudson.remoting; -import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Map; -import java.util.WeakHashMap; /** - * Manages unique ID for classloaders. + * Manages unique ID for exported objects. * * @author Kohsuke Kawaguchi */ final class ExportTable { - private final Map> table = new HashMap>(); - private final WeakHashMap reverse = new WeakHashMap(); + private final Map table = new HashMap(); + private final Map reverse = new HashMap(); // id==0 is reserved for bootstrap classloader private int iota = 1; + public synchronized int intern(T t) { + if(t==null) return 0; // bootstrap classloader - public synchronized int intern(T cl) { - if(cl==null) return 0; // bootstrap classloader - - Integer id = reverse.get(cl); + Integer id = reverse.get(t); if(id==null) { id = iota++; - table.put(id,new WeakReference(cl)); - reverse.put(cl,id); + table.put(id,t); + reverse.put(t,id); } return id; } public synchronized T get(int id) { - WeakReference ref = table.get(id); - if(ref==null) return null; - return ref.get(); + return table.get(id); + } + + public synchronized void unexport(T t) { + if(t==null) return; + Integer id = reverse.remove(t); + if(id==null) return; // presumably already unexported + table.remove(id); } } diff --git a/remoting/src/main/java/hudson/remoting/FutureAdapter.java b/remoting/src/main/java/hudson/remoting/FutureAdapter.java index 9d2e6632acf33393674ad21273c777dd715c2f3f..76f9d0d91b8cfa13ca316b3c3e4419c0e90137b8 100644 --- a/remoting/src/main/java/hudson/remoting/FutureAdapter.java +++ b/remoting/src/main/java/hudson/remoting/FutureAdapter.java @@ -1,7 +1,7 @@ package hudson.remoting; -import java.util.concurrent.Future; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; diff --git a/remoting/src/main/java/hudson/remoting/Pipe.java b/remoting/src/main/java/hudson/remoting/Pipe.java index d6adae4117abc0f61acf1079dc04519d93da8424..ae38e246cea5deaef761010afca4dd01365d869c 100644 --- a/remoting/src/main/java/hudson/remoting/Pipe.java +++ b/remoting/src/main/java/hudson/remoting/Pipe.java @@ -1,14 +1,14 @@ package hudson.remoting; +import java.io.BufferedOutputStream; +import java.io.IOException; import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.OutputStream; -import java.io.IOException; import java.io.PipedInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.io.ObjectInputStream; -import java.io.BufferedOutputStream; import java.io.PipedOutputStream; +import java.io.Serializable; /** * @author Kohsuke Kawaguchi diff --git a/remoting/src/main/java/hudson/remoting/RemoteInvocationHandler.java b/remoting/src/main/java/hudson/remoting/RemoteInvocationHandler.java index 9929616ad77878a4c677ad42616ea63690e54c2f..b5a276c61f90c4056ac9f718b81e6ecce11d2735 100644 --- a/remoting/src/main/java/hudson/remoting/RemoteInvocationHandler.java +++ b/remoting/src/main/java/hudson/remoting/RemoteInvocationHandler.java @@ -1,11 +1,11 @@ package hudson.remoting; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.Serializable; import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; -import java.io.Serializable; -import java.io.ObjectInputStream; -import java.io.IOException; +import java.lang.reflect.Method; /** * Sits behind a proxy object and implements the proxy logic. @@ -104,6 +104,8 @@ final class RemoteInvocationHandler implements InvocationHandler, Serializable { protected Serializable perform(Channel channel) throws Throwable { Object o = channel.exportedObjects.get(oid); + if(o==null) + throw new IllegalStateException("Unable to call "+methodName+". Invalid object ID "+oid); try { return (Serializable)choose(o).invoke(o,arguments); } catch (InvocationTargetException e) { diff --git a/remoting/src/main/java/hudson/remoting/Request.java b/remoting/src/main/java/hudson/remoting/Request.java index 537793c11e95617937defc68210dc9d3f0acfd90..d0a3230c76933ed23ffa16f9fb4ad165451dc9d3 100644 --- a/remoting/src/main/java/hudson/remoting/Request.java +++ b/remoting/src/main/java/hudson/remoting/Request.java @@ -2,12 +2,12 @@ package hudson.remoting; import java.io.IOException; import java.io.Serializable; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.concurrent.Future; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.logging.Level; +import java.util.logging.Logger; /** * Request/response pattern over {@link Command}.