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 2010/08/22 02:08:52 UTC

svn commit: r987840 - in /incubator/river/jtsk/trunk/src/org/apache/river: api/security/ imp/security/policy/se/ imp/util/

Author: peter_firmstone
Date: Sun Aug 22 00:08:52 2010
New Revision: 987840

URL: http://svn.apache.org/viewvc?rev=987840&view=rev
Log:
RevokableDynamicPolicy related changes, please review.  Note these features are experimental, subject to change or removal.

Added:
    incubator/river/jtsk/trunk/src/org/apache/river/imp/util/ConcurrentSoftMap.java
      - copied, changed from r986600, incubator/river/jtsk/trunk/src/org/apache/river/imp/util/ConcurrentSoftIdentityMap.java
    incubator/river/jtsk/trunk/src/org/apache/river/imp/util/ConcurrentWeakMap.java   (with props)
Removed:
    incubator/river/jtsk/trunk/src/org/apache/river/imp/util/ConcurrentSoftIdentityMap.java
Modified:
    incubator/river/jtsk/trunk/src/org/apache/river/api/security/ExecutionContextManager.java
    incubator/river/jtsk/trunk/src/org/apache/river/api/security/RevokeableDynamicPolicy.java
    incubator/river/jtsk/trunk/src/org/apache/river/imp/security/policy/se/ECM.java
    incubator/river/jtsk/trunk/src/org/apache/river/imp/util/ConcurrentWeakIdentityMap.java

Modified: incubator/river/jtsk/trunk/src/org/apache/river/api/security/ExecutionContextManager.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/api/security/ExecutionContextManager.java?rev=987840&r1=987839&r2=987840&view=diff
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/api/security/ExecutionContextManager.java (original)
+++ incubator/river/jtsk/trunk/src/org/apache/river/api/security/ExecutionContextManager.java Sun Aug 22 00:08:52 2010
@@ -37,25 +37,9 @@ import java.util.Collection;
  * when a Permission Revocation matching the stored Permission occurs.
  * </p><p>
  * Use of this class is not limited to Revokeable Permission's, although a
- * revocation event will cause #checkPermission(Permission) and #end() to block
+ * revocation event will cause #checkPermission(Permission) to block
  * until the revocation process is complete.
  * </p><p>
- * Typical usage:
- * </p>
- * <code>
- * <PRE>
- * ecm.begin(reaper);
- * try{
- *	    ecm.checkPermission(permissionA);
- *	    ecm.checkPermission(permissionB);
- *	    // do something
- *	    return;
- * } finally {
- *	    ecm.end();
- * }
- * </PRE>
- * </code>
- * <p>
  * When protecting method's, the method must return from the try block.
  * </p>
  * @author Peter Firmstone
