You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@river.apache.org by pe...@apache.org on 2011/12/31 01:13:02 UTC

svn commit: r1225993 [2/2] - in /river/jtsk/skunk/peterConcurrentPolicy: qa/harness/policy/ qa/src/com/sun/jini/qa/harness/ qa/src/com/sun/jini/test/impl/start/ qa/src/com/sun/jini/test/resources/ qa/src/com/sun/jini/test/spec/config/configurationfile/...

Modified: river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/policy/DynamicPolicyProvider.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/policy/DynamicPolicyProvider.java?rev=1225993&r1=1225992&r2=1225993&view=diff
==============================================================================
--- river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/policy/DynamicPolicyProvider.java (original)
+++ river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/policy/DynamicPolicyProvider.java Sat Dec 31 00:13:00 2011
@@ -21,6 +21,7 @@ package net.jini.security.policy;
 import java.io.IOException;
 import java.lang.ref.Reference;
 import java.rmi.RemoteException;
+import java.security.PrivilegedActionException;
 import org.apache.river.api.security.DelegateSecurityManager;
 import java.security.AccessController;
 import java.security.AllPermission;
@@ -32,6 +33,7 @@ import java.security.Permissions;
 import java.security.Policy;
 import java.security.Principal;
 import java.security.PrivilegedAction;
+import java.security.PrivilegedExceptionAction;
 import java.security.ProtectionDomain;
 import java.security.Security;
 import java.util.ArrayList;
@@ -43,15 +45,22 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
+import java.util.TreeSet;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import net.jini.security.GrantPermission;
+import net.jini.security.PermissionComparator;
 import org.apache.river.api.security.PermissionGrant;
 import org.apache.river.api.security.PermissionGrantBuilder;
 import org.apache.river.api.security.policy.RemotePolicy;
-import org.apache.river.api.security.RevokePermission;
+import org.apache.river.api.security.PolicyPermission;
 import org.apache.river.api.security.policy.RevokeableDynamicPolicy;
 import org.apache.river.impl.security.policy.util.PolicyUtils;
 import org.apache.river.impl.util.CollectionsConcurrent;
@@ -162,8 +171,8 @@ public class DynamicPolicyProvider exten
      private static final String basePolicyClassProperty =
 	"net.jini.security.policy.DynamicPolicyProvider.basePolicyClass";
     private static final String defaultBasePolicyClass =
