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}.