@@ -64,36 +48,6 @@ import java.util.Collection;
  * @see AccessControlContext
  */
 public interface ExecutionContextManager {
-    
-    /*
-     * <p>
-     * Marks the beginning of Management of the Execution context, of the
-     * AccessControlContext and submits a reaper to intercept and clean up
-     * in the event of a revocation during the execution of the try finally
-     * block.  This method may be omitted if a Reaper is not required.  The
-     * consequence of there being no reaper, is that a call in progress during
-     * revocation will return normally immediately after revocation has 
-     * occurred, the permission will have been checked prior to revocation
-     * however and any further permission checks, if they have been revoked
-     * will throw an AccessControlException.
-     * <p></p>
-     * This links the current Thread to a Runnable
-     * Reaper.  The checkPermission() call places the Thread and
-     * AccessControlContext into the execution cache.
-     * <p></p>
-     * The execution cache is used to monitor methods or protected blocks that
-     * must be intercepted.
-     * If desired, the reaper can be used to simply set a volatile variable, 
-     * in the original object, so a check in the final block can throw 
-     * an AccessControlException.
-     * </p>
-     * @param r Reaper provided to clean up if Revocation occurs during
-     * the execution that follows this call, until the try block exits, 
-     * the current thread is not interrupted, rather the reaper is expected
-     * to know what resources need to be closed.
-     */
-    // To Be removed
-    //void begin(Reaper r);
 
     /**
      * <p>
@@ -141,44 +95,6 @@ public interface ExecutionContextManager
      * @throws java.security.AccessControlException
      * @throws java.lang.NullPointerException 
      */
-    public void checkPermission(Collection<Permission> perms) throws AccessControlException,
-	    NullPointerException;
-    
-    /*
-     * <p>
-     * This method is to advise the ExecutionContextManager that the
-     * current method or protected region has returned, it must
-     * always follow the checkPermission() call, in response,
-     * the ECM removes the current context from the execution context cache
-     * and releases the reference to the Runnable reaper.
-     * </p><p>
-     * If the execution context is still in the cache at the time of 
-     * revocation, the reaper will be run only if affected directly by the 
-     * revocation, the thread may be asked to wait for a short period, to
-     * allow the determination to be made. 
-     * Revocation applicability is determined by
-     * AccessControlContext.checkPermission(Permission p) where p is the 
-     * Permission affected.
-     * </p><p>
-     * This should be executed in the finally{} block of a try catch statement,
-     * which always executes in the event of an exception or normal return.
-     * </p>
-     * <code>
-     * <PRE>
-     * ecm.begin(reaper);
-     * try{
-     *	    ecm.checkPermission(permission);
-     *	    // do something
-     *	    return;
-     * } finally {
-     *	    ecm.end();
-     * }
-     * </PRE>
-     * </code>
-     * <p>
-     * This should not be confused with AccessController.doPrivileged blocks
-     * </p>
-     */
-    // To be removed
-    //void end();
+    public void checkPermission(Collection<Permission> perms) 
+	    throws AccessControlException, NullPointerException;
 }
\ No newline at end of file