-//            "net.jini.security.policy.ConcurrentPolicyFile";
-	"net.jini.security.policy.PolicyFileProvider";
+            "net.jini.security.policy.ConcurrentPolicyFile";
+//	"net.jini.security.policy.PolicyFileProvider";
     private static final ProtectionDomain sysDomain = 
 	AccessController.doPrivileged(new PrivilegedAction<ProtectionDomain>() {
         
@@ -186,15 +195,14 @@ public class DynamicPolicyProvider exten
     private volatile PermissionGrant[] remotePolicyGrants; // Write protected by grantLock.
     /* This lock protects write updating of remotePolicyGrants reference */
     private final Object grantLock;
-    private final Policy basePolicy; // final looks after its own sync
+    private final Policy basePolicy; // refresh protected by transactionWriteLock
     /* cache of ProtectionDomain and their Permissions */
-    private final ConcurrentMap<ProtectionDomain, PermissionCollection> cache;
-    /* A cache of Permission's for each PermissionGrant, this avoids a mutating
-     * PermissionGrant implementation from escallating permissions, this
-     * cache should be updated when receiving PermissionGrant and checking
-     * the callers Permissions. The current implementation uses a weak identity
-     * map that cleans itself. */
-    private final ConcurrentMap<PermissionGrant, Permission[]> grantCache;
+    private final ConcurrentMap<ProtectionDomain, PermissionCollection> cache; // protected by transactionWriteLock
+    /* A transaction ID to avoid updating the cache with old information
+     * after it has been cleared */
+    private final Lock transactionWriteLock; // Lock to protect cache clear and transactionID write.
+    private final Lock transactionReadLock; // Lock to protect cache put and transactionID reads.
+    private int transactionID; // Protected by transaction locks 
     // DynamicPolicy grant's for Proxy's.
     private final Collection<PermissionGrant> dynamicPolicyGrants;
     private final boolean basePolicyIsDynamic; // Don't use cache if true.
@@ -206,6 +214,7 @@ public class DynamicPolicyProvider exten
     // avoid dead locks due to bug 4911907
 
     private final Guard revokePermission;
+    private final Permission implementsPermissionGrant;
     private final Guard protectionDomainPermission;
     
     /**
@@ -275,12 +284,10 @@ public class DynamicPolicyProvider exten
         ConcurrentMap<Referrer<ProtectionDomain>, Referrer<PermissionCollection>> internal 
                 = new ConcurrentHashMap<Referrer<ProtectionDomain>,Referrer<PermissionCollection>>(120);
         cache = RC.concurrentMap(internal, Ref.WEAK_IDENTITY, Ref.SOFT);
-        ConcurrentMap<Referrer<PermissionGrant>, Referrer<Permission[]>> gInternal
-                = new ConcurrentHashMap<Referrer<PermissionGrant>, Referrer<Permission[]>>(60);
-	grantCache = RC.concurrentMap(gInternal, Ref.WEAK, Ref.STRONG);
         loggable = logger.isLoggable(Level.FINEST);
 	grantLock = new Object();
-	revokePermission = new RevokePermission();
+	revokePermission = new PolicyPermission("REVOKE");
+        implementsPermissionGrant = new PolicyPermission("implementPermissionGrant");
         protectionDomainPermission = new RuntimePermission("getProtectionDomain");
         if (basePolicy instanceof DynamicPolicy) {
             DynamicPolicy dp = (DynamicPolicy) basePolicy;
@@ -300,6 +307,10 @@ public class DynamicPolicyProvider exten
         } else {
             basePolicyIsRemote = false;
         }
+        transactionID = 0;
+        ReadWriteLock rwl = new ReentrantReadWriteLock();
+        transactionWriteLock = rwl.writeLock();
+        transactionReadLock = rwl.readLock();
         ensureDependenciesResolved();
     }
     
@@ -320,12 +331,10 @@ public class DynamicPolicyProvider exten
         ConcurrentMap<Referrer<ProtectionDomain>, Referrer<PermissionCollection>> internal 
                 = new ConcurrentHashMap<Referrer<ProtectionDomain>,Referrer<PermissionCollection>>(120);
         cache = RC.concurrentMap(internal, Ref.WEAK_IDENTITY, Ref.SOFT);
-        ConcurrentMap<Referrer<PermissionGrant>, Referrer<Permission[]>> gInternal
-                = new ConcurrentHashMap<Referrer<PermissionGrant>, Referrer<Permission[]>>(60);
-	grantCache = RC.concurrentMap(gInternal, Ref.WEAK, Ref.STRONG);
         loggable = logger.isLoggable(Level.FINEST);
 	grantLock = new Object();
-	revokePermission = new RevokePermission();
+	revokePermission = new PolicyPermission("REVOKE");
+        implementsPermissionGrant = new PolicyPermission("implementPermissionGrant");
         protectionDomainPermission = new RuntimePermission("getProtectionDomain");
          if (basePolicy instanceof DynamicPolicy) {
             DynamicPolicy dp = (DynamicPolicy) basePolicy;
@@ -345,6 +354,10 @@ public class DynamicPolicyProvider exten
         } else {
             basePolicyIsRemote = false;
         }
+        transactionID = 0;
+        ReadWriteLock rwl = new ReentrantReadWriteLock();
+        transactionWriteLock = rwl.writeLock();
+        transactionReadLock = rwl.readLock();
         ensureDependenciesResolved();
     }
 
@@ -434,11 +447,11 @@ Put the policy providers and all referen
             PermissionGrant p = dynamicGrants.next();
             if ( p.implies(codesource, null) ){
 		// Only use the trusted grantCache.
-		Permission[] perm = grantCache.get(p);
-		int s = perm.length;
-		for ( int j = 0; j < s; j++){
-		    permissions.add(perm[j]);
-		}
+		Collection<Permission> perms = p.getPermissions();
+                Iterator<Permission> it = perms.iterator();
+                while (it.hasNext()){
+                    permissions.add(it.next());
+                }
 	    }
         }
 	return permissions;
@@ -464,22 +477,16 @@ Put the policy providers and all referen
          * TODO: Remove PolicyPermission's
 	 */
         PermissionCollection pc = basePolicy.getPermissions(domain);
-        PermissionCollection permissions = new Permissions();
-        Enumeration<Permission> enu = pc.elements();
-        while (enu.hasMoreElements()){
-            permissions.add(enu.nextElement());
-        }
+        pc = PolicyUtils.asConcurrent(pc);
 	PermissionGrant [] grantsRefCopy = remotePolicyGrants; // Interim updates not seen.
 	int l = grantsRefCopy.length;
 	for ( int i = 0; i < l; i++ ){
 	    if ( grantsRefCopy[i].implies(domain) ){
-		// Only use the trusted grantCache.
-		Permission[] perm = grantCache.get(grantsRefCopy[i]);
-		int s = perm.length;
-		for ( int j = 0; j < s; j++){
-		    PolicyPermission pp = new PolicyPermission(perm[j]);
-		    permissions.add(pp);
-		}
+		Collection<Permission> perms = grantsRefCopy[i].getPermissions();
+		Iterator<Permission> it = perms.iterator();
+                while (it.hasNext()){
+                    pc.add(it.next());
+                }
 	    }
 	}
         Iterator<PermissionGrant> dynamicGrants = dynamicPolicyGrants.iterator();
@@ -487,20 +494,14 @@ Put the policy providers and all referen
             PermissionGrant p = dynamicGrants.next();
             if ( p.implies(domain) ){
 		// Only use the trusted grantCache.
-		Permission[] perm = grantCache.get(p);
-		int s = perm.length;
-		for ( int j = 0; j < s; j++){
-                    Permission pp = null;
-                    if (revokeable) {
-                        pp = new PolicyPermission(perm[j]);
-                    }else{
-                        pp = perm[j];
-                    }
-		    permissions.add(pp);
-		}
+                Collection<Permission> perms = p.getPermissions();
+                Iterator<Permission> it = perms.iterator();
+                while (it.hasNext()){
+                    pc.add(it.next());
+                }
 	    }
         }
-	return permissions;	
+	return pc;	
     }
     
     /* River-26 Mark Brouwer suggested making UmbrellaPermission's expandable
@@ -518,9 +519,12 @@ Put the policy providers and all referen
         }
 	if (permission == null) throw new NullPointerException("permission not allowed to be null");
         // First check our cache if the basePolicy is not dynamic.
+        
         PermissionCollection pc = domain != null? cache.get(domain): null;
         if ( pc != null ) {
-            if (pc.implies(permission)) return true;          
+            /* Out of date cache is cleared and only updated with the latest 
+             * grants don't bother retrieving it again */
+            if ( pc.implies(permission) ) return true;          
         } 
         Thread thread = Thread.currentThread();
         if (thread.isInterrupted()) return false;
