Modified: 
river/jtsk/branches/2.2/src/com/sun/jini/jeri/internal/runtime/ObjectTable.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/branches/2.2/src/com/sun/jini/jeri/internal/runtime/ObjectTable.java?rev=1137619&r1=1137618&r2=1137619&view=diff
==============================================================================
--- 
river/jtsk/branches/2.2/src/com/sun/jini/jeri/internal/runtime/ObjectTable.java 
(original)
+++ 
river/jtsk/branches/2.2/src/com/sun/jini/jeri/internal/runtime/ObjectTable.java 
Mon Jun 20 13:00:41 2011
@@ -19,41 +19,19 @@
 package com.sun.jini.jeri.internal.runtime;
 
 import com.sun.jini.jeri.internal.runtime.ImplRefManager.ImplRef;
-import com.sun.jini.logging.Levels;
 import com.sun.jini.thread.NewThreadAction;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.ObjectInputStream;
-import java.io.OutputStream;
-import java.lang.reflect.Method;
 import java.rmi.Remote;
 import java.rmi.server.ExportException;
 import java.rmi.server.Unreferenced;
 import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.security.PrivilegedExceptionAction;
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-import net.jini.export.ServerContext;
 import net.jini.id.Uuid;
-import net.jini.id.UuidFactory;
-import net.jini.io.MarshalInputStream;
-import net.jini.io.UnsupportedConstraintException;
-import net.jini.jeri.BasicInvocationDispatcher;
-import net.jini.jeri.InvocationDispatcher;
-import net.jini.jeri.InboundRequest;
 import net.jini.jeri.RequestDispatcher;
-import net.jini.jeri.ServerCapabilities;
-import net.jini.core.constraint.InvocationConstraints;
 import net.jini.security.Security;
 import net.jini.security.SecurityContext;
 