Modified: incubator/river/jtsk/trunk/src/org/apache/river/api/security/RevokeableDynamicPolicy.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/api/security/RevokeableDynamicPolicy.java?rev=987840&r1=987839&r2=987840&view=diff
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/api/security/RevokeableDynamicPolicy.java (original)
+++ incubator/river/jtsk/trunk/src/org/apache/river/api/security/RevokeableDynamicPolicy.java Sun Aug 22 00:08:52 2010
@@ -22,10 +22,45 @@ import java.security.Permission;
 import java.util.List;
 
 /**
+ * <p>
  * RevokeableDynamicPolicy, is a Java Security Policy Provider that supports
- * Runtime Dynamically Grantable and Revokeable Permission's, in the form
- * of PermissionGrant's
- * 
+ * Runtime Dynamic addition and removal of PermissionGrant's
+ * </p><p>
+ * Warning: Not all Permission's are truely revokeable, while any Permission can 
+ * be dynamically added and later removed from this policy, the majority of JVM Permission's
+ * don't prevent references from escaping.
+ * </p><p>
+ * To quote Tim Blackman, from river-dev:
+ * </p><p><CITE>
+ * I remember talking with Bob and Mike Warres about this.  The problem with
+ * removing permission grants is that when code is granted a permission, 
+ * it can very likely squirrel away something -- an object, or another 
+ * capability available through the granted permission -- that will permit 
+ * it to perform the same operation again without the JVM checking for 
+ * the permission again.
+ * </CITE>
+ * </p><p>
+ * In order for a Permission to be fully revoked, the permission must be
+ * used to guard methods only, not Objects or their creation.  A Security 
+ * Delegate, may be used as a wrapper with an identical interface to the object
+ * it protects, a new Permission class must be implemented, for the Delegate's
+ * use, in a checkPermission call, to protect access to the underlying
+ * object's method. If an existing JVM Permission guards the underlying object,
+ * the delegate needs to be given the standard JVM Permission.
+ * </p><p>
+ * The ability to revoke a Permission fully is intended for smart proxy's to
+ * be given some trust temporarily, so that objects recieved from the smart proxy 
+ * by a client cannot be used to continue gathering and sending information to
+ * a remote server after the proxy has been discarded.
+ * </p><p>
+ * A list of Permission's that are revokeable will be provided here.
+ * </p><p>
+ * TODO: Write some Permission's that are revokeable and delegates
+ * for the network and test.
+ * </p><p>
+ * Note: This feature is currently experimental, it should not be relied upon for any
+ * application and may never make release.
+ * </p>
  * @author Peter Firmstone
  * @see java.security.Policy
  * @see java.security.ProtectionDomain
@@ -33,6 +68,7 @@ import java.util.List;
  * @see java.security.DomainCombiner
  * @see java.security.AccessControlContext
  * @see java.security.Permission
+ * @see PermissionGrant
  */
 public interface RevokeableDynamicPolicy {
     /**
@@ -60,10 +96,10 @@ public interface RevokeableDynamicPolicy
      */
     public List<PermissionGrant> getPermissionGrants();
     /**
-     * The Revocation of Permission's requires a new construct for controlling
-     * access.  Typically many objects that provide privileged functionality
+     * The Revocation of Permission's requires an optimised check permission
+     * call.  Typically many objects that provide privileged functionality
      * are guarded in their constructor by a checkPermission(Permission) call
-     * or by a GuardedObject, once this check has succeeded, the caller receives
+     * or by a GuardedObject, once a check has succeeded, the caller receives
      * a reference to the guarded object.  These Permission's cannot be
      * revoked completely, because the reference has escaped, the permission 
      * check will not be called again.

Modified: incubator/river/jtsk/trunk/src/org/apache/river/imp/security/policy/se/ECM.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/imp/security/policy/se/ECM.java?rev=987840&r1=987839&r2=987840&view=diff
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/imp/security/policy/se/ECM.java (original)
+++ incubator/river/jtsk/trunk/src/org/apache/river/imp/security/policy/se/ECM.java Sun Aug 22 00:08:52 2010
@@ -22,27 +22,18 @@ import java.security.AccessControlContex
 import java.security.AccessControlException;
 import java.security.AccessController;
 import java.security.Permission;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 import org.apache.river.api.security.ExecutionContextManager;
-import org.apache.river.api.security.Reaper;
-import org.apache.river.imp.util.ConcurrentWeakIdentityMap;
+import org.apache.river.imp.util.ConcurrentSoftMap;
 
 /**
  * Only a Single instance of ECM is required per policy, it is threadsafe.
@@ -61,10 +52,6 @@ import org.apache.river.imp.util.Concurr
 class ECM implements ExecutionContextManager{
     
     private final ConcurrentMap<Permission,Set<AccessControlContext>> checkedCache;
-    //private final ConcurrentMap<AccessControlContext, Set<Thread>> executionCache;
-    //private final ConcurrentMap<Thread, Set<AccessControlContext>> threadAssociation;
-    //private final ConcurrentMap<Thread, Reaper> association;
-    //private final ExecutorService executor;
     private final ReadWriteLock revokeLock;
     private final Lock rl; // This lock is held briefly by callers of begin and end.
     private final Lock wl; // This lock is held by revocation.
@@ -72,23 +59,7 @@ class ECM implements ExecutionContextMan
     ECM(){
 	/* The clients control garbage collection, the Permission objects
 	 * are those passed by clients.*/
-	checkedCache = new ConcurrentWeakIdentityMap<Permission, Set<AccessControlContext>>();
-	/* The thread association controls the removal of AccessContolContext
-	 * keys from the executionCache, since the threadAssociation holds a 
-	 * strong reference from the thread, which is removed when end()
-	 * is executed.
-	 */ 
-	//executionCache = new ConcurrentWeakIdentityMap<AccessControlContext, Set<Thread>>();
-	/* Thread association is utilised to track a thread as it enters and
-	 * leaves the ExecutionContextManager try finally block.
-	 */ 
-	//threadAssociation = new ConcurrentHashMap<Thread, Set<AccessControlContext>>();
-	/* The association is only made while threads are within the clients
-	 * try finally block.
-	 */ 
-	//association = new ConcurrentHashMap<Thread, Reaper>();
-	// TODO: Analyse needs, enable client configuration of thread pool.
-	//executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()+1);
+	checkedCache = new ConcurrentSoftMap<Permission, Set<AccessControlContext>>(300);
 	/* This lock guards revocation */ 
 	revokeLock = new ReentrantReadWriteLock();
 	rl = revokeLock.readLock();
@@ -98,32 +69,6 @@ class ECM implements ExecutionContextMan
     void revoke(Set<Permission> perms) throws InterruptedException, ExecutionException{
 	wl.lock();
 	try {
-	    /* This is where we determine what needs to be revoked, we first
-	     * get all the AccessControlContexts for the Permission class,
-	     * these are removed from the checkedCache, then we narrow
-	     * down the AccessControlContext's to only those in the
-	     * execution cache, each AccessControlContext in the execution cache
-	     * has checkPermission(Permission) called for each permission
-	     * with a Class identical to one of the revoked permission's.  
-	     * Any AccessControlContext's that throw AccessControlException will be
-	     * caught and have a Reaper run, when it exists, for all Threads referenced
-	     * from that AccessControlContext.
-	     * 
-	     * The RevokeableDynamicPolicy will have updated the current
-	     * Permission's after revocation, so the ProtectionDomain's in
-	     * the AccessControlContext will now throw an AccessControlException
-	     * if the revocation applied to them.
-	     * 
-	     * The wl lock will be released, any threads that were interrupted
-	     * will exit through the finally block and remove themselves
-	     * from the execution cache.  Those that aren't may throw
-	     * an exception and bubble up the stack, due to closing sockets
-	     * etc.
-	     * 
-	     * The execution cache is comprised of the following fields:
-	     * executionCache
-	     * threadAssociation
-	     */ 
 	    // Identify Permission's with matching class files to those revoked.
 	    Set<Class> permClasses = new HashSet<Class>();
 	    Iterator<Permission> itp = perms.iterator();
@@ -131,79 +76,20 @@ class ECM implements ExecutionContextMan
 		permClasses.add(itp.next().getClass());
 	    }
 	    // Remove Permission's and AccessControlContexts from the checked cache.
-//	    Map<Permission, Set<AccessControlContext>> removed = 
-//		    new HashMap<Permission, Set<AccessControlContext>>();
 	    Iterator<Permission> keysIt = checkedCache.keySet().iterator();
 	    while (keysIt.hasNext()){
 		Permission p = keysIt.next();
 		if (permClasses.contains(p.getClass())){
 		    Set<AccessControlContext> a = checkedCache.get(p);
 		    keysIt.remove();
-//		    removed.put(p, a);
 		}		
 	    }
-//	    // Match the AccessControlContexts with the execution cache;
-//	    Set<AccessControlContext> exCache = executionCache.keySet();
-//	    // Get the AccessControlContext's in the execution cache that fail.
-//	    Set<AccessControlContext> accFails = new HashSet<AccessControlContext>();
-//	    Iterator<Permission> retests = removed.keySet().iterator();
-//	    while (retests.hasNext()){
-//		Permission p = retests.next();
-//		Set<AccessControlContext> rechecks = removed.get(p);
-//		Iterator<AccessControlContext> recheck = rechecks.iterator();
-//		while (recheck.hasNext()){
-//		    AccessControlContext a = recheck.next();
-//		    if (accFails.contains(a)) continue;
-//		    // This really narrows down the checks.
-//		    if (exCache.contains(a)){
-//			try { 
-//			    a.checkPermission(p);
-//			} catch (AccessControlException e){
-//			    accFails.add(a);
-//			}
-//		    }
-//		}
-//	    }
-//	    // Identify the threads and prepare reapers.
-//	    Set<Runnable> reapers = new HashSet<Runnable>();
-//	    Iterator<AccessControlContext> failedAcc = accFails.iterator();
-//	    while (failedAcc.hasNext()){
-//		AccessControlContext fail = failedAcc.next();
-//		Set<Thread> threads = executionCache.get(fail);
-//		Iterator<Thread> i = threads.iterator();
-//		while (i.hasNext()) {
-//		    Thread t = i.next();
-//		    Reaper r = association.get(t);
-//		    if ( r == null ) continue;
-//		    r.put(t);
-//		    reapers.add(r);
-//		}		
-//	    }
-//	    /* Process the reapers, this requires a thread pool, but we don't
-//	     * want to return until all reapers have completed.
-//	     */
-//	    Iterator<Runnable> reaper = reapers.iterator();
-//	    List<Future> results = new ArrayList<Future>(reapers.size());
-//	    while ( reaper.hasNext()) {
-//		results.add(executor.submit(reaper.next()));
-//	    }
-//	    Iterator<Future> result = results.iterator();
-//	    while (result.hasNext()){
-//		// Waits for result.
-//		result.next().get();
-//	    }
 	    /* We're done, go home & rest */
 	} finally {
 	    wl.unlock();
 	}
     }
 