@@ -529,7 +533,16 @@ Put the policy providers and all referen
          * while another Permission within that collection is already
          * resolved, the Enumeration will cause a ConcurrentModificationException.
          */ 
-        PermissionCollection bpc = basePolicy.getPermissions(domain);
+        int currentTransactionID;
+        PermissionCollection bpc = null;
+        transactionReadLock.lock();
+        try {
+            currentTransactionID = transactionID;
+            bpc = basePolicy.getPermissions(domain);
+        }finally{
+            transactionReadLock.unlock();
+        }
+        
         /* Be mindful of static Permissions held by the 
          * ProtectionDomain, a Permission may be implied by the 
          * the combination of Permission's in the ProtectionDomain and 
@@ -556,31 +569,17 @@ Put the policy providers and all referen
         pc = PolicyUtils.asConcurrent(bpc);
         /* Don't place it in the cache half finished or check it yet since
          * mutations are blocking */
-//            PermissionCollection existed = 
-//                    domain != null ? cache.putIfAbsent(domain, pc): null; 
-//            if ( existed != null ){
-//                pc = existed;
-//            }
-//            expandUmbrella(pc); // We need to avoid using PolicyFileProvider as grants from it are not revokable.
-//	    if ( pc.implies(permission)) return true;
-	
-        // Once we get to here pc is definitely not null and we have the
-        // copy referenced in the cache.
-//        if (loggable){
-//            logger.log(Level.FINEST, domain + permission.toString() + 
-//                    ": Base policy is not dynamic and returned false" );
-//        }
-        // If the base policy doesn't imply a Permission then we should check for dynamic grants
-        Collection<Permission> dynamicallyGrantedPermissions = new HashSet<Permission>(120);
-	PermissionGrant[] grantsRefCopy = remotePolicyGrants; // In case the grants volatile reference is updated.
-        
+	PermissionGrant[] grantsRefCopy = remotePolicyGrants; // In case the grants volatile reference is updated.       
         if (thread.isInterrupted()) return false;
 	int l = grantsRefCopy.length;
 	for ( int i = 0; i < l; i++){
 	    if (grantsRefCopy[i].implies(domain)) {
 		// We only trust grantCache in case of mutable PermissionGrant.
-		Permission[] perms = grantCache.get(grantsRefCopy[i]);
-		dynamicallyGrantedPermissions.addAll(Arrays.asList(perms));
+		Collection<Permission> perms = grantsRefCopy[i].getPermissions();
+		Iterator<Permission> it = perms.iterator();
+                while (it.hasNext()){
+                    pc.add(it.next());
+                }
 	    }
 	}
         if (thread.isInterrupted()) return false;