@@ -67,66 +45,48 @@ final class ObjectTable {
     private static final Logger logger =
        Logger.getLogger("net.jini.jeri.BasicJeriExporter");
 
-    private static final Collection dgcDispatcherMethods = new ArrayList(2);
-    static {
-       Method[] methods = DgcServer.class.getMethods();
-       for (int i = 0; i < methods.length; i++) {
-           final Method m = methods[i];
-           AccessController.doPrivileged(new PrivilegedAction() {
-               public Object run() {
-                   m.setAccessible(true);
-                   return null;
-               }
-           });
-           dgcDispatcherMethods.add(m);
-       }
-    }
-
-    private static final ServerCapabilities dgcServerCapabilities =
-       new ServerCapabilities() {
-           public InvocationConstraints checkConstraints(
-               InvocationConstraints constraints)
-               throws UnsupportedConstraintException
-           {
-               assert constraints.equals(InvocationConstraints.EMPTY);
-               return InvocationConstraints.EMPTY;
-           }
-       };
-
     /**
      * lock to serialize request dispatcher reservation per export, so
      * that a partial export will not cause another export to fail
      * unnecessarily
      **/
-    private final Object requestDispatchersLock = new Object();
+    private final Object requestDispatchersLock;
 
     /** table of references to impls exported with DGC */
-    private final ImplRefManager implRefManager = new ImplRefManager();
-
-    /** lock guarding keepAliveCount and keeper */
-    private final Object keepAliveLock = new Object();
+    private final ImplRefManager implRefManager;
 
     /** number of objects exported with keepAlive == true */
-    private int keepAliveCount = 0;
-
-    /** thread to keep VM alive while keepAliveCount > 0 */
-    private Thread keeper = null;
+    private final JvmLifeSupport keepAliveCount;
 
     /** maps client ID to Lease (lock guards leaseChecker too) */
-    private final Map leaseTable = new HashMap();
+    private final ConcurrentMap<Uuid,Lease> leaseTable;
 
     /** thread to check for expired leases */
-    private Thread leaseChecker = null;
-
-    ObjectTable() { }
+    private Thread leaseChecker;
+    
+    /** thread guard */
+    private Boolean running;
+
+    ObjectTable() { 
+        requestDispatchersLock = new Object();
+        implRefManager = new ImplRefManager();
+        keepAliveCount = new JvmLifeSupport();
+        leaseTable = new ConcurrentHashMap<Uuid,Lease>(256);//Plenty of 
capacity to reduce resizing.
+        leaseChecker = null;
+        running = Boolean.FALSE;
+    }
 
     RequestDispatcher createRequestDispatcher(Unreferenced unrefCallback) {
-       return new RD(unrefCallback);
+       return new DgcRequestDispatcher(unrefCallback, this);
     }
 
     boolean isReferenced(RequestDispatcher requestDispatcher) {
        return getRD(requestDispatcher).isReferenced();
     }
+    
+    DgcServer getDgcServer(DgcRequestDispatcher dgdRD){
+        return new DgcServerImpl(dgdRD);
+    }
 
     Target export(Remote impl,
                  RequestDispatcher[] requestDispatchers,
@@ -135,21 +95,31 @@ final class ObjectTable {
                  Uuid id)
         throws ExportException
     {
-       RD[] rds = new RD[requestDispatchers.length];
+       DgcRequestDispatcher[] rds = new 
DgcRequestDispatcher[requestDispatchers.length];
        for (int i = 0; i < requestDispatchers.length; i++) {
            rds[i] = getRD(requestDispatchers[i]);
        }
-
-       return new Target(impl, id, rds, allowDGC, keepAlive);
+        SecurityContext securityContext = Security.getContext();
+        ClassLoader ccl = Thread.currentThread().getContextClassLoader();
+        Target t = null;
+        t = new Target(id, rds, allowDGC, keepAlive,this,
+                securityContext, ccl, keepAliveCount);
+        synchronized (requestDispatchersLock){
+            t.procRequestDispatchers();
+        }
+        ImplRef implRef = implRefManager.getImplRef(impl, t);
+        t.setImplRef(implRef);
+        t.setExported();
+       return t;
     }
 
-    private RD getRD(RequestDispatcher requestDispatcher) {
+    private DgcRequestDispatcher getRD(RequestDispatcher requestDispatcher) {
        /*
         * The following cast will throw a ClassCastException if we were
         * passed a RequestDispatcher that was not returned by this class's
         * createRequestDispatcher method:
         */
-       RD rd = (RD) requestDispatcher;
+       DgcRequestDispatcher rd = (DgcRequestDispatcher) requestDispatcher;
        if (!rd.forTable(this)) {
            throw new IllegalArgumentException(
                "request dispatcher for different object table");
@@ -157,813 +127,245 @@ final class ObjectTable {
        return rd;
     }
 
-    /**
-     * Increments the count of objects exported with keepAlive true,
-     * starting a non-daemon thread if necessary.
-     **/
-    private void incrementKeepAliveCount() {
-       synchronized (keepAliveLock) {
-           keepAliveCount++;
-
-           if (keeper == null) {
-               keeper = (Thread) AccessController.doPrivileged(
-                   new NewThreadAction(new Runnable() {
-                       public void run() {
-                           try {
-                               while (true) {
-                                   Thread.sleep(Long.MAX_VALUE);
-                               }
-                           } catch (InterruptedException e) {
-                               // pass away if interrupted
-                           }
-                       }
-                   }, "KeepAlive", false));
-               keeper.start();
-           }
-       }
-    }
-
-    /**
-     * Decrements the count of objects exported with keepAlive true,
-     * stopping the non-daemon thread if decremented to zero.
-     **/
-    private void decrementKeepAliveCount() {
-       synchronized (keepAliveLock) {
-           keepAliveCount--;
-
-           if (keepAliveCount == 0) {
-               assert keeper != null;
-               AccessController.doPrivileged(new PrivilegedAction() {
-                   public Object run() {
-                       keeper.interrupt();
-                       return null;
-                   }
-               });
-               keeper = null;
-           }
-       }
-    }
-
-    /**
-     * A Target is returned by the export method to represent the object
-     * exported to this ObjectTable.  It can be used to unexport the
-     * exported object.
-     */
-    final class Target {
-
-       private final ImplRef implRef;
-       private final Uuid id;
-       private final RD[] requestDispatchers;
-       private final boolean allowDGC;
-       private final boolean keepAlive;
-       private final SecurityContext securityContext;
-       private final ClassLoader ccl;
-
-       /** lock guarding all mutable instance state (below) */
-       private final Object lock = new Object();
-       private InvocationDispatcher invocationDispatcher;
-       private boolean exported = false;
-       private int callsInProgress = 0;
-       private final Set referencedSet;
-       private final Map sequenceTable;
-
-       Target(Remote impl,
-              Uuid id,
-              RD[] requestDispatchers,
-              boolean allowDGC,
-              boolean keepAlive)
-           throws ExportException
-       {
-           this.id = id;
-           this.requestDispatchers = requestDispatchers;
-           this.allowDGC = allowDGC;
-           this.keepAlive = keepAlive;
-
-           securityContext = Security.getContext();
-           ccl = Thread.currentThread().getContextClassLoader();
-
-           synchronized (requestDispatchersLock) {
-               boolean success = false;
-               int i = 0;
-               try {
-                   for (i = 0; i < requestDispatchers.length; i++) {
-                       requestDispatchers[i].put(this);
-                   }
-                   success = true;
-               } finally {
-                   if (!success) {
-                       for (int j = 0; j < i; j++) {
-                           requestDispatchers[i].remove(this, false);
-                       }
-                   }
-               }
-           }
-
-           implRef = implRefManager.getImplRef(impl, this);
-
-           if (allowDGC) {
-               referencedSet = new HashSet(3);
-               sequenceTable = new HashMap(3);
-           } else {
-               referencedSet = null;
-               sequenceTable = null;
-           }
-
-           if (keepAlive) {
-               incrementKeepAliveCount();
-           }
-
-           synchronized (lock) {
-               exported = true;
-           }
-       }
-
-       void setInvocationDispatcher(InvocationDispatcher id) {
-           assert id != null;
-           synchronized (lock) {
-               assert invocationDispatcher == null;
-               invocationDispatcher = id;
-           }
-       }
-
-       boolean unexport(boolean force) {
-           synchronized (lock) {
-               if (!exported) {
-                   return true;
-               }
-               if (!force && callsInProgress > 0) {
-                   return false;
-               }
-               exported = false;
-
-               if (keepAlive && callsInProgress == 0) {
-                   decrementKeepAliveCount();
-               }
-
-               if (allowDGC) {
-                   if (!referencedSet.isEmpty()) {
-                       for (Iterator i = referencedSet.iterator();
-                            i.hasNext();)
-                       {
-                           Uuid clientID = (Uuid) i.next();
-                           unregisterTarget(this, clientID);
-                       }
-                       referencedSet.clear();
-                   }
-                   sequenceTable.clear();
-               }
-           }
-
-           implRef.release(this);
-
-           for (int i = 0; i < requestDispatchers.length; i++) {
-               requestDispatchers[i].remove(this, false);
-           }
-           return true;
-       }
-
-       void collect() {
-           synchronized (lock) {
-               if (!exported) {
-                   return;
-               }
-
-               if (logger.isLoggable(Level.FINE)) {
-                   logger.log(Level.FINE,
-                       "garbage collection of object with id {0}", id);
-               }
-
-               exported = false;
-
-               if (keepAlive && callsInProgress == 0) {
-                   decrementKeepAliveCount();
-               }
-
-               if (allowDGC) {
-                   assert referencedSet.isEmpty();
-                   sequenceTable.clear();
-               }
-           }
-
-           for (int i = 0; i < requestDispatchers.length; i++) {
-               requestDispatchers[i].remove(this, true);
-           }
-       }
-
-       Uuid getObjectIdentifier() {
-           return id;
-       }
-
-       // used by ImplRef for invoking Unreferenced.unreferenced
-       boolean getEnableDGC() {
-           return allowDGC;
-       }
-
-       // used by ImplRef for invoking Unreferenced.unreferenced
-       SecurityContext getSecurityContext() {
-           return securityContext;
-       }
-
-       // used by ImplRef for invoking Unreferenced.unreferenced
-       ClassLoader getContextClassLoader() {
-           return ccl;
-       }
-
-       void referenced(Uuid clientID, long sequenceNum) {
-           if (!allowDGC) {
-               return; // ignore if DGC not enabled for this object
-           }
-
-           synchronized (lock) {
-               if (!exported) {
-                   return;
-               }
-
-               if (logger.isLoggable(Level.FINEST)) {
-                   logger.log(Level.FINEST,
-                       "this={0}, clientID={1}, sequenceNum={2}",
-                       new Object[] {
-                           this, clientID, new Long(sequenceNum)
-                       });
-               }
-
-               /*
-                * Check current sequence number against the last
-                * recorded sequence number for the client.  If the
-                * current value is lower, then this is a "late dirty
-                * call", which should not be processed.  Otherwise,
-                * update the last recorded sequence number.
-                */
-               SequenceEntry entry =
-                   (SequenceEntry) sequenceTable.get(clientID);
-               if (entry == null) {
-                   // no record: must assume this is not a late dirty call
-                   entry = new SequenceEntry(sequenceNum);
-                   sequenceTable.put(clientID, entry);
-               } else if (sequenceNum < entry.sequenceNum) {
-                   return;     // late dirty call: ignore
-               } else {
-                   entry.sequenceNum = sequenceNum;
-               }
-
-               if (!referencedSet.contains(clientID)) {
-                   if (referencedSet.isEmpty()) {
-                       Remote impl = implRef.getImpl();
-                       if (impl == null) {
-                           return;     // too late if impl was collected
-                       }
-                       implRef.pin(this);
-                   }
-                   referencedSet.add(clientID);
-
-                   registerTarget(this, clientID);
-               }
-           }
-       }
-
-       void unreferenced(Uuid clientID, long sequenceNum, boolean strong) {
-           if (!allowDGC) {
-               return; // ignore if DGC not enabled for this object
-           }
-
-           synchronized (lock) {
-               if (!exported) {
-                   return;
-               }
-
-               if (logger.isLoggable(Level.FINEST)) {
-                   logger.log(Level.FINEST,
-                       "this={0}, clientID={1}, sequenceNum={2}, strong={3}",
-                       new Object[] {
-                           this, clientID, new Long(sequenceNum),
-                           Boolean.valueOf(strong)
-                       });
-               }
-
-               /*
-                * Check current sequence number against the last
-                * recorded sequence number for the client.  If the
-                * current value is lower, then this is a "late clean
-                * call", which should not be processed.  Otherwise:
-                * if this is for a strong clean call, then update the
-                * last recorded sequence number; if no strong clean
-                * call has been processed for this client, discard
-                * its sequence number record.
-                */
-               SequenceEntry entry =
-                   (SequenceEntry) sequenceTable.get(clientID);
-               if (entry == null) {
-                   // no record: must assume this is not a late clean call
-                   if (strong) {
-                       entry = new SequenceEntry(sequenceNum);
-                       sequenceTable.put(clientID, entry);
-                       entry.keep = true;
-                   }
-               } else if (sequenceNum < entry.sequenceNum) {
-                   return;     // late clean call: ignore
-               } else if (strong) {
-                   entry.sequenceNum = sequenceNum;
-                   entry.keep = true;  // strong clean: retain sequence number
-               } else if (!entry.keep) {
-                   sequenceTable.remove(clientID);
-               }
-
-               unregisterTarget(this, clientID);
-
-               if (referencedSet.remove(clientID) &&
-                   referencedSet.isEmpty())
-               {
-                   implRef.unpin(this);
-               }
-           }
-       }
-
-       void leaseExpired(Uuid clientID) {
-           assert allowDGC;
-
-           synchronized (lock) {
-               if (!exported) {
-                   return;
-               }
-
-               if (logger.isLoggable(Level.FINEST)) {
-                   logger.log(Level.FINEST,
-                       "this={0}, clientID={1}",
-                       new Object[] { this, clientID });
-               }
-
-               SequenceEntry entry =
-                   (SequenceEntry) sequenceTable.get(clientID);
-               if (entry != null && !entry.keep) {
-                   /*
-                    * REMIND: We could be removing the sequence number
-                    * for a more recent lease, thus allowing a "late
-                    * clean call" to be inappropriately processed?
-                    * (See 4848840 Comments.)
-                    */
-                   sequenceTable.remove(clientID);
-               }
-
-               if (referencedSet.remove(clientID) &&
-                   referencedSet.isEmpty())
-               {
-                   implRef.unpin(this);
-               }
-           }
-       }
-
-       void dispatch(InboundRequest request)
-           throws IOException, NoSuchObject
-       {
-           InvocationDispatcher id;
-           synchronized (lock) {
-               if (!exported || invocationDispatcher == null) {
-                   if (logger.isLoggable(Level.FINEST)) {
-                       logger.log(Level.FINEST,
-                           "this={0}, not exported", this);
-                   }
-                   throw new NoSuchObject();
-               }
-               id = invocationDispatcher; // save for reference outside lock
-               callsInProgress++;
-           }
-           try {
-               Remote impl = implRef.getImpl();
-               if (impl == null) {
-                   if (logger.isLoggable(Level.FINEST)) {
-                       logger.log(Level.FINEST,
-                           "this={0}, garbage collected", this);
-                   }
-                   throw new NoSuchObject();
-               }
-
-               dispatch(request, id, impl);
-
-           } finally {
-               synchronized (lock) {
-                   assert callsInProgress > 0;
-                   callsInProgress--;
-
-                   if (keepAlive && !exported && callsInProgress == 0) {
-                       decrementKeepAliveCount();
-                   }
-               }
-           }
-       }
-
-       private void dispatch(final InboundRequest request,
-                             final InvocationDispatcher id,
-                             final Remote impl)
-           throws IOException, NoSuchObject
-       {
-           Thread t = Thread.currentThread();
-           ClassLoader savedCcl = t.getContextClassLoader();
-           try {
-               if (ccl != savedCcl) {
-                   t.setContextClassLoader(ccl);
-               }
-               AccessController.doPrivileged(securityContext.wrap(
-                   new PrivilegedExceptionAction() {
-                       public Object run() throws IOException {
-                           dispatch0(request, id, impl);
-                           return null;
-                       }
-                   }), securityContext.getAccessControlContext());
-                           
-           } catch (java.security.PrivilegedActionException e) {
-               throw (IOException) e.getException();
-           } finally {
-               if (ccl != savedCcl || savedCcl != t.getContextClassLoader()) {
-                   t.setContextClassLoader(savedCcl);
-               }
-           }
-       }
-
-       private void dispatch0(final InboundRequest request,
-                              final InvocationDispatcher id,
-                              final Remote impl)
-           throws IOException
-       {
-           request.checkPermissions();
-
-           OutputStream out = request.getResponseOutputStream();
-           out.write(Jeri.OBJECT_HERE);
-
-           final Collection context = new ArrayList(5);
-           request.populateContext(context);
-
-           ServerContext.doWithServerContext(new Runnable() {
-               public void run() {
-                   id.dispatch(impl, request, context);
-               }
-           }, Collections.unmodifiableCollection(context));
-       }
-
-       public String toString() {      // for logging
-           return "Target@" + Integer.toHexString(hashCode()) +
-               "[" + id + "]";
-       }
-    }
-
-    private static final class SequenceEntry {
-       long sequenceNum;
-       boolean keep;
-
-       SequenceEntry(long sequenceNum) {
-           this.sequenceNum = sequenceNum;
-       }
-    }
-
     void registerTarget(Target target, Uuid clientID) {
-       synchronized (leaseTable) {
-           Lease lease = (Lease) leaseTable.get(clientID);
-           if (lease == null) {
-               target.leaseExpired(clientID);
-           } else {
-               synchronized (lease.notifySet) {
-                   lease.notifySet.add(target);
-               }
-           }
-       }
+        Lease lease = leaseTable.get(clientID);
+        if (lease == null) {
+            target.leaseExpired(clientID);
+        } else {
+            boolean added = lease.add(target);
+            if ( added == false){
+                // lease has been locked because it has expired
+                // prior to removal
+                target.leaseExpired(clientID);
+            }
+        }
     }
 
     void unregisterTarget(Target target, Uuid clientID) {
-       synchronized (leaseTable) {
-           Lease lease = (Lease) leaseTable.get(clientID);
-           if (lease != null) {
-               synchronized (lease.notifySet) {
-                   lease.notifySet.remove(target);
-               }
-           }
-       }
-    }
-
-    /**
-     * RequestDispatcher implementation.
-     **/
-    private class RD implements RequestDispatcher {
-
-       private final Unreferenced unrefCallback;
-
-       private final Map idTable = new HashMap();
-       private int dgcEnabledCount = 0;        // guarded by idTable lock
-
-       private final InvocationDispatcher dgcDispatcher;
-       private final DgcServerImpl dgcServerImpl;
-
-       RD(Unreferenced unrefCallback) {
-           this.unrefCallback = unrefCallback;
-           try {
-               dgcDispatcher =
-                   new BasicInvocationDispatcher(
-                       dgcDispatcherMethods, dgcServerCapabilities,
-                       null, null, this.getClass().getClassLoader())
-                   {
-                       protected ObjectInputStream createMarshalInputStream(
-                           Object impl,
-                           InboundRequest request,
-                           boolean integrity,
-                           Collection context)
-                           throws IOException
-                       {
-                           ClassLoader loader = getClassLoader();
-                           return new MarshalInputStream(
-                               request.getRequestInputStream(),
-                               loader, integrity, loader,
-                               Collections.unmodifiableCollection(context));
-                           // useStreamCodebases() not invoked
-                       }
-                   };
-           } catch (ExportException e) {
-               throw new AssertionError();
-           }
-           dgcServerImpl = new DgcServerImpl();
-       }
-
-       boolean forTable(ObjectTable table) {
-           return ObjectTable.this == table;
-       }
-
-       boolean isReferenced() {
-           synchronized (idTable) {
-               return !idTable.isEmpty();
-           }
-       }
-
-       Target get(Uuid id) {
-           synchronized (idTable) {
-               return (Target) idTable.get(id);
-           }
-       }
-
-       void put(Target target) throws ExportException {
-           synchronized (idTable) {
-               Uuid id = target.getObjectIdentifier();
-               if (id.equals(Jeri.DGC_ID)) {
-                   throw new ExportException(
-                       "object identifier reserved for DGC");
-               }
-               if (idTable.containsKey(id)) {
-                   throw new ExportException(
-                       "object identifier already in use");
-               }
-               idTable.put(id, target);
-               if (target.getEnableDGC()) {
-                   dgcEnabledCount++;
-               }
-           }
-       }
-
-       void remove(Target target, boolean gc) {
-           boolean empty = false;
-           synchronized (idTable) {
-               Uuid id = target.getObjectIdentifier();
-               assert idTable.get(id) == target;
-               idTable.remove(id);
-               if (target.getEnableDGC()) {
-                   dgcEnabledCount--;
-                   assert dgcEnabledCount >= 0;
-               }
-
-               if (idTable.isEmpty()) {
-                   empty = true;
-               }
-           }
-
-           if (gc && empty) {
-               /*
-                * We have to be careful to make this callback without holding
-                * the lock for idTable, because the callback implementation
-                * will likely be code that calls this object's isReferenced
-                * method in its own synchronized block.
-                */
-               unrefCallback.unreferenced();
-           }
-       }
-
-       private boolean hasDgcEnabledTargets() {
-           synchronized (idTable) {
-               return dgcEnabledCount > 0;
-           }
-       }
-
-       public void dispatch(InboundRequest request) {
-           try {
-               InputStream in = request.getRequestInputStream();
-               Uuid id = UuidFactory.read(in);
-
-               if (logger.isLoggable(Level.FINEST)) {
-                   logger.log(Level.FINEST, "id={0}", id);
-               }
-
-               try {
-                   /*
-                    * The DGC object identifier is hardwired here,
-                    * rather than install it in idTable; this
-                    * eliminates the need to worry about not counting
-                    * the DGC server as an exported object in the
-                    * table, and it doesn't need all of the machinery
-                    * that Target provides.
-                    */
-                   if (id.equals(Jeri.DGC_ID)) {
-                       dispatchDgcRequest(request);
-                       return;
-                   }
-
-                   Target target = (Target) get(id);
-                   if (target == null) {
-                       logger.log(Level.FINEST, "id not in table");
-                       throw new NoSuchObject();
-                   }
-                   target.dispatch(request);
-
-               } catch (NoSuchObject e) {
-                   in.close();
-                   OutputStream out = request.getResponseOutputStream();
-                   out.write(Jeri.NO_SUCH_OBJECT);
-                   out.close();
-
-                   if (logger.isLoggable(Levels.FAILED)) {
-                       logger.log(Levels.FAILED, "no such object: {0}", id);
-                   }
-               }
-           } catch (IOException e) {
-               request.abort();
-
-               if (logger.isLoggable(Levels.FAILED)) {
-                   logger.log(Levels.FAILED,
-                              "I/O exception dispatching request", e);
-               }
-           }
-       }
-
-       private void dispatchDgcRequest(final InboundRequest request)
-           throws IOException, NoSuchObject
-       {
-           if (!hasDgcEnabledTargets()) {
-               logger.log(Level.FINEST, "no DGC-enabled targets");
-               throw new NoSuchObject();
-           }
-
-           OutputStream out = request.getResponseOutputStream();
-           out.write(Jeri.OBJECT_HERE);
-
-           final Collection context = new ArrayList(5);
-           request.populateContext(context);
-
-           ServerContext.doWithServerContext(new Runnable() {
-               public void run() {
-                   dgcDispatcher.dispatch(dgcServerImpl, request, context);
-               }
-           }, Collections.unmodifiableCollection(context));
-       }
-
-       private class DgcServerImpl implements DgcServer {
-
-           public long dirty(Uuid clientID,
-                             long sequenceNum,
-                             Uuid[] ids)
-           {
-               if (logger.isLoggable(Level.FINEST)) {
-                   logger.log(Level.FINEST,
-                       "clientID={0}, sequenceNum={1}, ids={2}",
-                       new Object[] {
-                           clientID, new Long(sequenceNum), Arrays.asList(ids)
-                       });
-               }
-
-               long duration = Jeri.leaseValue;
-
-               synchronized (leaseTable) {
-                   Lease lease = (Lease) leaseTable.get(clientID);
-                   if (lease == null) {
-                       leaseTable.put(clientID,
-                                      new Lease(clientID, duration));
-                       if (leaseChecker == null) {
-                           leaseChecker =
-                               (Thread) AccessController.doPrivileged(
-                                   new NewThreadAction(new LeaseChecker(),
-                                       "DGC Lease Checker", true));
-                           leaseChecker.start();
-                       }
-                   } else {
-                       lease.renew(duration);
-                   }
-               }
-
-               for (int i = 0; i < ids.length; i++) {
-                   Target target = get(ids[i]);
-                   if (target != null) {
-                       target.referenced(clientID, sequenceNum);
-                   }
-               }
-
-               return duration;
-           }
-
-           public void clean(Uuid clientID,
-                             long sequenceNum,
-                             Uuid[] ids,
-                             boolean strong)
-           {
-               if (logger.isLoggable(Level.FINEST)) {
-                   logger.log(Level.FINEST,
-                       "clientID={0}, sequenceNum={1}, ids={2}, strong={3}",
-                       new Object[] {
-                           clientID, new Long(sequenceNum),
-                           Arrays.asList(ids), Boolean.valueOf(strong)
-                       });
-               }
-
-               for (int i = 0; i < ids.length; i++) {
-                   Target target = get(ids[i]);
-                   if (target != null) {
-                       target.unreferenced(clientID, sequenceNum, strong);
-                   }
-               }
-           }
-       }
+        Lease lease = leaseTable.get(clientID);
+        if (lease != null) {
+            lease.remove(target);
+        }
+    }
+
+    
+
+    private class DgcServerImpl implements DgcServer {
+        private final DgcRequestDispatcher dgcRequestDispatcher;
+        
+        DgcServerImpl(DgcRequestDispatcher dgcRequestDispatcher){
+            this.dgcRequestDispatcher = dgcRequestDispatcher;
+        }
+
+        public long dirty(Uuid clientID,
+                          long sequenceNum,
+                          Uuid[] ids)
+        {
+            if (logger.isLoggable(Level.FINEST)) {
+                logger.log(Level.FINEST,
+                    "clientID={0}, sequenceNum={1}, ids={2}",
+                    new Object[] {
+                        clientID, new Long(sequenceNum), Arrays.asList(ids)
+                    });
+            }
+
+            long duration = Jeri.leaseValue;
+
+            Lease lease = leaseTable.get(clientID);
+            if (lease == null) {
+                lease = new Lease(clientID, duration);
+                Lease existed = leaseTable.putIfAbsent(clientID,lease);
+                if (existed != null){
+                    assert clientID.equals(existed.getClientID());
+                    boolean renewed = existed.renew(duration);
+                    if (!renewed){
+                        /* The probability of getting here is low,
+                         * it indicates a lease with an extremely short 
+                         * expiry and a very small lease table.
+                         */               
+                        if (logger.isLoggable(Level.WARNING)) {
+                            logger.log(Level.WARNING,
+                                "Problem with lease table, try a longer " +
+                                "lease duration clientID={0}, " +
+                                "sequenceNum={1}, ids={2}",
+                                new Object[] {
+                                    clientID, new Long(sequenceNum), 
Arrays.asList(ids)
+                                });
+                        }                          
+                    }
+                }
+            } else {
+                assert clientID.equals(lease.getClientID());
+                boolean renewed = lease.renew(duration);
+                if (!renewed){
+                    // Indicates an expired lease in the table.  A lease
+                    // always becomes expired prior to removal, it is 
+                    // never removed prior to expiry, in case it is
+                    // renewed by another thread, which would risk a renewed
+                    // lease being removed from the table.  
+                    // An expired lease must be replaced.
+                    leaseTable.remove(clientID, lease); // Another thread 
could remove it first.
+                    lease = new Lease(clientID, duration);
+                    Lease existed = leaseTable.putIfAbsent(clientID, lease);
+                    if (existed != null){
+                        lease = existed;
+                        assert clientID.equals(lease.getClientID());
+                        renewed = lease.renew(duration);
+                        if (!renewed){
+                            /* The probability of getting here is low,
+                             * it indicates a lease of extremely short 
+                             * duration and a very small lease table.
+                             */               
+                            if (logger.isLoggable(Level.WARNING)) {
+                                logger.log(Level.WARNING,
+                                    "Problem with lease table, try a longer " +
+                                    "lease duration clientID={0}, " +
+                                    "sequenceNum={1}, ids={2}",
+                                    new Object[] {
+                                        clientID, new Long(sequenceNum), 
Arrays.asList(ids)
+                                    });
+                            }                          
+                        }  
+                    }
+                }
+            }
+            /* FIXED: River-142: 
+             * In the server-side DGC implementation's thread that check's for
+             * lease expirations 
+             * 
(com.sun.jini.jeri.internal.runtime.ObjectTable.LeaseChecker.run),
+             * it checks for them while synchronized on the overall lease 
table,
+             * but it delays notifying the expired leases' individual 
registered
+             * Targets about the expirations until after it has released the
+             * lease table lock. This approach was taken from the 
+             * JRMP implementation, which is that way because of the fix 
+             * for 4118056 (a previous deadlock bug-- but now, I'm thinking 
+             * that the JRMP implementation has this bug too).
+             *
+             * The problem seems to be that after releasing the lease table 
+             * lock, it is possible for another lease renewal/request to 
+             * come in (from the same DGC client and for the same remote 
object)
+             * that would then be invalidated by the subsequent Target 
+             * notification made by the lease expiration check thread-- and 
+             * thus the client's lease renewal (for that remote object) will 
+             * be forgotten. It would appear that the synchronization approach 
+             * here needs to be reconsidered.
+             * 
+             * ( Comments note: )
+             * In addition to the basic problem of the expired-then-renewed 
+             * client being removed from the referenced set, there is also 
+             * the problem of the sequence table entry being forgotten-- which 
+             * prevents detection of a "late clean call".
+             * Normally, late clean calls are not a problem because sequence 
+             * numbers are retained while the client is in the referenced set 
+             * (and there is no such thing as a "strong dirty"). 
+             * But in this case, with the following order of events on 
+             * the server side:
+             *
+             *   1. dirty, seqNo=2
+             *   2. (lease expiration)
+             *   3. clean, seqNo=1
+             *
+             * The primary bug here is that the first two events will leave 
+             * the client missing from the referenced set. But the secondary 
+             * bug is that even if that's fixed, with the sequence number 
+             * forgotten, the third event (the "late clean call") will still 
+             * cause the client to be removed from the referenced set.
+             * 
+             * FIX:
+             * This issue has been fixed by making the Lease responsible for
+             * it's own state, which is protected internally by 
synchronization.
+             * The lease checker passes the time to the Lease, which checks 
+             * itself and notifies the Targets in the event of expiry.
+             * 
+             * Because the notification is not delayed, the client id and
+             * sequence number will not be not be removed by the LeaseChecker
+             * thread after being updated by the second dirty call (when
+             * the lock was cleared as described).
+             * 
+             * The client id and sequence number are added to the Target 
sequence
+             * table by the second dirty call, after the Lease removes
+             * them immediately upon expiry being invoked by the LeaseChecker.
+             * 
+             * Then because the late clean call sequence number is less than 
the 
+             * second dirty call and exists, it is correctly recognised.
+             */
+            synchronized (running){
+                if (!running) {
+                    leaseChecker =
+                        (Thread) AccessController.doPrivileged(
+                            new NewThreadAction(new LeaseChecker(),
+                                "DGC Lease Checker", true));
+                    leaseChecker.start();
+                }
+            }
+            for (int i = 0; i < ids.length; i++) {
+                Target target = dgcRequestDispatcher.get(ids[i]);
+                if (target != null) {
+                    target.referenced(clientID, sequenceNum);
+                }
+            }
+            return duration;
+        }
+
+        public void clean(Uuid clientID,
+                          long sequenceNum,
+                          Uuid[] ids,
+                          boolean strong)
+        {
+            if (logger.isLoggable(Level.FINEST)) {
+                logger.log(Level.FINEST,
+                    "clientID={0}, sequenceNum={1}, ids={2}, strong={3}",
+                    new Object[] {
+                        clientID, new Long(sequenceNum),
+                        Arrays.asList(ids), Boolean.valueOf(strong)
+                    });
+            }
+
+            for (int i = 0; i < ids.length; i++) {
+                Target target = dgcRequestDispatcher.get(ids[i]);
+                if (target != null) {
+                    target.unreferenced(clientID, sequenceNum, strong);
+                }
+            }
+        }
     }
 
     private class LeaseChecker implements Runnable {
 
        public void run() {
            boolean done = false;
-           do {
-               try {
-                   Thread.sleep(Jeri.leaseCheckInterval);
-               } catch (InterruptedException e) {
-                   // REMIND: shouldn't happen, OK to ignore?
-               }
-
-               long now = System.currentTimeMillis();
-
-               Collection expiredLeases = new ArrayList();
-
-               synchronized (leaseTable) {
-                   for (Iterator i = leaseTable.values().iterator();
-                        i.hasNext();)
-                   {
-                       Lease lease = (Lease) i.next();
-                       if (lease.hasExpired(now)) {
-                           expiredLeases.add(lease);
-                           i.remove();
-                       }
-                   }
-
-                   if (leaseTable.isEmpty()) {
-                       leaseChecker = null;
-                       done = true;
-                   }
-               }
-
-               if (expiredLeases.isEmpty()) {
-                   continue;
-               }
-
-               for (Iterator i = expiredLeases.iterator(); i.hasNext();) {
-                   Lease lease = (Lease) i.next();
-                   if (lease.notifySet.isEmpty()) {
-                       continue;
-                   }
-
-                   for (Iterator i2 = lease.notifySet.iterator();
-                        i2.hasNext();)
-                   {
-                       Target target = (Target) i2.next();
-                       target.leaseExpired(lease.getClientID());
-                   }
-               }
-           } while (!done);
-       }
-    }
-
-    private static class Lease {
-
-       private final Uuid clientID;
-       final Set notifySet = new HashSet(3);   // guarded?
-       private long expiration;                // guarded by leaseTable lock
-
-       Lease(Uuid clientID, long duration) {
-           this.clientID = clientID;
-           expiration = System.currentTimeMillis() + duration;
-       }
-
-       Uuid getClientID() {
-           return clientID;
-       }
-
-       void renew(long duration) {
-           long newExpiration = System.currentTimeMillis() + duration;
-           if (newExpiration > expiration) {
-               expiration = newExpiration;
-           }
-       }
-
-       boolean hasExpired(long now) {
-           return expiration < now;
+            try {
+                do {
+                    Thread.sleep(Jeri.leaseCheckInterval);
+                    long now = System.currentTimeMillis();             
+                    for (Iterator i = leaseTable.values().iterator();
+                         i.hasNext();)
+                    {
+                        Lease lease = (Lease) i.next();
+                        boolean expired = lease.notifyIfExpired(now);
+                        if (expired) {                     
+                            i.remove();
+                        }
+                    }
+                    if (leaseTable.isEmpty()) {
+                        done = true;
+                    }          
+                } while (!done);
+            } catch (InterruptedException e) {
+                // REMIND: shouldn't happen, OK to ignore?
+                // No, restore the interrupted status
+                 Thread.currentThread().interrupt();
+            } finally {
+                // This is always executed and returns the lease checker
+                // to the non running state, such that if the application
+                // has not exited, another thread will be started eventually.
+                synchronized (running){
+                    leaseChecker = null;
+                    running = Boolean.FALSE;               
+                }
+            }
        }
     }
 
-    private static class NoSuchObject extends Exception { }
+    static class NoSuchObject extends Exception { }
 }

Modified: 
river/jtsk/branches/2.2/src/manifest/jsk-resources/META-INF/services/net.jini.config.Configuration
URL: 
http://svn.apache.org/viewvc/river/jtsk/branches/2.2/src/manifest/jsk-resources/META-INF/services/net.jini.config.Configuration?rev=1137619&r1=1137618&r2=1137619&view=diff
==============================================================================
--- 
river/jtsk/branches/2.2/src/manifest/jsk-resources/META-INF/services/net.jini.config.Configuration
 (original)
+++ 
river/jtsk/branches/2.2/src/manifest/jsk-resources/META-INF/services/net.jini.config.Configuration
 Mon Jun 20 13:00:41 2011
@@ -15,4 +15,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #*/
-com.sun.jini.config.GroovyConfig
+net.jini.config.GroovyConfig

Modified: river/jtsk/branches/2.2/src/net/jini/config/package.html
URL: 
http://svn.apache.org/viewvc/river/jtsk/branches/2.2/src/net/jini/config/package.html?rev=1137619&r1=1137618&r2=1137619&view=diff
==============================================================================
--- river/jtsk/branches/2.2/src/net/jini/config/package.html (original)
+++ river/jtsk/branches/2.2/src/net/jini/config/package.html Mon Jun 20 
13:00:41 2011
@@ -59,6 +59,10 @@ The {@link net.jini.config.EmptyConfigur
 instance of this class to simplify handling cases where no configuration
 is specified rather than, for example, checking for a <code>null</code>
 configuration. <p>
+    
+The net.jini.config.GroovyConfig class is a new configuration provider,
+it uses Groovy configuration objects, this is potentially the most
+powerful configuration mechanism. <p>
 
 <a name="Usage"><h2>Using Configuration</h2></a>
 

Modified: 
river/jtsk/branches/2.2/src/net/jini/jeri/connection/ConnectionManager.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/branches/2.2/src/net/jini/jeri/connection/ConnectionManager.java?rev=1137619&r1=1137618&r2=1137619&view=diff
==============================================================================
--- river/jtsk/branches/2.2/src/net/jini/jeri/connection/ConnectionManager.java 
(original)
+++ river/jtsk/branches/2.2/src/net/jini/jeri/connection/ConnectionManager.java 
Mon Jun 20 13:00:41 2011
@@ -106,6 +106,10 @@ import net.jini.jeri.OutboundRequestIter
  * milliseconds to leave idle client-side connections around before
  * closing them. The default value is 15000 milliseconds (15 seconds).
  *
+ * <li><code>com.sun.jini.jeri.handshakeTimeout</code> - Time in
+ * milliseconds for client-side connections to wait for the server to
+ * acknowledge an opening handshake. The default value is 15000 milliseconds 
(15 seconds).
+ *
  * </ul>
  **/
 public final class ConnectionManager {
@@ -113,10 +117,17 @@ public final class ConnectionManager {
      * How long to leave idle muxes around before closing them.
      */
     private static final long TIMEOUT =
-       ((Long) AccessController.doPrivileged(new GetLongAction(
+       ( (Long) AccessController.doPrivileged(new GetLongAction(
                "com.sun.jini.jeri.connectionTimeout", 
                15000))).longValue();
     /**
+     * How long to wait for a server to respond to an initial client message.
+     */
+    private static final long HANDSHAKE_TIMEOUT =
+       ((Long) AccessController.doPrivileged(new GetLongAction(
+               "com.sun.jini.jeri.handshakeTimeout", 
+               15000))).longValue();
+    /**
      * ConnectionManager logger.
      */
     private static final Logger logger =
@@ -278,6 +289,7 @@ public final class ConnectionManager {
        try {
            mux = (c.getChannel() == null) ?
                new OutboundMux(c) : new OutboundMux(c, true);
+                mux.setStartTimeout(HANDSHAKE_TIMEOUT);
        } finally {
            if (mux == null) {
                try {

Modified: 
river/jtsk/branches/2.2/src/net/jini/loader/pref/PreferredClassProvider.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/branches/2.2/src/net/jini/loader/pref/PreferredClassProvider.java?rev=1137619&r1=1137618&r2=1137619&view=diff
==============================================================================
--- 
river/jtsk/branches/2.2/src/net/jini/loader/pref/PreferredClassProvider.java 
(original)
+++ 
river/jtsk/branches/2.2/src/net/jini/loader/pref/PreferredClassProvider.java 
Mon Jun 20 13:00:41 2011
@@ -43,6 +43,8 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
 import java.util.StringTokenizer;
 import java.util.WeakHashMap;
@@ -274,7 +276,7 @@ public class PreferredClassProvider exte
      * references, so this table does not prevent loaders from being
      * garbage collected.
      */
-    private final Map loaderTable = new HashMap();
+    private final Map<LoaderKey,LoaderEntryHolder> loaderTable = new 
HashMap<LoaderKey,LoaderEntryHolder>();
 
     /** reference queue for cleared class loader entries */
     private final ReferenceQueue refQueue = new ReferenceQueue();
@@ -1556,31 +1558,55 @@ public class PreferredClassProvider exte
         * }
         */
 
+       /*
+        * Take this opportunity to remove from the table entries
+        * whose weak references have been cleared.
+        */
+       List<LoaderKey> toRemove = new LinkedList<LoaderKey>();
+       Object ref;
+       while ((ref = refQueue.poll()) != null) {
+           if (ref instanceof LoaderKey)
+               toRemove.add((LoaderKey) ref);
+           else if (ref instanceof LoaderEntry) {
+               LoaderEntry entry = (LoaderEntry) ref;
+               if (!entry.removed)     // ignore entries removed below
+                   toRemove.add(entry.key);
+           }
+       }
+
+       LoaderKey key = new LoaderKey(urls, parent);
+       LoaderEntryHolder holder;
        synchronized (loaderTable) {
-           /*
-            * Take this opportunity to remove from the table entries
-            * whose weak references have been cleared.
-            */
-           Object ref;
-           while ((ref = refQueue.poll()) != null) {
-               if (ref instanceof LoaderKey) {
-                   LoaderKey key = (LoaderKey) ref;
-                   loaderTable.remove(key);
-               } else if (ref instanceof LoaderEntry) {
-                   LoaderEntry entry = (LoaderEntry) ref;
-                   if (!entry.removed) {       // ignore entries removed below
-                       loaderTable.remove(entry.key);
-                   }
-               }
+           if (!toRemove.isEmpty()) {
+               for (LoaderKey oldKey : toRemove)
+                   loaderTable.remove(oldKey);
+               toRemove.clear();
            }
 
            /*
             * Look up the codebase URL path and parent class loader pair
             * in the table of RMI class loaders.
             */
-           LoaderKey key = new LoaderKey(urls, parent);
-           LoaderEntry entry = (LoaderEntry) loaderTable.get(key);
+           holder = loaderTable.get(key);
+           if (null == holder) {
+               holder = new LoaderEntryHolder();
+               loaderTable.put(key, holder);
+           }
+       }
 
+       /*
+        * Four possible cases:
+        *   1) this is our first time creating this classloader
+        *      - holder.entry is null, need to make a new entry and a new 
loader
+        *   2) we made this classloader before, but it was garbage collected a 
long while ago
+        *      - identical to case #1 and it was reaped by the toRemove code 
above
+        *   3) we made this classloader before, and it was garbage collected 
recently
+        *      - holder.entry is non-null, but holder.entry.get() is null, 
very similar to case #1
+        *   4) we made this classloader before, and it's still alive (CACHE 
HIT)
+        *      - just return it
+        */
+       synchronized (holder) {
+           LoaderEntry entry = holder.entry;
            ClassLoader loader;
            if (entry == null ||
                (loader = (ClassLoader) entry.get()) == null)
@@ -1593,7 +1619,6 @@ public class PreferredClassProvider exte
                 * from the weak reference queue.
                 */
                if (entry != null) {
-                   loaderTable.remove(key);
                    entry.removed = true;
                }
 
@@ -1623,7 +1648,7 @@ public class PreferredClassProvider exte
                 * weak reference and store it in the table with the key.
                 */
                entry = new LoaderEntry(key, loader);
-               loaderTable.put(key, entry);
+               holder.entry = entry;
            }
            return loader;
        }
@@ -1737,6 +1762,9 @@ public class PreferredClassProvider exte
            this.key = key;
        }
     }
+    private class LoaderEntryHolder {
+       public LoaderEntry entry;
+    }
 
     private static ClassLoader getClassLoader(final Class c) {
        return (ClassLoader) AccessController.doPrivileged(

Modified: 
river/jtsk/branches/2.2/test/src/com/sun/jini/outrigger/FastListTest.java
URL: 
http://svn.apache.org/viewvc/river/jtsk/branches/2.2/test/src/com/sun/jini/outrigger/FastListTest.java?rev=1137619&r1=1137618&r2=1137619&view=diff
==============================================================================
--- river/jtsk/branches/2.2/test/src/com/sun/jini/outrigger/FastListTest.java 
(original)
+++ river/jtsk/branches/2.2/test/src/com/sun/jini/outrigger/FastListTest.java 
Mon Jun 20 13:00:41 2011
@@ -29,7 +29,6 @@ public class FastListTest {
     public void initialize() {
         testee = new FastList<TestNode>();
         rawTestee = new Iterable<TestNode>() {
-            @Override
             public Iterator<TestNode> iterator() {
                 return testee.rawIterator();
             }
@@ -181,7 +180,6 @@ public class FastListTest {
     private List<Thread> getAddThreads(final List<List<TestNode>> elements,
             final CyclicBarrier barrier) {
         NodeListRunnableFactory factory = new NodeListRunnableFactory() {
-            @Override
             public Runnable getRunnable(final List<TestNode> nodes) {
                 return new Runnable() {
                     public void run() {
@@ -207,7 +205,6 @@ public class FastListTest {
     private List<Thread> getRemoveThreads(final List<List<TestNode>> elements,
             final CyclicBarrier barrier) {
         NodeListRunnableFactory factory = new NodeListRunnableFactory() {
-            @Override
             public Runnable getRunnable(final List<TestNode> nodes) {
                 return new Runnable() {
                     public void run() {
@@ -233,7 +230,6 @@ public class FastListTest {
     private List<Thread> getRemoveAllThreads(final List<List<TestNode>> 
elements,
             final CyclicBarrier barrier, final int tries) {
         NodeListRunnableFactory factory = new NodeListRunnableFactory() {
-            @Override
             public Runnable getRunnable(final List<TestNode> nodes) {
                 return new Runnable() {
                     public void run() {


Reply via email to