-//    public void begin(Reaper r) {
-//	Thread currentThread = Thread.currentThread();
-//	if ( r == null ) return;
-//	association.put(currentThread, r);	
-//    }
-
     public void checkPermission(Collection<Permission> perms) throws AccessControlException {
 	if (perms == null ) throw new NullPointerException("Permission Collection null");
 	if (perms.isEmpty()) return; // Should we do this or is it a bug?
@@ -213,26 +99,6 @@ class ECM implements ExecutionContextMan
 	permissions.addAll(perms);
 	rl.lock();
 	try {
-//	    // execution cache, fast for repeated calls.
-//	    Set<Thread> exCacheThreadSet = executionCache.get(executionContext);
-//	    if ( exCacheThreadSet == null ){
-//		exCacheThreadSet = Collections.synchronizedSet(new HashSet<Thread>());
-//		Set<Thread> existed = executionCache.putIfAbsent(executionContext, exCacheThreadSet);
-//		if (existed != null){
-//		    exCacheThreadSet = existed;
-//		}
-//	    }
-//	    exCacheThreadSet.add(currentThread);// end execution cache.
-//	    // thread association, fast for repeated calls.
-//	    Set<AccessControlContext> thAssocSet = threadAssociation.get(currentThread);
-//	    if ( thAssocSet == null ){
-//		thAssocSet = Collections.synchronizedSet(new HashSet<AccessControlContext>());
-//		Set<AccessControlContext> existed = threadAssociation.putIfAbsent(currentThread, thAssocSet);
-//		if (existed != null){
-//		    thAssocSet = existed;
-//		}
-//	    }
-//	    thAssocSet.add(executionContext); // end thread association.
 	    // checkedCache - the permission check, fast for repeated calls.
 	    Iterator<Permission> permiter = permissions.iterator();
 	    while (permiter.hasNext()){
@@ -248,27 +114,10 @@ class ECM implements ExecutionContextMan
 		if ( checked.contains(executionContext)) continue; // it's passed before.
 		executionContext.checkPermission(p); // Throws AccessControlException
 		// If we get here cache the AccessControlContext.
-		checked.add(executionContext); // end checkedCache.
+		checked.add(executionContext);
 	    }
 	} finally {
 	    rl.unlock();
 	}
     }