@@ -588,36 +587,27 @@ Put the policy providers and all referen
         while (grants.hasNext()){
             PermissionGrant g = grants.next();
             if (g.implies(domain)){
-                dynamicallyGrantedPermissions.addAll(g.getPermissions());
+                Collection<Permission> perms = g.getPermissions();
+                Iterator<Permission> it = perms.iterator();
+                while (it.hasNext()){
+                    pc.add(it.next());
+                }
             }
         }
         if (thread.isInterrupted()) return false;
-//        if (loggable) {
-//            logger.log(Level.FINEST, "Grants: " + dynamicallyGrantedPermissions.toString());
-//        }
-//        if (dynamicallyGrantedPermissions.isEmpty()) {
-//            // We have no dynamic grants
-//            return false;
-//        }
-        Iterator<Permission> dgpi = dynamicallyGrantedPermissions.iterator();
-        while (dgpi.hasNext()){
-            pc.add(dgpi.next());
-        }
-        if (thread.isInterrupted()) return false;
-        // If we get refreshed the cache could be empty, which is more pedantic
-        // however the result may still be true so we'll return it anyway.
-//        if (loggable) {
-//            logger.log(Level.FINEST, "PermissionCollection: " + pc.toString());
-//        }
         // We have added dynamic grants, lets expand any UmbrellaGrant's
         expandUmbrella(pc);
-        if ( pc.implies(permission) ){
-            // The cache is replaced rather than updated, to avoid umbrella grants
-            // causing GrantPermission recursion.
-            if (domain != null ) cache.replace(domain, pc);
-            return true;
+        if (domain != null) {
+            if (transactionReadLock.tryLock()){
+                try { 
+                    if (transactionID == currentTransactionID) 
+                        cache.putIfAbsent(domain, pc);
+                }finally {
+                    transactionReadLock.unlock();
+                }
+            }
         }
-        return false;
+        return pc.implies(permission);
     }
     
     /**
@@ -630,9 +620,15 @@ Put the policy providers and all referen
      * 
      */
     
+    @SuppressWarnings("unchecked")
     public void refresh() {
-        cache.clear();
-        basePolicy.refresh();
+        transactionWriteLock.lock();
+        try {
+            basePolicy.refresh();
+            transactionID++;
+        }finally{
+            transactionWriteLock.unlock();
+        }
         // Clean up any void dynamic grants.
         Collection<PermissionGrant> remove = new ArrayList<PermissionGrant>(40);
 	Iterator<PermissionGrant> i = dynamicPolicyGrants.iterator();
@@ -643,15 +639,24 @@ Put the policy providers and all referen
             }
         }
         dynamicPolicyGrants.removeAll(remove);
+        // Increment transaction ID after cache clear, 
+        transactionWriteLock.lock();
+        try {
+            cache.clear();
+            transactionID++;
+        }finally{
+            transactionWriteLock.unlock();
+        }
         // Don't bother removing void from the remotePolicy, it get's replaced anyway.
         // Policy file based grant's don't become void, only dynamic grant's
         // to ProtectionDomain or ClassLoader.
-        SecurityManager sm = System.getSecurityManager();
+        final SecurityManager sm = System.getSecurityManager();
         if (sm != null){
             if (sm instanceof DelegateSecurityManager){
                 ((DelegateSecurityManager) sm).clearFromCache(null);
             }
         }
