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