-
-//    public void end() {
-//	// Removal from execution cache.
-//	Thread t = Thread.currentThread();
-//	rl.lock();
-//	try {
-//	    association.remove(t);
-//	    Set<AccessControlContext> accSet = threadAssociation.remove(t);
-//	    Iterator<AccessControlContext> it = accSet.iterator();
-//	    while (it.hasNext()){
-//		AccessControlContext acc = it.next();
-//		executionCache.get(acc).remove(t);
-//	    }
-//	}finally {
-//	    rl.unlock();
-//	}	
-//    }
 }

Copied: incubator/river/jtsk/trunk/src/org/apache/river/imp/util/ConcurrentSoftMap.java (from r986600, incubator/river/jtsk/trunk/src/org/apache/river/imp/util/ConcurrentSoftIdentityMap.java)
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/imp/util/ConcurrentSoftMap.java?p2=incubator/river/jtsk/trunk/src/org/apache/river/imp/util/ConcurrentSoftMap.java&p1=incubator/river/jtsk/trunk/src/org/apache/river/imp/util/ConcurrentSoftIdentityMap.java&r1=986600&r2=987840&rev=987840&view=diff
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/imp/util/ConcurrentSoftIdentityMap.java (original)
+++ incubator/river/jtsk/trunk/src/org/apache/river/imp/util/ConcurrentSoftMap.java Sun Aug 22 00:08:52 2010
@@ -31,10 +31,11 @@ import java.util.concurrent.ConcurrentHa
 import java.util.concurrent.ConcurrentMap;
 
 /**
- * Identity-based sofly referenced hash map, safe for concurrent threads.
+ * A Sofly referenced hash map, safe for concurrent threads.
  * 
  * Based on an underlying ConcurrentHashMap, it doesn't accept null keys.
  *
+ * Key's must not be mutated, soing so will cause strange results.
  *
  * @param K - key
  * @param V - value
@@ -42,11 +43,28 @@ import java.util.concurrent.ConcurrentMa
  *
  * @since 2.3
  */