+        
     }
 
     public boolean grantSupported() {
@@ -684,10 +689,16 @@ Put the policy providers and all referen
                 .permissions(permissions)
                 .context(PermissionGrantBuilder.CLASSLOADER)
                 .build();
-	// We built this grant it's safe to trust.
-	grantCache.put(pe, permissions); // Replace any existing too.
 	// This grant is new, in the grantCache and we trust it.
 	dynamicPolicyGrants.add(pe);
+        // Increment transaction ID after cache clear, 
+        transactionWriteLock.lock();
+        try {
+            cache.clear();
+            transactionID++;
+        }finally{
+            transactionWriteLock.unlock();
+        }
 //	if (loggable){
 //	    logger.log(Level.FINEST, "Granting: {0}", pe.toString());
 //	}
@@ -745,8 +756,18 @@ Put the policy providers and all referen
                 grants.remove();
 	    }
 	}
-        // Unfortunately this is quite expensive, but we don't know which ProtectionDomains a ClassLoader references.
-        cache.clear();
+        // Unfortunately we don't know which
+        // ProtectionDomains a ClassLoader references, so we must clear the
+        // cache.
+        // Increment transaction ID after cache clear.
+        transactionWriteLock.lock();
+        try {
+            cache.clear();
+            transactionID++;
+        }finally{
+            transactionWriteLock.unlock();
+        }
+        
         SecurityManager sm = System.getSecurityManager();
         if (sm instanceof DelegateSecurityManager) {
             ((DelegateSecurityManager) sm).clearFromCache(removed);
@@ -832,7 +853,33 @@ Put the policy providers and all referen
 	// changes between now and gaining the lock, only the length of the
 	// HashSet is potentially not optimal, keeping the HashSet creation
 	// outside of the lock reduces the lock held duration.
-        checkNullElements(grants);
+        Set<ProtectionDomain> domains = new HashSet<ProtectionDomain>();
+        int l = grants.length;
+        for (int i = 0; i < l; i++ ){
+            if (grants[i] == null ) throw new NullPointerException("null PermissionGrant prohibited");
+            // This causes a ProtectionDomain security check.
+            final Class c = grants[i].getClass();
+            List<ProtectionDomain> doms = AccessController.doPrivileged(
+                new PrivilegedAction<List<ProtectionDomain>>() {
+                    public List<ProtectionDomain> run() {
+                        Class[] classes = c.getDeclaredClasses();
+                        List<ProtectionDomain> domains = new ArrayList<ProtectionDomain>();
+                        int l = classes.length;
+                        for ( int i = 0; i < l; i++ ){
+                            domains.add(classes[i].getProtectionDomain());
+                        }
+                        return domains;
+                    }
+                });
+            domains.addAll(doms);
+        }
+        Iterator<ProtectionDomain> it = domains.iterator();
+        while (it.hasNext()){
+            if ( ! it.next().implies(implementsPermissionGrant)) {
+                throw new SecurityException("Missing permission: " 
+                        + implementsPermissionGrant.toString());
+            }
+        }
 	HashSet<PermissionGrant> holder 
 		    = new HashSet<PermissionGrant>(grants.length);
 	    holder.addAll(Arrays.asList(grants));
@@ -843,7 +890,13 @@ Put the policy providers and all referen
 	    PermissionGrant[] updated = new PermissionGrant[holder.size()];
 	    remotePolicyGrants = holder.toArray(updated);
 	}
-        cache.clear();
+        transactionWriteLock.lock();
+        try {
+            cache.clear();
+            transactionID++;
+        }finally{
+            transactionWriteLock.unlock();
+        }
         Collection<PermissionGrant> oldGrants = new HashSet<PermissionGrant>(old.length);
         oldGrants.addAll(Arrays.asList(old));
         oldGrants.removeAll(holder);
@@ -861,52 +914,5 @@ Put the policy providers and all referen
         }
         // oldGrants now only has the grants which have been removed.
     }
-    
-    /*
-     * The sole purpose of this implementation is to make Dynamic permissions
-     * printable for debugging from a ProtectionDomain without their contained 
-     * permissions becoming merged in the static ProtectionDomain permissions.
-     */
-    private static class PolicyPermission extends Permission {
-        private static final long serialVersionUID = 1L;
-
-        private final Permission perm;
-
-        PolicyPermission(Permission p){
-            super("Dynamic Policy");
-            perm = p;
-        }
-
-        @Override
-        public boolean implies(Permission permission) {
-            return equals(permission);
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (obj == this) return true;
-            if (obj instanceof PolicyPermission &&
-                this.perm.equals(((PolicyPermission) obj).perm)) return true;           
-            return false;
-        }
-
-        @Override
-        public int hashCode() {
-            int hash = 7;
-            hash = 31 * hash + (this.perm != null ? this.perm.hashCode() : 0);
-            return hash;
-        }
-
-        @Override
-        public String toString(){
-            return perm.toString();
-        }
-
-        @Override
-        public String getActions() {
-            return "";
-        }
-
-    }
 
 }

Modified: river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/DelegateCombinerSecurityManager.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/DelegateCombinerSecurityManager.java?rev=1225993&r1=1225992&r2=1225993&view=diff
==============================================================================
--- river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/DelegateCombinerSecurityManager.java (original)
+++ river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/DelegateCombinerSecurityManager.java Sat Dec 31 00:13:00 2011
@@ -18,14 +18,15 @@
 
 package org.apache.river.api.security;
 
-import java.net.SocketPermission;
 import java.security.AccessControlContext;
 import java.security.AccessController;
+import java.security.AllPermission;
 import java.security.DomainCombiner;
 import java.security.Guard;
 import java.security.Permission;
 import java.security.PrivilegedAction;
 import java.security.ProtectionDomain;
+import java.security.SecurityPermission;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Comparator;
@@ -40,11 +41,9 @@ import java.util.concurrent.ConcurrentMa
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.SynchronousQueue;
 import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.Level;
@@ -77,9 +76,13 @@ extends SecurityManager implements Deleg
     private final Action action;
     private final ExecutorService executor;
     private final Comparator<Referrer<Permission>> permCompare;
+    private final AccessControlContext SMContext;
     
     public DelegateCombinerSecurityManager(){
         super();
+        // Get context before this becomes a SecurityManager.
+        // super() checked the permission to create a SecurityManager.
+        SMContext = AccessController.getContext();
         dc = new DelegateDomainCombiner();
         ConcurrentMap<Referrer<AccessControlContext>, 
                 Referrer<AccessControlContext>> internal = 
@@ -90,7 +93,7 @@ extends SecurityManager implements Deleg
                 = new ConcurrentHashMap<Referrer<AccessControlContext>, 
                 Referrer<Set<Permission>>>(100);
         checked = RC.concurrentMap(refmap, Ref.SOFT, Ref.STRONG);
-        g = new RevokePermission();
+        g = new SecurityPermission("getPolicy");
         action = new Action();
         // Make this a tunable property.
         double blocking_coefficient = 0.6; // 0 CPU intensive to 0.9 IO intensive
@@ -108,12 +111,19 @@ extends SecurityManager implements Deleg
                 TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), 
                 new ThreadPoolExecutor.CallerRunsPolicy());
         permCompare = RC.comparator(new PermissionComparator());
+        // Refresh the policy, in case it hasn't parsed all policy files.
+        java.security.Policy.getPolicy().refresh();
+    }
+    
+    public void checkPermission(Permission perm) throws SecurityException {
+        checkPermission(perm, AccessController.getContext());
     }
     
     @Override
     public void checkPermission(Permission perm, Object context) throws SecurityException {
 	if (!(context instanceof AccessControlContext)) throw new SecurityException();
 	if (perm == null ) throw new NullPointerException("Permission Collection null");
+        if (SMContext.equals(context)) return; // prevents endless loop in debug.
         AccessControlContext executionContext = (AccessControlContext) context;
         // Checks if Permission has already been checked for this context.
         Set<Permission> checkedPerms = checked.get(executionContext);
@@ -125,43 +135,49 @@ extends SecurityManager implements Deleg
             if (existed != null) checkedPerms = existed;
         }
         if (checkedPerms.contains(perm)) return; // don't need to check again.