-public class ConcurrentSoftIdentityMap<K, V> implements ConcurrentMap<K, V> {
+public class ConcurrentSoftMap<K, V> implements ConcurrentMap<K, V> {
     // ConcurrentHashMap must be protected from null values;
-    private final ConcurrentHashMap<Key, V> map = new ConcurrentHashMap<Key, V>();
-    private final ReferenceQueue queue = new ReferenceQueue();
-
+    private final ConcurrentHashMap<Key, V> map;
+    private final ReferenceQueue queue;
+    
+    public ConcurrentSoftMap(int initialCapacity, float loadFactor, int concurrencyLevel ){
+	map = new ConcurrentHashMap<Key, V>(initialCapacity, loadFactor, concurrencyLevel);
+	queue = new ReferenceQueue();
+    }
+    
+    public ConcurrentSoftMap(int initialCapacity, float loadFactor){
+	this(initialCapacity, loadFactor, 16);
+    }
+    
+    public ConcurrentSoftMap(int initialCapacity ){
+	this(initialCapacity, 0.75F, 16);
+    }
+    
+    public ConcurrentSoftMap(){
+	this(16, 0.75F, 16);
+    }
+    
     /**
      * Associates value with given key, returning value previously associated
      * with key, or null if none.
@@ -111,26 +129,24 @@ public class ConcurrentSoftIdentityMap<K
 	    if (q == null) {return new Key(k);}
 	    return new Key(k, q);	  
 	}
-
+	
 	private Key(T k) {
 	    super(k);
-	    hash = System.identityHashCode(k);
+	    hash = k.hashCode();
 	}
 
 	private Key(T k, ReferenceQueue<? super T> q) {
 	    super(k, q);
-	    hash = System.identityHashCode(k);
+	    hash = k.hashCode();
 	}
 
         @Override
 	public boolean equals(Object o) {
-	    if (this == o) {
-		return true;
-	    } else if (!(o instanceof Key)) {
-		return false;
-	    }
+	    if (this == o) return true;
+	    // Don't worry about hashcode because they're already equal.
+	    if (!(o instanceof Key)) return false;    
 	    Object k1 = get(), k2 = ((Key) o).get();
-	    return (k1 != null && k2 != null && k1 == k2);
+	    return (k1 != null && k1.equals(k2));
 	}
 
         @Override

Modified: incubator/river/jtsk/trunk/src/org/apache/river/imp/util/ConcurrentWeakIdentityMap.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/imp/util/ConcurrentWeakIdentityMap.java?rev=987840&r1=987839&r2=987840&view=diff
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/imp/util/ConcurrentWeakIdentityMap.java (original)
+++ incubator/river/jtsk/trunk/src/org/apache/river/imp/util/ConcurrentWeakIdentityMap.java Sun Aug 22 00:08:52 2010
@@ -44,8 +44,25 @@ import java.util.concurrent.ConcurrentMa
  */
 public class ConcurrentWeakIdentityMap<K, V> implements ConcurrentMap<K, V> {
     // ConcurrentHashMap must be protected from null values;
-    private final ConcurrentHashMap<Key, V> map = new ConcurrentHashMap<Key, V>();
-    private final ReferenceQueue queue = new ReferenceQueue();
+    private final ConcurrentHashMap<Key, V> map;
+    private final ReferenceQueue queue;
+    
+    public ConcurrentWeakIdentityMap(int initialCapacity, float loadFactor, int concurrencyLevel ){
+	map = new ConcurrentHashMap<Key, V>(initialCapacity, loadFactor, concurrencyLevel);
+	queue = new ReferenceQueue();
+    }
+    
+    public ConcurrentWeakIdentityMap(int initialCapacity, float loadFactor){
+	this(initialCapacity, loadFactor, 16);
+    }
+    
+    public ConcurrentWeakIdentityMap(int initialCapacity ){
+	this(initialCapacity, 0.75F, 16);
+    }
+    
+    public ConcurrentWeakIdentityMap(){
+	this(16, 0.75F, 16);
+    }
 
     /**
      * Associates value with given key, returning value previously associated

Added: incubator/river/jtsk/trunk/src/org/apache/river/imp/util/ConcurrentWeakMap.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/imp/util/ConcurrentWeakMap.java?rev=987840&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/imp/util/ConcurrentWeakMap.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/imp/util/ConcurrentWeakMap.java Sun Aug 22 00:08:52 2010
@@ -0,0 +1,231 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.river.imp.util;
+
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * A Sofly referenced hash map, safe for concurrent threads.
+ * 
+ * Based on an underlying ConcurrentHashMap, it doesn't accept null keys.
+ *
+ * Key's must not be mutated, soing so will cause strange results.
+ *
+ * @param K - key
+ * @param V - value
+ * @author Peter Firmstone.
+ *
+ * @since 2.3
+ */
+public class ConcurrentWeakMap<K, V> implements ConcurrentMap<K, V> {
+    // ConcurrentHashMap must be protected from null values;
+    private final ConcurrentHashMap<Key, V> map;
+    private final ReferenceQueue queue;
+    
+    public ConcurrentWeakMap(int initialCapacity, float loadFactor, int concurrencyLevel ){
+	map = new ConcurrentHashMap<Key, V>(initialCapacity, loadFactor, concurrencyLevel);
+	queue = new ReferenceQueue();
+    }
+    
+    public ConcurrentWeakMap(int initialCapacity, float loadFactor){
+	this(initialCapacity, loadFactor, 16);
+    }
+    
+    public ConcurrentWeakMap(int initialCapacity ){
+	this(initialCapacity, 0.75F, 16);
+    }
+    
+    public ConcurrentWeakMap(){
+	this(16, 0.75F, 16);
+    }
+    
+    /**
+     * Associates value with given key, returning value previously associated
+     * with key, or null if none.
+     * @param key - Key
+     * @param value - Value
+     * @return previous value or null
+     */
+    public V put(K key, V value) {
+	processQueue();
+        if (key == null){return null;}
+	return map.put(Key.create(key, queue), value);
+    }
+
+    /**
+     * Returns value associated with given key, or null if none.
+     */
+    public V get(Object key) {
+	processQueue();
+        if (key == null) { return null;}
+	return map.get(Key.create(key, null));
+    }
+
+    /**
+     * Removes association for given key, returning value previously associated
+     * with key, or null if none.
+     */
+    public V remove(Object key) {
+	processQueue();
+        if (key == null) {return null;}
+	return map.remove(Key.create(key, null));
+    }
+
+    /**
+     * Returns collection containing all values currently held in this map.
+     */
+    public Collection<V> values() {
+	processQueue();
+	return map.values();
+    }
+
+    /**
+     * Removes all associations from this map.
+     */
+    public void clear() {
+	processQueue();
+	map.clear();
+    }
+
+    private void processQueue() {
+	Key k;
+	while ((k = (Key) queue.poll()) != null) {
+	    map.remove(k);
+	}
+    }
+
+    private static class Key<T> extends WeakReference<T> {
+	private final int hash;
+
+        @SuppressWarnings("unchecked")
+	static Key create(Object k, ReferenceQueue q) {
+            //if (k == null) {return null;} // Perhaps this is incorrect
+	    if (q == null) {return new Key(k);}
+	    return new Key(k, q);	  
+	}
+	
+	private Key(T k) {
+	    super(k);
+	    hash = k.hashCode();
+	}
+
+	private Key(T k, ReferenceQueue<? super T> q) {
+	    super(k, q);
+	    hash = k.hashCode();
+	}
+
+        @Override
+	public boolean equals(Object o) {
+	    if (this == o) return true;
+	    // Don't worry about hashcode because they're already equal.
+	    if (!(o instanceof Key)) return false;    
+	    Object k1 = get(), k2 = ((Key) o).get();
+	    return (k1 != null && k1.equals(k2));
+	}
+
+        @Override
+	public int hashCode() {
+	    return hash;
+	}
+    }
+
+    public int size() {
+        processQueue();
+        return map.size();
+    }
+
+    public boolean isEmpty() {
+        processQueue();
+        return map.isEmpty();
+    }
+
+    @SuppressWarnings("unchecked")
+    public boolean containsKey(Object key) {
+        processQueue();
+        if (key == null) {return false;}
+        return map.containsKey(new Key(key));
+    }
+
+    public boolean containsValue(Object value) {
+        processQueue();
+        if (value == null) {return false;}
+        return map.containsValue(value);
+    }
+    
+    /**
+     * Unsupported method
+     * @param m
+     */
+    public void putAll(Map<? extends K, ? extends V> m) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+    
+    @SuppressWarnings("unchecked")
+    public Set<K> keySet() {
+        processQueue();
+        Enumeration<Key> keys = map.keys(); //Defensive copy by ConcurrentHashMap
+        Set<K> keySet = new HashSet<K>();
+        while (keys.hasMoreElements()){
+            keySet.add( (K) keys.nextElement().get());
+        }
+        return keySet;
+    }
+    
+    /**
+     * Unsupported method
+     * @return
+     */
+    public Set<Map.Entry<K, V>> entrySet() {
+        throw new UnsupportedOperationException("Not supported yet, ever?");
+    }
+
+    @SuppressWarnings("unchecked")
+    public V putIfAbsent(K key, V value) {
+        processQueue();  //may be a slight delay before atomic putIfAbsent
+        return map.putIfAbsent(new Key(key), value);       
+    }
+
+    @SuppressWarnings("unchecked")
+    public boolean remove(Object key, Object value) {
+        return map.remove(new Key(key), value);
+    }
+
+    @SuppressWarnings("unchecked")
+    public boolean replace(K key, V oldValue, V newValue) {
+        processQueue();
+        return map.replace(new Key(key), oldValue, newValue);
+    }
+
+    @SuppressWarnings("unchecked")
+    public V replace(K key, V value) {
+        processQueue();
+        return map.replace(new Key(key), value);
+    }
+}

Propchange: incubator/river/jtsk/trunk/src/org/apache/river/imp/util/ConcurrentWeakMap.java
------------------------------------------------------------------------------
    svn:eol-style = native