-        if (perm instanceof DelegatePermission) {
-            // This is an expensive operation, so we cache the created AccessControlContext.
-            AccessControlContext delegateContext = contextCache.get(executionContext);
-            if (delegateContext == null) {
-                final AccessControlContext finalExecutionContext = executionContext;
-                // Create a new AccessControlContext with the DelegateDomainCombiner
-                // we don't need to preserve the Subject accross the call
-                // we have sufficient privilege.
-                delegateContext = AccessController.doPrivileged( 
-                    new PrivilegedAction<AccessControlContext>(){
-                        public AccessControlContext run() {
-                            return new AccessControlContext(finalExecutionContext, dc);
-                        }
+        // This is an expensive operation, so we cache the created AccessControlContext.
+        AccessControlContext delegateContext = contextCache.get(executionContext);
+        if (delegateContext == null) {
+            final AccessControlContext finalExecutionContext = executionContext;
+            // Create a new AccessControlContext with the DelegateDomainCombiner
+            // we don't need to preserve the Subject accross the call
+            // we have sufficient privilege.
+            delegateContext = AccessController.doPrivileged( 
+                new PrivilegedAction<AccessControlContext>(){
+                    public AccessControlContext run() {
+                        return new AccessControlContext(finalExecutionContext, dc);
                     }
-                );
-                // Optimise the delegateContext, this runs the DelegateDomainCombiner
-                // and returns the AccessControlContext.
-                // This is a mutator method, the delegateContext returned
-                // is actually the same object passed in, after it is
-                // mutated, but just in case that changes in future we
-                // return it.
-                delegateContext = AccessController.doPrivileged(action, delegateContext);
-                contextCache.putIfAbsent(executionContext, delegateContext);
-                // Above putIfAbsent: It doesn't matter if it already existed,
-                // the context we have is valid to perform a permissionCheck.
-            }
-            executionContext = delegateContext;
-        } 
+                }
+            );
+            // Optimise the delegateContext, this runs the DelegateDomainCombiner
+            // and returns the AccessControlContext.
+            // This is a mutator method, the delegateContext returned
+            // is actually the same object passed in, after it is
+            // mutated, but just in case that changes in future we
+            // return it.
+            delegateContext = AccessController.doPrivileged(action, delegateContext);
+            contextCache.putIfAbsent(executionContext, delegateContext);
+            // Above putIfAbsent: It doesn't matter if it already existed,
+            // the context we have is valid to perform a permissionCheck.
+        }
         // Normal execution, same as SecurityManager.
-        executionContext.checkPermission(perm);
+        delegateContext.checkPermission(perm);
         /* It's ok to cache SocketPermission if we use a comparator */
         // If we get to here, no exceptions were thrown, we have permission.
         // Don't cache SocketPermission.
         // if (perm instanceof SocketPermission) return;
         checkedPerms.add(perm);
     }
-
+    
+    /**
+     * This method is intended to be called only by a Policy.
+     * 
+     * To clear the cache of checked Permissions requires the following Permission:
+     * java.security.SecurityPermission("getPolicy");
+     * 
+     * @param perms
+     * @throws SecurityException 
+     */
     public void clearFromCache(Set<Permission> perms) throws SecurityException {
         // This is a slow operation, with the benefit
         // of faster security checks, which occur far more often.
@@ -294,10 +310,11 @@ extends SecurityManager implements Deleg
                 // interrupt status swallowed.
                 boolean externalInterrupt = false;
                 if (!terminated.get()) externalInterrupt = true;
-
-                Iterator<Future<Boolean>> it = resultList.iterator();
-                while (it.hasNext()){
-                    it.next().cancel(true);
+                if ( resultList != null ){ // could be interrupted before executor invoking.
+                    Iterator<Future<Boolean>> it = resultList.iterator();
+                    while (it.hasNext()){
+                        it.next().cancel(true);
+                    }
                 }
                 Logger.getLogger(DelegateCombinerSecurityManager.class.getName()).log(Level.FINEST, null, ex);
                 // Task interruption is normal, it just means one task returned false.

Copied: river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/PolicyPermission.java (from r1222835, river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/RevokePermission.java)
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/PolicyPermission.java?p2=river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/PolicyPermission.java&p1=river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/RevokePermission.java&r1=1222835&r2=1225993&rev=1225993&view=diff
==============================================================================
--- river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/RevokePermission.java (original)
+++ river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/PolicyPermission.java Sat Dec 31 00:13:00 2011
@@ -5,49 +5,26 @@
 
 package org.apache.river.api.security;
 
-import java.security.Permission;
+import java.security.BasicPermission;
 
 /**
- * <p>RevokePermission allows for a permission to be revoked at runtime provided
- * it has been dynamically granted.<p>
- * 
- * A RevokePermission cannot dynamically grant itself a permission.<p>
- * 
- * A domain with revoke permission can not revoke a RevokePermission
- * unless it has been granted dynamically. </p>
- *
- * -- seems logical.
+ * <p>A "revoke" or "REVOKE" PolicyPermission is allows updating a 
+ * RemotePolicy or remove Dynamically granted permission from a 
+ * RevokeableDynamicPolicy.</p>
  * 
+ * <p>A "implementPermissionGrant" PolicyPermission allows a class to implement
+ * org.apache.river.api.security.PermissionGrant interface and use it to
+ * update a RemotePolicy.  This is not a permission to grant lightly, since
+ * a poor implementation could destabilise a policy or worse allow the caller
+ * to grant AllPermission anyone using mutation.</p>
  * 
  * @author Peter Firmstone
  */
-public class RevokePermission extends Permission {
+public class PolicyPermission extends BasicPermission {
     private static final long serialVersionUID = 1L;
     
-    public RevokePermission(){
-        super("");
+    public PolicyPermission(String name){
+        super(name);
     }
     
-    @Override
-    public boolean implies(Permission permission) {
-        if ( !(permission instanceof RevokePermission)) return false;
-	if ( permission.getClass() != this.getClass()) return false;
-        return true;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj instanceof RevokePermission) return true;
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        return RevokePermission.class.hashCode();
-    }
-
-    @Override
-    public String getActions() {
-        return "";
-    }
 }

Modified: river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/policy/util/Messages.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/policy/util/Messages.java?rev=1225993&r1=1225992&r2=1225993&view=diff
==============================================================================
--- river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/policy/util/Messages.java (original)
+++ river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/policy/util/Messages.java Sat Dec 31 00:13:00 2011
@@ -242,7 +242,7 @@ public class Messages {
         // Attempt to load the messages.
         try {
             bundle = setLocale(Locale.getDefault(),
-                    "org.apache.river.security.policy.util.messages"); //$NON-NLS-1$
+                    "org.apache.river.impl.security.policy.util.messages"); //$NON-NLS-1$
         } catch (Throwable e) {
             e.printStackTrace();
         }

Modified: river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/util/AbstractReferenceComparator.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/util/AbstractReferenceComparator.java?rev=1225993&r1=1225992&r2=1225993&view=diff
==============================================================================
--- river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/util/AbstractReferenceComparator.java (original)
+++ river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/util/AbstractReferenceComparator.java Sat Dec 31 00:13:00 2011
@@ -31,8 +31,33 @@ abstract class AbstractReferenceComparat
     AbstractReferenceComparator() {
     }
 
+    /**
+     * This is implemented such that if either Referrer contains a null
+     * referent, the comparison is only made using Referrer's, this may
+     * have a different natural order, than the comparator provided, however
+     * equals will always return 0, this is important to correctly remove
+     * a Referrer once its referent has been collected.
+     * 
+     * The following tests give this a good workout:
+     * 
+     * com/sun/jini/test/impl/joinmanager/ZRegisterStorm.td
+     * com/sun/jini/test/spec/renewalmanager/EventTest.td
+     * 
+     * @param o1
+     * @param o2
+     * @return 
+     */
     public int compare(Referrer<T> o1, Referrer<T> o2) {
-        return get().compare(o1 != null ? o1.get() : null, o2 != null ? o2.get() : null);
+        if (o1 == o2) return 0;
+        T t1 = o1.get();
+        T t2 = o2.get();
+        if ( t1 != null && t2 != null) return get().compare(t1, t2);
+        int hash1 = o1.hashCode();
+        int hash2 = o2.hashCode();
+        if (hash1 < hash2) return -1;
+        if (hash1 > hash2) return 1;
+        if (o1.equals(o2)) return 0;
+        return -1;
     }
 
     @Override

Modified: river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/util/ReferenceFactory.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/util/ReferenceFactory.java?rev=1225993&r1=1225992&r2=1225993&view=diff
==============================================================================
--- river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/util/ReferenceFactory.java (original)
+++ river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/util/ReferenceFactory.java Sat Dec 31 00:13:00 2011
@@ -39,6 +39,7 @@ class ReferenceFactory<T> {
     private ReferenceFactory(){}
     
     static <T> Referrer<T> create(T t, ReferenceQueue<? super T> queue, Ref type ){
+        if (t == null) throw new NullPointerException("Reference collections cannot contain null");
         switch (type){
             case WEAK_IDENTITY: 
                 return new ReferrerWrapper<T>(new WeakIdentityReferenceKey<T>(t, queue));
@@ -78,6 +79,7 @@ class ReferenceFactory<T> {
      * @return 
      */
     static <T> Referrer<T> singleUseForLookup(T t, Ref type){
+        if (t == null) throw new NullPointerException("Reference collections cannot contain null");
         switch (type){
             case WEAK_IDENTITY: 
                 return new TempIdentityReferrer<T>(t);