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/09/21 15:35:30 UTC
svn commit: r1173631 [1/2] - in /river/jtsk/skunk/peterConcurrentPolicy:
src/com/sun/jini/collection/ src/com/sun/jini/thread/
src/net/jini/loader/pref/ src/net/jini/security/
src/net/jini/security/policy/ src/org/apache/river/api/delegates/
src/org/ap...
Author: peter_firmstone
Date: Wed Sep 21 13:35:29 2011
New Revision: 1173631
URL: http://svn.apache.org/viewvc?rev=1173631&view=rev
Log:
Reference Collection Utilities
Modified:
river/jtsk/skunk/peterConcurrentPolicy/src/com/sun/jini/collection/WeakKeyReference.java
river/jtsk/skunk/peterConcurrentPolicy/src/com/sun/jini/thread/ThreadPool.java
river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/loader/pref/PreferredClassLoader.java
river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/Security.java
river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/policy/ConcurrentPolicyFile.java
river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/policy/DynamicPolicyProvider.java
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/delegates/DelegatePermission.java
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/DelegateCombinerSecurityManager.java
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/DelegateReflectionSecurityManager.java
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/dos/IsolatedExecutor.java
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/policy/util/DefaultPolicyParser.java
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/policy/util/DefaultPolicyScanner.java
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/policy/util/Messages.java
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/policy/util/PolicyUtils.java
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/util/ConcurrentCollections.java
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/util/ConcurrentSoftMap.java
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/util/ConcurrentWeakIdentityMap.java
river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/util/ConcurrentWeakMap.java
river/jtsk/skunk/peterConcurrentPolicy/test/src/net/jini/security/policy/ConcurrentPolicyFileTest.java
river/jtsk/skunk/peterConcurrentPolicy/test/src/org/apache/river/api/security/PermissionGrantTest.java
river/jtsk/skunk/peterConcurrentPolicy/test/src/org/apache/river/impl/security/dos/IsolateTest.java
river/jtsk/skunk/peterConcurrentPolicy/test/src/org/apache/river/impl/security/policy/util/PolicyEntryTest.java
river/jtsk/skunk/peterConcurrentPolicy/test/src/org/apache/river/impl/security/policy/util/PolicyUtilsTest.java
Modified: river/jtsk/skunk/peterConcurrentPolicy/src/com/sun/jini/collection/WeakKeyReference.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/com/sun/jini/collection/WeakKeyReference.java?rev=1173631&r1=1173630&r2=1173631&view=diff
==============================================================================
--- river/jtsk/skunk/peterConcurrentPolicy/src/com/sun/jini/collection/WeakKeyReference.java (original)
+++ river/jtsk/skunk/peterConcurrentPolicy/src/com/sun/jini/collection/WeakKeyReference.java Wed Sep 21 13:35:29 2011
@@ -79,12 +79,8 @@ public class WeakKeyReference extends We
* </ul>
*/
public boolean equals(Object other) {
- if (other == null)
- return false;
- if (this == other)
- return true;
- if (!(other instanceof WeakKeyReference))
- return false;
+ if (this == other) return true;
+ if (!(other instanceof WeakKeyReference)) return false;
Object thisRef = get();
Object otherRef = ((WeakKeyReference) other).get();
if (thisRef == null || otherRef == null) // if null it's not *anything*
Modified: river/jtsk/skunk/peterConcurrentPolicy/src/com/sun/jini/thread/ThreadPool.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/com/sun/jini/thread/ThreadPool.java?rev=1173631&r1=1173630&r2=1173631&view=diff
==============================================================================
--- river/jtsk/skunk/peterConcurrentPolicy/src/com/sun/jini/thread/ThreadPool.java (original)
+++ river/jtsk/skunk/peterConcurrentPolicy/src/com/sun/jini/thread/ThreadPool.java Wed Sep 21 13:35:29 2011
@@ -56,7 +56,7 @@ import java.util.logging.Logger;
*
* @author Sun Microsystems, Inc.
**/
-final class ThreadPool implements Executor {
+final class ThreadPool implements Executor, java.util.concurrent.Executor {
/** how long a thread waits in the idle state before passing away */
private static final long idleTimeout = // default 5 minutes
@@ -101,6 +101,10 @@ final class ThreadPool implements Execut
t.start();
}
+ public void execute(Runnable command) {
+ execute(command, "com.sun.jini.thread.ThreadPool");
+ }
+
/**
* Task simply encapsulates a task's Runnable object with its name.
*/
Modified: river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/loader/pref/PreferredClassLoader.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/loader/pref/PreferredClassLoader.java?rev=1173631&r1=1173630&r2=1173631&view=diff
==============================================================================
--- river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/loader/pref/PreferredClassLoader.java (original)
+++ river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/loader/pref/PreferredClassLoader.java Wed Sep 21 13:35:29 2011
@@ -1155,9 +1155,9 @@ public class PreferredClassLoader extend
* Create an AccessControlContext that consists of a single
* protection domain with only the permissions calculated above.
* Comment added 7th May 2010 by Peter Firmstone:
- * This calls the pre java 1.4 constructor which causes the
+ * This did call the pre java 1.4 constructor which causes the
* ProtectionDomain to not consult the Policy, this
- * has the effect of not allowing Dynamic Permission changes to be
+ * had the effect of not allowing Dynamic Permission changes to be
* effected by the Policy. It doesn't affect the existing
* DynamicPolicy implementation as it returns the Permissions
* allowing the ProtectionDomain domain combiner to combine
Modified: river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/Security.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/Security.java?rev=1173631&r1=1173630&r2=1173631&view=diff
==============================================================================
--- river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/Security.java (original)
+++ river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/Security.java Wed Sep 21 13:35:29 2011
@@ -51,7 +51,6 @@ import javax.security.auth.Subject;
import javax.security.auth.SubjectDomainCombiner;
import net.jini.security.policy.DynamicPolicy;
import net.jini.security.policy.SecurityContextSource;
-import org.apache.river.impl.util.ConcurrentWeakIdentityMap;
/**
* Provides methods for executing actions with privileges enabled, for
Modified: river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/policy/ConcurrentPolicyFile.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/policy/ConcurrentPolicyFile.java?rev=1173631&r1=1173630&r2=1173631&view=diff
==============================================================================
--- river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/policy/ConcurrentPolicyFile.java (original)
+++ river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/policy/ConcurrentPolicyFile.java Wed Sep 21 13:35:29 2011
@@ -27,8 +27,10 @@
package net.jini.security.policy;
import java.io.File;
+import java.lang.ref.Reference;
import java.net.URL;
import java.security.AccessController;
+import java.security.AllPermission;
import java.security.CodeSource;
import java.security.Permission;
import java.security.PermissionCollection;
@@ -39,25 +41,23 @@ import java.security.PrivilegedException
import java.security.ProtectionDomain;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
-import java.util.logging.Level;
-import java.util.logging.Logger;
import net.jini.security.ConcurrentPermissions;
import org.apache.river.api.security.PermissionGrant;
import org.apache.river.impl.security.policy.util.DefaultPolicyParser;
import org.apache.river.impl.security.policy.util.PolicyParser;
import org.apache.river.impl.security.policy.util.PolicyUtils;
-import org.apache.river.impl.util.ConcurrentCollections;
-import org.apache.river.impl.util.ConcurrentWeakIdentityMap;
+import org.apache.river.impl.util.Ref;
+import org.apache.river.impl.util.RC;
/**
@@ -200,7 +200,7 @@ public class ConcurrentPolicyFile extend
* Default constructor, equivalent to
* <code>ConcurrentPolicyFile(new DefaultPolicyParser())</code>.
*/
- public ConcurrentPolicyFile() {
+ public ConcurrentPolicyFile() throws PolicyInitializationException {
this(new DefaultPolicyParser());
}
@@ -208,16 +208,37 @@ public class ConcurrentPolicyFile extend
* Extension constructor for plugging-in a custom parser.
* @param dpr
*/
- public ConcurrentPolicyFile(PolicyParser dpr) {
+ protected ConcurrentPolicyFile(PolicyParser dpr) throws PolicyInitializationException {
parser = dpr;
ReadWriteLock rwl = new ReentrantReadWriteLock();
rl = rwl.readLock();
wl = rwl.writeLock();
grants = new HashSet<PermissionGrant>(120);
- cache = new ConcurrentWeakIdentityMap<Object, Collection<Permission>>(120);
- impliesCache =
- new ConcurrentWeakIdentityMap<ProtectionDomain,PermissionCollection>(80);
- refresh();
+ ConcurrentMap<Reference<Object>, Reference<Collection<Permission>>> cacheInternal
+ = new ConcurrentHashMap<Reference<Object>, Reference<Collection<Permission>>>(120);
+ cache = RC.concurrentMap(cacheInternal, Ref.WEAK_IDENTITY, Ref.SOFT);
+ ConcurrentMap<Reference<ProtectionDomain>, Reference<PermissionCollection>> impInternal
+ = new ConcurrentHashMap<Reference<ProtectionDomain>, Reference<PermissionCollection>>(80);
+ impliesCache = RC.concurrentMap(impInternal, Ref.WEAK_IDENTITY, Ref.SOFT);
+ /*
+ * The bootstrap policy makes implies decisions until this constructor
+ * has returned. We don't need to lock.
+ */
+ try {
+ initialize();
+ ensureDependenciesResolved();
+ } catch (SecurityException e){
+ throw new PolicyInitializationException("PolicyInitialization failed", e);
+ }
+ }
+
+ private void ensureDependenciesResolved() {
+ // Investigate bug 4911907, do we need to do anything?
+ // From the work around above, we might not need to do anything.
+ // But these actions prevent the JVM from delaying classloading
+ // of required classes.
+ ProtectionDomain own = this.getClass().getProtectionDomain();
+ implies(own, new AllPermission());
}
/**
@@ -329,19 +350,27 @@ public class ConcurrentPolicyFile extend
*/
@Override
public void refresh() {
- Set<PermissionGrant> fresh = Collections.emptySet();
- wl.lock();
+ if ( !wl.tryLock() ) return; // If another thread has the lock, policy is undergoing an update.
try {
- fresh = AccessController.doPrivileged(
+ initialize();
+ } finally {
+ wl.unlock();
+ }
+ }
+
+ private void initialize(){
+ try {
+ Set<PermissionGrant> fresh = AccessController.doPrivileged(
new PrivilegedExceptionAction<Set<PermissionGrant>>(){
- public Set<PermissionGrant> run() throws Exception {
+ public Set<PermissionGrant> run() throws SecurityException {
Set<PermissionGrant> fresh = new HashSet<PermissionGrant>(120);
Properties system = System.getProperties();
system.setProperty("/", File.separator); //$NON-NLS-1$
URL[] policyLocations = PolicyUtils.getPolicyURLs(system,
JAVA_SECURITY_POLICY,
POLICY_URL_PREFIX);
- for (int i = 0; i < policyLocations.length; i++) {
+ int l = policyLocations.length;
+ for (int i = 0; i < l; i++) {
//TODO debug log
// System.err.println("Parsing policy file: " + policyLocations[i]);
try {
@@ -350,12 +379,14 @@ public class ConcurrentPolicyFile extend
grants.addAll(pc);
fresh.addAll(pc);
} catch (Exception e){
- // TODO log warning
-// System.err.println("Ignoring policy file: "
-// + policyLocations[i] + ". Reason:\n"+ e);
+ // It's best to let a SecurityException bubble up
+ // in case there is a problem with our policy configuration
+ // or implementation.
+ if ( e instanceof SecurityException ) throw (SecurityException) e;
+ e.printStackTrace(System.out);
+ // ignore.
}
}
-// System.err.println(fresh.toString());
return fresh;
}
}
@@ -368,9 +399,7 @@ public class ConcurrentPolicyFile extend
cache.clear(); // Clear the cache.
impliesCache.clear();
}catch (PrivilegedActionException e){
- System.err.println(e);
- } finally {
- wl.unlock();
+ e.printStackTrace(System.err);
}
}
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=1173631&r1=1173630&r2=1173631&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 Wed Sep 21 13:35:29 2011
@@ -1,8 +1,25 @@
-
+/*
+ * 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 net.jini.security.policy;
import java.io.IOException;
+import java.lang.ref.Reference;
import java.rmi.RemoteException;
import org.apache.river.api.security.DelegateSecurityManager;
import java.security.AccessController;
@@ -26,6 +43,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -36,8 +54,9 @@ import org.apache.river.api.security.pol
import org.apache.river.api.security.RevokePermission;
import org.apache.river.api.security.policy.RevokeableDynamicPolicy;
import org.apache.river.impl.security.policy.util.PolicyUtils;
-import org.apache.river.impl.util.ConcurrentCollections;
-import org.apache.river.impl.util.ConcurrentWeakIdentityMap;
+import org.apache.river.impl.util.CollectionsConcurrent;
+import org.apache.river.impl.util.RC;
+import org.apache.river.impl.util.Ref;
/**
* Security policy provider that supports dynamic granting of permissions at
@@ -236,12 +255,27 @@ public class DynamicPolicyProvider exten
throw new PolicyInitializationException(
"unable to construct base policy", e);
}
- dynamicPolicyGrants = ConcurrentCollections.multiReadCollection(
+ dynamicPolicyGrants = CollectionsConcurrent.multiReadCollection(
new ArrayList<PermissionGrant>(120));
remotePolicyGrants = new PermissionGrant[0];
- cache = new ConcurrentWeakIdentityMap<ProtectionDomain, PermissionCollection>(120);
- grantCache = new ConcurrentWeakIdentityMap<PermissionGrant, Permission[]>(60);
+ /*
+ * By using a Softly referenced PermissionCollection, if the
+ * JVM suffers from low memory, the cache will be cleared, even
+ * when the ProtectionDomain is still strongly reachable, this will
+ * slow down the performance of Dynamic Grant based permissions.
+ *
+ * This configuration has been chosen because a ProtectionDomain
+ * may never be garbage collected even after a smart proxy is no longer
+ * referenced, in the case where the client hangs onto objects
+ * recieved from it.
+ */
+ ConcurrentMap<Reference<ProtectionDomain>, Reference<PermissionCollection>> internal
+ = new ConcurrentHashMap<Reference<ProtectionDomain>,Reference<PermissionCollection>>(120);
+ cache = RC.concurrentMap(internal, Ref.WEAK_IDENTITY, Ref.SOFT);
+ ConcurrentMap<Reference<PermissionGrant>, Reference<Permission[]>> gInternal
+ = new ConcurrentHashMap<Reference<PermissionGrant>, Reference<Permission[]>>(60);
+ grantCache = RC.concurrentMap(gInternal, Ref.WEAK, Ref.STRONG);
loggable = logger.isLoggable(Level.FINEST);
grantLock = new Object();
revokePermission = new RevokePermission();
@@ -278,11 +312,15 @@ public class DynamicPolicyProvider exten
*/
public DynamicPolicyProvider(Policy basePolicy){
this.basePolicy = basePolicy;
- dynamicPolicyGrants = ConcurrentCollections.multiReadCollection(
+ dynamicPolicyGrants = CollectionsConcurrent.multiReadCollection(
new ArrayList<PermissionGrant>(120));
remotePolicyGrants = new PermissionGrant[0];
- cache = new ConcurrentWeakIdentityMap<ProtectionDomain, PermissionCollection>(120);
- grantCache = new ConcurrentWeakIdentityMap<PermissionGrant, Permission[]>(60);
+ ConcurrentMap<Reference<ProtectionDomain>, Reference<PermissionCollection>> internal
+ = new ConcurrentHashMap<Reference<ProtectionDomain>,Reference<PermissionCollection>>(120);
+ cache = RC.concurrentMap(internal, Ref.WEAK_IDENTITY, Ref.SOFT);
+ ConcurrentMap<Reference<PermissionGrant>, Reference<Permission[]>> gInternal
+ = new ConcurrentHashMap<Reference<PermissionGrant>, Reference<Permission[]>>(60);
+ grantCache = RC.concurrentMap(gInternal, Ref.WEAK, Ref.STRONG);
loggable = logger.isLoggable(Level.FINEST);
grantLock = new Object();
revokePermission = new RevokePermission();
Modified: river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/delegates/DelegatePermission.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/delegates/DelegatePermission.java?rev=1173631&r1=1173630&r2=1173631&view=diff
==============================================================================
--- river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/delegates/DelegatePermission.java (original)
+++ river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/delegates/DelegatePermission.java Wed Sep 21 13:35:29 2011
@@ -23,6 +23,7 @@ import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
+import java.lang.ref.Reference;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Permissions;
@@ -30,9 +31,11 @@ import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.river.api.security.DelegateSecurityManager;
-import org.apache.river.impl.util.ConcurrentWeakMap;
+import org.apache.river.impl.util.RC;
+import org.apache.river.impl.util.Ref;
/**
* A DelegatePermission represents any another Permission, called the candidate.
@@ -86,7 +89,11 @@ public final class DelegatePermission ex
* consumption.
*/
private static final ConcurrentMap<Permission,DelegatePermission> instances
- = new ConcurrentWeakMap<Permission,DelegatePermission>();
+ = RC.concurrentMap(
+ new ConcurrentHashMap<Reference<Permission>,
+ Reference<DelegatePermission>>(120),
+ Ref.WEAK, Ref.SOFT
+ );
/**
* Factory method to obtain a DelegatePermission
* @param p Permission to be represented.
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=1173631&r1=1173630&r2=1173631&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 Wed Sep 21 13:35:29 2011
@@ -18,6 +18,7 @@
package org.apache.river.api.security;
+import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.security.AccessControlContext;
import java.security.AccessController;
@@ -31,11 +32,12 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.river.api.delegates.DelegatePermission;
-import org.apache.river.impl.util.ConcurrentCollections;
-import org.apache.river.impl.util.ConcurrentWeakIdentityMap;
-import org.apache.river.impl.util.ConcurrentWeakMap;
+import org.apache.river.impl.util.CollectionsConcurrent;
+import org.apache.river.impl.util.RC;
+import org.apache.river.impl.util.Ref;
/**
* DelegateCombinerSecurityManager, is intended to be a high performance
@@ -57,8 +59,15 @@ extends SecurityManager implements Deleg
public DelegateCombinerSecurityManager(){
super();
dc = new DelegateDomainCombiner();
- contextCache = new ConcurrentWeakMap<AccessControlContext, AccessControlContext>(100);
- checked = new ConcurrentWeakMap<AccessControlContext, Set<Permission>>(100);
+ ConcurrentMap<Reference<AccessControlContext>,
+ Reference<AccessControlContext>> internal =
+ new ConcurrentHashMap<Reference<AccessControlContext>,
+ Reference<AccessControlContext>>(100);
+ contextCache = RC.concurrentMap(internal, Ref.SOFT, Ref.STRONG);
+ ConcurrentMap<Reference<AccessControlContext>, Reference<Set<Permission>>> refmap
+ = new ConcurrentHashMap<Reference<AccessControlContext>,
+ Reference<Set<Permission>>>(100);
+ checked = RC.concurrentMap(refmap, Ref.SOFT, Ref.STRONG);
g = new RevokePermission();
action = new Action();
}
@@ -71,7 +80,8 @@ extends SecurityManager implements Deleg
// Checks if Permission has already been checked for this context.
Set<Permission> checkedPerms = checked.get(executionContext);
if (checkedPerms == null){
- checkedPerms = ConcurrentCollections.multiReadSet(new HashSet<Permission>(96));
+ Set<Reference<Permission>> internal = new HashSet<Reference<Permission>>(96);
+ checkedPerms = CollectionsConcurrent.multiReadSet(RC.set(internal, Ref.SOFT));
Set<Permission> existed = checked.putIfAbsent(executionContext, checkedPerms);
if (existed != null) checkedPerms = existed;
}
@@ -157,7 +167,9 @@ extends SecurityManager implements Deleg
// create any more than abolutely necessary.
private final ConcurrentMap<ProtectionDomain,DelegateProtectionDomain> cache;
private DelegateDomainCombiner (){
- cache = new ConcurrentWeakIdentityMap<ProtectionDomain,DelegateProtectionDomain>(120);
+ ConcurrentMap<Reference<ProtectionDomain>,Reference<DelegateProtectionDomain>> internal =
+ new ConcurrentHashMap<Reference<ProtectionDomain>,Reference<DelegateProtectionDomain>>(120);
+ cache = RC.concurrentMap(internal, Ref.WEAK_IDENTITY, Ref.STRONG);
}
public ProtectionDomain[] combine(ProtectionDomain[] currentDomains, ProtectionDomain[] assignedDomains) {
Modified: river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/DelegateReflectionSecurityManager.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/DelegateReflectionSecurityManager.java?rev=1173631&r1=1173630&r2=1173631&view=diff
==============================================================================
--- river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/DelegateReflectionSecurityManager.java (original)
+++ river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/DelegateReflectionSecurityManager.java Wed Sep 21 13:35:29 2011
@@ -18,6 +18,7 @@
package org.apache.river.api.security;
+import java.lang.ref.Reference;
import org.apache.river.api.delegates.DelegatePermission;
import java.lang.reflect.Field;
import java.security.AccessControlContext;
@@ -34,9 +35,9 @@ import java.util.concurrent.ConcurrentHa
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
import java.util.logging.Logger;
-import org.apache.river.impl.util.ConcurrentCollections;
-import org.apache.river.impl.util.ConcurrentSoftMap;
-import org.apache.river.impl.util.ConcurrentWeakMap;
+import org.apache.river.impl.util.CollectionsConcurrent;
+import org.apache.river.impl.util.RC;
+import org.apache.river.impl.util.Ref;
/**
* The DelegateReflectionSecurityManager provides cached permission check results and
@@ -78,11 +79,15 @@ public class DelegateReflectionSecurityM
* contains.
*/
+ @SuppressWarnings("unchecked")
public DelegateReflectionSecurityManager(){
/* This checks adequate permission is held */
super();
- /* Previous checks */
- checks = new ConcurrentSoftMap<AccessControlContext,ExecutionContextChecks>(40);
+ /* Previous checks - softly referenced */
+ ConcurrentMap<Reference<AccessControlContext>,Reference<ExecutionContextChecks>> internal
+ = new ConcurrentHashMap<Reference<AccessControlContext>,Reference<ExecutionContextChecks>>(40);
+// ConcurrentMap internal = new ConcurrentHashMap(40);
+ checks = RC.concurrentMap(internal, Ref.SOFT, Ref.STRONG);
/* This lock guards revocation, although if Permission has already
* been removed from the policy then this lock isn't really necessary
*/
@@ -220,7 +225,7 @@ public class DelegateReflectionSecurityM
}
// If we get to here then we have permission.
if (perms == null ){
- perms = ConcurrentCollections.multiReadSet(new HashSet<Permission>());
+ perms = CollectionsConcurrent.multiReadSet(new HashSet<Permission>());
Set<Permission> existed = passed.putIfAbsent(perm.getClass(), perms);
if ( existed != null ){
perms = existed;
Modified: river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/dos/IsolatedExecutor.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/dos/IsolatedExecutor.java?rev=1173631&r1=1173630&r2=1173631&view=diff
==============================================================================
--- river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/dos/IsolatedExecutor.java (original)
+++ river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/dos/IsolatedExecutor.java Wed Sep 21 13:35:29 2011
@@ -18,33 +18,92 @@
package org.apache.river.impl.security.dos;
+import java.lang.ref.Reference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.ThreadPoolExecutor.AbortPolicy;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import org.apache.river.impl.util.Ref;
+import org.apache.river.impl.util.RC;
/**
* Performs Callable tasks in an isolated thread, which is terminated
- * if any Errors are experienced. This may take some time to terminate the thread
- * as it is not forcibly stopped. The thread priority is minimal and a daemon
- * thread so it would take a number of unterminated threads to cause problems.
+ * if any Errors occur. The daemon thread priority is minimal.
*
* The caller can give up on the execution of the task by setting a timeout.
*
- * @param T
+ * @param <T>
* @author peter
*/
-public class IsolatedExecutor<T> {
- private volatile ExecutorService isolateExecutor;
- private volatile Throwable thrown;
+public class IsolatedExecutor<T> implements ExecutorService {
+ /*
+ * We could optionally make this multithreaded, however once an Error
+ * occurs we'd still need to shut down.
+ */
+ private Lock exRl;
+ private Lock exWl;
+ private ExecutorService isolateExecutor;
+ private volatile State state;
+ private volatile byte [] free;
+ private final BlockingQueue queue;
+ private final RejectedExecutionHandler policy;
+ private final ThreadFactory factory;
+ private final ExecutorService nullExec;
+ private volatile List<Runnable> failedTasks;
+
public IsolatedExecutor()
{
- thrown = null;
- isolateExecutor = Executors.newSingleThreadExecutor(new Factory(this));
+ /* This Executor is single threaded, but that thread is replaced
+ * if idle for extended periods.
+ * SynchronousQueue has zero capacity, so it cannot create memory problems.
+ */
+ queue = new SynchronousQueue<Runnable>();
+ ReadWriteLock rwl = new ReentrantReadWriteLock();
+ exRl = rwl.readLock();
+ exWl = rwl.writeLock();
+ state = State.RUNNING;
+ policy = new AbortPolicy();
+ factory = new Factory();
+ failedTasks = RC.list(new ArrayList<Reference<Runnable>>(),Ref.SOFT);
+ nullExec = new NullExecutor(); // Can't create one lazily if memory low.
+ isolateExecutor = new Executor(0, 1,
+ 60L, TimeUnit.SECONDS,
+ queue,
+ factory,
+ policy);
+ }
+
+ private ExecutorService getExecutor(){
+ exRl.lock();
+ try {
+ if (getIsolateExecutor() != null) return getIsolateExecutor();
+ } finally { exRl.unlock(); }
+ exWl.lock();
+ try {
+ if (getIsolateExecutor() != null) return getIsolateExecutor();
+ setIsolateExecutor(new Executor(0, 1,
+ 60L, TimeUnit.SECONDS,
+ queue,
+ factory,
+ policy));
+ return getIsolateExecutor();
+ } finally { exWl.unlock(); }
}
/**
@@ -61,54 +120,225 @@ public class IsolatedExecutor<T> {
* @throws java.util.concurrent.ExecutionException
*/
public T process(Callable<T> task, long timeout, TimeUnit timeUnit) throws
- IsolationException, ExecutionException, InterruptedException,
- TimeoutException {
- if ( thrown != null){
- throw new IsolationException ("Isolate has" +
- "experienced an error during processing and " +
- "cannot perform further " +
- "tasks", thrown);
- }
- return isolateExecutor.submit(task).get(timeout, timeUnit);
- }
-
- private void terminate(Throwable e){
- thrown = e;
- isolateExecutor.shutdownNow();
- }
-
- private static class Factory implements ThreadFactory{
- private static final ThreadGroup tg = new ThreadGroup("Isolated");
- {
- tg.setDaemon(true);
- tg.setMaxPriority(4);
+ ExecutionException, InterruptedException, TimeoutException {
+ Future<T> result = getIsolateExecutor().submit(task);
+ return result.get(timeout, timeUnit);
+ }
+
+ public void shutdown(){
+ state = State.SHUTDOWN;
+ getIsolateExecutor().shutdown();
+ }
+
+ public List<Runnable> shutdownNow(){
+ state = State.SHUTDOWN;
+ return getIsolateExecutor().shutdownNow();
+ }
+
+ public boolean isShutdown(){
+ return getIsolateExecutor().isShutdown();
}
- private final IsolatedExecutor isolate;
- Factory(IsolatedExecutor uri){
- isolate = uri;
+
+ public boolean isTerminated() {
+ return getIsolateExecutor().isTerminated();
}
+ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+ return getIsolateExecutor().awaitTermination(timeout, unit);
+ }
+
+ public <T> Future<T> submit(Callable<T> task) {
+ return getIsolateExecutor().submit(task);
+ }
+
+ public <T> Future<T> submit(Runnable task, T result) {
+ return getIsolateExecutor().submit(task, result);
+ }
+
+ public Future<?> submit(Runnable task) {
+ return getIsolateExecutor().submit(task);
+ }
+
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
+ return getIsolateExecutor().invokeAll(tasks);
+ }
+
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException {
+ return getIsolateExecutor().invokeAll(tasks, timeout, unit);
+ }
+
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
+ return getIsolateExecutor().invokeAny(tasks);
+ }
+
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+ return getIsolateExecutor().invokeAny(tasks, timeout, unit);
+ }
+
+ public void execute(Runnable command) {
+ getIsolateExecutor().execute(command);
+ }
+
+ /**
+ * @return the isolateExecutor
+ */
+ public ExecutorService getIsolateExecutor() {
+ return isolateExecutor;
+ }
+
+ /**
+ * @param isolateExecutor the isolateExecutor to set
+ */
+ public void setIsolateExecutor(ExecutorService isolateExecutor) {
+ this.isolateExecutor = isolateExecutor;
+ }
+
+ private class Factory implements ThreadFactory{
+ ThreadGroup group;
+ Factory(){
+ group = new ThreadGroup("Isolated");
+ group.setDaemon(true);
+ group.setMaxPriority(Thread.MIN_PRIORITY);
+ }
+ /*
+ * Because we're only single threaded, if the existing
+ * Thread exits, the ThreadGroup is destroyed.
+ */
public Thread newThread(Runnable r) {
- Thread t = new Thread(tg, r);
- t.setUncaughtExceptionHandler(new ExceptionHandler(isolate));
+ // Try to limit the stack size of created Threads; hint to jvm.
+ Thread t = new Thread(group, r, "Isolated", 131072L);
+ t.setUncaughtExceptionHandler(new ExceptionHandler());
+ free = new byte[1024]; // assign some memory
+ free[0] = 1; // ensure it gets allocated by jit.
return t;
}
}
- private static class ExceptionHandler implements Thread.UncaughtExceptionHandler{
- private final IsolatedExecutor isolate;
+ private class ExceptionHandler implements Thread.UncaughtExceptionHandler{
- ExceptionHandler(IsolatedExecutor uri){
- isolate = uri;
+ ExceptionHandler(){
}
public void uncaughtException(Thread t, Throwable e) {
- // For all other Exceptions we let the ExecutorService handle it.
- if ( e instanceof Error){
- // Do we want to take different actions based on the error?
- isolate.terminate(e);
+ // This is only useful for logging, the jvm ignores any exceptions
+ // thrown.
+ System.out.println("Thread ExceptionHandler called for Isolate: \n");
+ System.out.println(t);
+ System.out.println(Thread.currentThread());
}
}
+ private enum State {
+ SHUTDOWN, TERMINATED, RUNNING
+ }
+
+ private class NullExecutor implements ExecutorService {
+
+ public void shutdown() {
+ // Do nothing
+}
+
+ public List<Runnable> shutdownNow() {
+ return failedTasks;
+ }
+
+ public boolean isShutdown() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public boolean isTerminated() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public <T> Future<T> submit(Callable<T> task) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public <T> Future<T> submit(Runnable task, T result) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public Future<?> submit(Runnable task) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public void execute(Runnable command) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ }
+
+ private class Executor extends ThreadPoolExecutor{
+ Executor(int corePoolSize,
+ int maximumPoolSize,
+ long keepAliveTime,
+ TimeUnit unit,
+ BlockingQueue<Runnable> workQueue,
+ ThreadFactory threadFactory,
+ RejectedExecutionHandler handler){
+ super( corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
+ threadFactory, handler);
+ }
+
+ @Override
+ protected void afterExecute(Runnable r, Throwable t) {
+ super.afterExecute(r, t);
+ if (t == null && r instanceof Future<?>) {
+ try {
+ ((Future<?>) r).get();
+ } catch (CancellationException ce) {
+ t = ce;
+ } catch (ExecutionException ee) {
+ t = ee.getCause();
+ } catch (InterruptedException ie) {
+ // Don't need to interrupt thread, the Executor will do it.
+ shutdownNow();// Ensure the interrupt isn't cleared.
+ } finally {
+ //TODO: Implement a shutdown hook for the jvm for other Error's.
+ /*
+ * Even though ThreadPoolExecutor only catches a RuntimeException,
+ * a FutureTask catches a Throwable, so we can get an Error
+ * cause, wrapped in an ExecutionException.
+ */
+ if ( t instanceof OutOfMemoryError || t instanceof StackOverflowError ){
+ /* Do we want to take different actions based on the error?
+ * OutOfMemoryError doesn't mean the jvm is completely devoid
+ * of memory, it indicates that there wasn't enough
+ * memory to create the last object.
+ */
+ free = null; // Free some memory to allow recovery.
+ System.gc();
+ shutdownNow(); // Interrupt all threads in Executor.
+ /* It might be tempting to throw ThreadDeath, it's not required,
+ * instead we just let the thread stack overflow, or run until
+ * no more objects can be created in the jvm. The low
+ * thread priority ensure that performance isn't impacted
+ * by endless loops, however large memory consumption
+ * will impact performance.
+ */
+ }
+ }
+ }
+ }
}
}
Modified: river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/policy/util/DefaultPolicyParser.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/policy/util/DefaultPolicyParser.java?rev=1173631&r1=1173630&r2=1173631&view=diff
==============================================================================
--- river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/policy/util/DefaultPolicyParser.java (original)
+++ river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/policy/util/DefaultPolicyParser.java Wed Sep 21 13:35:29 2011
@@ -141,7 +141,8 @@ public class DefaultPolicyParser impleme
}
}
catch (Exception e) {
- // TODO: log warning
+ if ( e instanceof SecurityException ) throw (SecurityException) e;
+ e.printStackTrace(System.out);
}
}
@@ -183,23 +184,26 @@ public class DefaultPolicyParser impleme
*/
protected PermissionGrant resolveGrant(DefaultPolicyScanner.GrantEntry ge,
KeyStore ks, Properties system, boolean resolve) throws Exception {
-
+ /*
+ * Do we return multiple grants or do we allow a codebase array
+ * in a permission grant?
+ */
URL codebase = null;
Certificate[] signers = null;
Set<Principal>principals = new HashSet<Principal>();
Set<Permission>permissions = new HashSet<Permission>();
- if (ge.codebase != null) {
- codebase = new URL(resolve ? PolicyUtils.expandURL(ge.codebase,
- system) : ge.codebase);
+ if (ge.getCodebase(null) != null) {
+ codebase = new URL(resolve ? PolicyUtils.expandURL(ge.getCodebase(system),
+ system) : ge.getCodebase(null));
}
- if (ge.signers != null) {
+ if (ge.getSigners() != null) {
if (resolve) {
- ge.signers = PolicyUtils.expand(ge.signers, system);
+ ge.setSigners(PolicyUtils.expand(ge.getSigners(), system));
}
- signers = resolveSigners(ks, ge.signers);
+ signers = resolveSigners(ks, ge.getSigners());
}
- if (ge.principals != null) {
- for (Iterator<PrincipalEntry> iter = ge.principals.iterator(); iter.hasNext();) {
+ if (ge.getPrincipals(null) != null) {
+ for (Iterator<PrincipalEntry> iter = ge.getPrincipals(system).iterator(); iter.hasNext();) {
DefaultPolicyScanner.PrincipalEntry pe = iter
.next();
if (resolve) {
@@ -212,8 +216,8 @@ public class DefaultPolicyParser impleme
}
}
}
- if (ge.permissions != null) {
- for (Iterator<PermissionEntry> iter = ge.permissions.iterator(); iter.hasNext();) {
+ if (ge.getPermissions() != null) {
+ for (Iterator<PermissionEntry> iter = ge.getPermissions().iterator(); iter.hasNext();) {
DefaultPolicyScanner.PermissionEntry pe = iter
.next();
try {
@@ -221,6 +225,7 @@ public class DefaultPolicyParser impleme
resolve));
}
catch (Exception e) {
+ if ( e instanceof SecurityException ) throw (SecurityException) e;
// TODO: log warning
}
}
@@ -328,9 +333,9 @@ public class DefaultPolicyParser impleme
if ("self".equals(protocol)) { //$NON-NLS-1$
//need expanding to list of principals in grant clause
- if (ge.principals != null && ge.principals.size() != 0) {
+ if (ge.getPrincipals(null) != null && ge.getPrincipals(null).size() != 0) {
StringBuilder sb = new StringBuilder();
- for (Iterator<PrincipalEntry> iter = ge.principals.iterator(); iter
+ for (Iterator<PrincipalEntry> iter = ge.getPrincipals(null).iterator(); iter
.hasNext();) {
DefaultPolicyScanner.PrincipalEntry pr = iter
.next();
@@ -341,6 +346,7 @@ public class DefaultPolicyParser impleme
pr.name)));
}
catch (Exception e) {
+ if ( e instanceof SecurityException ) throw (SecurityException) e;
throw new PolicyUtils.ExpansionFailedException(
Messages.getString("security.143", pr.name), e); //$NON-NLS-1$
}
@@ -360,6 +366,7 @@ public class DefaultPolicyParser impleme
return pc2str(getPrincipalByAlias(ks, data));
}
catch (Exception e) {
+ if ( e instanceof SecurityException ) throw (SecurityException) e;
throw new PolicyUtils.ExpansionFailedException(
Messages.getString("security.143", data), e); //$NON-NLS-1$
}
@@ -477,6 +484,8 @@ public class DefaultPolicyParser impleme
return ks;
}
catch (Exception e) {
+ if ( e instanceof SecurityException ) throw (SecurityException) e;
+ e.printStackTrace(System.err);
// TODO: log warning
}
}
Modified: river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/policy/util/DefaultPolicyScanner.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/policy/util/DefaultPolicyScanner.java?rev=1173631&r1=1173630&r2=1173631&view=diff
==============================================================================
--- river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/policy/util/DefaultPolicyScanner.java (original)
+++ river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/policy/util/DefaultPolicyScanner.java Wed Sep 21 13:35:29 2011
@@ -25,9 +25,21 @@ package org.apache.river.impl.security.p
import java.io.IOException;
import java.io.Reader;
import java.io.StreamTokenizer;
+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.Properties;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.apache.river.impl.security.policy.util.PolicyUtils.ExpansionFailedException;
/**
@@ -209,13 +221,13 @@ public class DefaultPolicyScanner {
case StreamTokenizer.TT_WORD:
if (Util.equalsIgnoreCase("signedby", st.sval)) { //$NON-NLS-1$
if (st.nextToken() == '"') {
- ge.signers = st.sval;
+ ge.setSigners(st.sval);
} else {
handleUnexpectedToken(st, Messages.getString("security.8B")); //$NON-NLS-1$
}
} else if (Util.equalsIgnoreCase("codebase", st.sval)) { //$NON-NLS-1$
if (st.nextToken() == '"') {
- ge.codebase = st.sval;
+ ge.setCodebase(st.sval);
} else {
handleUnexpectedToken(st, Messages.getString("security.8C")); //$NON-NLS-1$
}
@@ -230,7 +242,7 @@ public class DefaultPolicyScanner {
break;
case '{':
- ge.permissions = readPermissionEntries(st);
+ ge.setPermissions(readPermissionEntries(st));
break parsing;
default: // handle token in the main loop
@@ -393,6 +405,229 @@ public class DefaultPolicyScanner {
composeStatus(st)));
}
+ private SortedSet<Segment> segment(String s, Properties p) throws ExpansionFailedException{
+ final String ARRAY_START_MARK = "${{";
+ final String ARRAY_END_MARK = "}}";
+ final String ARRAY_SEPARATOR = ":";
+ final String START_MARK = "${"; //$NON-NLS-1$
+ final String END_MARK = "}"; //$NON-NLS-1$
+ SortedSet<Segment> stringSegments = new TreeSet<Segment>();
+ Segment primary = new Segment(s, null);
+ Collection<Segment> segments
+ = primary.divideAndReplace(ARRAY_START_MARK, ARRAY_END_MARK,
+ ARRAY_SEPARATOR, p);
+ Iterator<Segment> i = segments.iterator();
+ while (i.hasNext()){
+ stringSegments.addAll(i.next().divideAndReplace(
+ START_MARK, END_MARK, null, p));
+ }
+ return stringSegments;
+ }
+
+ protected final Collection<String> expandPolicyProperties(String s, Properties p){
+ Collection<StringBuilder> cache = new ArrayList<StringBuilder>();
+ final String ARRAY_START_MARK = "${{";
+ final String ARRAY_END_MARK = "}}";
+ final String ARRAY_SEPARATOR = ":";
+ final String START_MARK = "${"; //$NON-NLS-1$
+ final String END_MARK = "}"; //$NON-NLS-1$
+ // Check for array's first, for every array, we must clone and create
+ // a new string, with the array substituted. If there are multiple
+ // arrays, then we must clone for each.
+ // It would be more efficient to use a pattern match to avoid
+ // mutiple processing.
+ Collection<Key> keys = getKeys(p, s, ARRAY_START_MARK, ARRAY_END_MARK);
+ Iterator<Key> i = keys.iterator();
+ int uniqueStrings = 1;
+ while (i.hasNext()){
+ Key k = i.next();
+ while (k != null){
+ short valueCount = k.valueCount(ARRAY_SEPARATOR);
+ uniqueStrings = valueCount * uniqueStrings;
+ }
+ }
+ if ( uniqueStrings < 0 ) throw new IllegalStateException("integer overflow");
+ Collection<String> result = new ArrayList<String>(uniqueStrings);
+ // Now we must replace each key with a value such that we have
+ // X unique stings where:
+ // X = key1.valueCount() * key2.valueCount() ... * keyn.valueCount()
+ Key[] keyArray = keys.toArray(new Key[keys.size()]);
+ StringBuilder sb = new StringBuilder(); // Key builder
+ int lastKey = keyArray.length;
+ for ( int u = 0; u < uniqueStrings ; u++ ){
+ if ( lastKey > 1){
+ for ( int k = 0; k < lastKey; k++ ){
+ if (keyArray[k] == null) continue;
+ // Key builder (for building keys) we use for matching.
+ sb.append(ARRAY_START_MARK)
+ .append(keyArray[k].toString())
+ .append(ARRAY_END_MARK);
+ String primaryKey = sb.toString();
+ sb.delete(0, sb.length()); // Clear the builder for next use.
+ Collection<String> values =
+ keyArray[k].expandValues(ARRAY_SEPARATOR);
+ String[] value = values.toArray(new String[values.size()]);
+ int lastValue = value.length;
+ for ( int v = 0; v < lastValue; v++ ){
+ if (value[v] == null) continue;
+ // Now replace all keys in the string with a value.
+ // for the primary key's value, do this until we have
+ // exhausted the other key's values.
+ // Add each new string to the cache.
+ StringBuilder line = new StringBuilder(64 + s.length()); //extra space for values.
+ boolean primaryKeyIsReplaced = false;
+ line.append(s);
+ for ( int kN = 0; kN < lastKey; kN++){
+ if ( kN == k || keyArray[kN] == null ) continue; // ignore self.
+ sb.append(ARRAY_START_MARK)
+ .append(keyArray[kN].toString())
+ .append(ARRAY_END_MARK);
+ String secondaryKey = sb.toString();
+ sb.delete(0, sb.length()); // Clear the builder for next use.
+ // We are now ready to create our unique string.
+ // each key value must be combined with each
+ // value from each other key.
+ Iterator<String> kNvalues = keyArray[kN]
+ .expandValues(ARRAY_SEPARATOR).iterator();
+ while (kNvalues.hasNext()){
+ // In all honesty, we have a problem now if
+ // there is more than one identical key, how
+ // do we ensure we replace only the correct
+ // position in the string?
+ // Ans: Replace them in order of occurance in the
+ // key array.
+ if (k < kN){ // replace k first.
+ // We must create a new line for every
+ // value. the above iterator won't work.
+ } else { // replace kN first.
+
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+
+ protected static Collection<String> expandStringValues(String value, String separator){
+ Collection<String> result = new ArrayList<String>();
+ StringBuilder b = new StringBuilder(value);
+ int sep = b.indexOf(separator);
+ final int start = 0;
+ while (sep > 0){
+ String sub = b.substring(start, sep + 1);
+ b.delete(start, sep +1);
+ sep = b.indexOf(separator);
+ result.add(sub);
+ }
+ return result;
+ }
+
+ protected final Collection<Key> getKeys(Properties p, String keys,
+ String START_MARK, String END_MARK)
+ {
+ Collection<Key> result = new ArrayList<Key>();
+ final int START_OFFSET = START_MARK.length();
+ final int END_OFFSET = END_MARK.length();
+ int start = keys.indexOf(START_MARK);
+ while (start >= 0){
+ int end = keys.indexOf(END_MARK, start);
+ if (end >= 0) {
+ String key = (keys.substring(start + START_OFFSET, end));
+ result.add(new Key(p,key));
+ start = keys.indexOf(START_MARK, end + END_OFFSET);
+ }
+ }
+ return result;
+ }
+
+
+// protected final String getNextKey(String s, String START_MARK,
+// String END_MARK, int from)
+// {
+// final int START_OFFSET = START_MARK.length();
+// int start = s.indexOf(START_MARK, from);
+// if (start >= 0){
+// int end = s.indexOf(END_MARK , start + START_OFFSET);
+// if (end >= 0) {
+// String key = s.substring(start + START_OFFSET, end);
+// return key;
+// }
+// }
+// return "";
+// }
+
+ /*
+ * A string is made up of string segments and key's, if we create an array
+ * which has string segments and keys represented by array elements, we
+ * can use a string builder for every possible combination and simply build
+ * each possible combination. Also by finding the array key's first,
+ * we can then find simpler key's that don't contain arrays in the segment
+ * and replace those immediately.
+ *
+ * The array key's
+ */
+ public final static class Key {
+
+ public static final Key nullKey = new Key();
+ private final String key;
+ private final String value;
+ private Collection<String> values;
+ private String separator;
+
+ public Key(Properties p, String key){
+ this.key = key;
+ String v = p.getProperty(key);
+ value = v == null ? "" : v;
+ values = null;
+ separator = null;
+ }
+
+ private Key(){
+ key = "";
+ value = "";
+ }
+
+ public final String getValue(){
+ return value;
+ }
+
+ public short valueCount(String separator){
+ short result;
+ if (values != null
+ && this.separator != null
+ && this.separator.equals(separator)
+ ) result = (short) values.size();
+ values = expandStringValues(value, separator);
+ result = (short) values.size();
+ if (result < 0) throw new IllegalStateException("short overflow");
+ return result;
+ }
+
+ public Collection<String> expandValues(String separator){
+ if (values != null
+ && this.separator != null
+ && this.separator.equals(separator)
+ ) return values;
+ values = expandStringValues(value, separator);
+ return values;
+ }
+
+ @Override
+ public final String toString(){
+ return key;
+ }
+
+ public final boolean isNull(){
+ if (key.isEmpty()) return true;
+ return false;
+ }
+ }
+
/**
* Compound token representing <i>keystore </i> clause. See policy format
* {@link org.apache.river.imp.security.policy.se.ConcurrentPolicyFile description}for details.
@@ -426,33 +661,111 @@ public class DefaultPolicyScanner {
* The signers part of grant clause. This is a comma-separated list of
* certificate aliases.
*/
- public String signers;
+ private String signers;
/**
* The codebase part of grant clause. This is an URL from which code
- * originates.
+ * originates. Comma separate list allowed?
+ */
+ private String codebase;
+
+ /**
+ * If there is an array string of codebases, use this field instead.
*/
- public String codebase;
+ private Collection<String> codebases;
/**
* Collection of PrincipalEntries of grant clause.
*/
- public Collection<PrincipalEntry> principals;
+ private Collection<PrincipalEntry> principals;
/**
* Collection of PermissionEntries of grant clause.
*/
- public Collection<PermissionEntry> permissions;
+ private Collection<PermissionEntry> permissions;
/**
* Adds specified element to the <code>principals</code> collection.
* If collection does not exist yet, creates a new one.
*/
public void addPrincipal(PrincipalEntry pe) {
- if (principals == null) {
- principals = new HashSet<PrincipalEntry>();
+ if (getPrincipals(null) == null) {
+ setPrincipals(new HashSet<PrincipalEntry>());
}
- principals.add(pe);
+ getPrincipals(null).add(pe);
+ }
+
+ /**
+ * @return the signers
+ */
+ public String getSigners() {
+ return signers;
+ }
+
+ /**
+ * @param signers the signers to set
+ */
+ public void setSigners(String signers) {
+ this.signers = signers;
+ }
+
+ /**
+ * @return the codebase
+ */
+ public String getCodebase(Properties system) {
+ if (system == null) return codebase;
+ try {
+ return PolicyUtils.expand(codebase, system);
+ } catch (ExpansionFailedException ex) {
+ Logger.getLogger(DefaultPolicyScanner.class.getName()).log(Level.SEVERE, null, ex);
+ return codebase;
+ }
+ }
+
+ /**
+ * Set the the codebase string.
+ * @param codebase the codebase to set
+ */
+ public void setCodebase(String codebase) {
+ this.codebase = codebase;
+ }
+
+ /**
+ * Check this first? Or just get this and iterate?
+ * @return the codebases
+ */
+ public Collection<String> getCodebases(Properties system) {
+
+
+ return codebases;
+ }
+
+ /**
+ * @return the principals
+ */
+ public Collection<PrincipalEntry> getPrincipals(Properties system) {
+ return principals;
+ }
+
+ /**
+ * @param principals the principals to set
+ */
+ public void setPrincipals(Collection<PrincipalEntry> principals) {
+ this.principals = principals;
+ }
+
+ /**
+ * @return the permissions
+ */
+ public Collection<PermissionEntry> getPermissions() {
+ return permissions;
+ }
+
+ /**
+ * @param permissions the permissions to set
+ */
+ public void setPermissions(Collection<PermissionEntry> permissions) {
+ this.permissions = permissions;
}
}
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=1173631&r1=1173630&r2=1173631&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 Wed Sep 21 13:35:29 2011
@@ -242,7 +242,7 @@ public class Messages {
// Attempt to load the messages.
try {
bundle = setLocale(Locale.getDefault(),
- "org.apache.harmony.security.internal.nls.messages"); //$NON-NLS-1$
+ "org.apache.river.security.policy.util.messages"); //$NON-NLS-1$
} catch (Throwable e) {
e.printStackTrace();
}
Modified: river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/policy/util/PolicyUtils.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/policy/util/PolicyUtils.java?rev=1173631&r1=1173630&r2=1173631&view=diff
==============================================================================
--- river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/policy/util/PolicyUtils.java (original)
+++ river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/security/policy/util/PolicyUtils.java Wed Sep 21 13:35:29 2011
@@ -24,8 +24,10 @@
package org.apache.river.impl.security.policy.util;
import java.io.File;
+import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
@@ -74,7 +76,7 @@ public class PolicyUtils {
/**
* Returns InputStream from the target URL.
*/
- public InputStream run() throws Exception {
+ public InputStream run() throws IOException {
return location.openStream();
}
}
@@ -144,18 +146,28 @@ public class PolicyUtils {
*/
public static String expand(String str, Properties properties)
throws ExpansionFailedException {
+ final String ARRAY_START_MARK = "${{";
+ final String ARRAY_END_MARK = "}}";
final String START_MARK = "${"; //$NON-NLS-1$
final String END_MARK = "}"; //$NON-NLS-1$
- final int START_OFFSET = START_MARK.length();
- final int END_OFFSET = END_MARK.length();
+ if ( str.indexOf(ARRAY_START_MARK) > 0) {
+ return process(str, properties, ARRAY_START_MARK, ARRAY_END_MARK);
+ }
+ return process(str, properties, START_MARK, END_MARK);
+ }
+
+ private static String process(String str, Properties p, String START_MARK,
+ String END_MARK) throws ExpansionFailedException{
StringBuilder result = new StringBuilder(str);
+ final int START_OFFSET = START_MARK.length();
+ final int END_OFFSET = END_MARK.length();
int start = result.indexOf(START_MARK);
while (start >= 0) {
int end = result.indexOf(END_MARK, start);
if (end >= 0) {
String key = result.substring(start + START_OFFSET, end);
- String value = properties.getProperty(key);
+ String value = p.getProperty(key);
if (value != null) {
result.replace(start, end + END_OFFSET, value);
start += value.length();
@@ -202,6 +214,7 @@ public class PolicyUtils {
return codebase.toURI().normalize().toURL();
}
} catch (Exception e) {
+ if ( e instanceof SecurityException ) throw (SecurityException) e;
// Ignore
}
}
@@ -378,8 +391,8 @@ public class PolicyUtils {
if (dynamicURL == null) {
dynamicURL = new URL(location);
}
- }
- catch (Exception e) {
+ } catch (Exception e) {
+ if ( e instanceof SecurityException ) throw (SecurityException) e;
// TODO: log error
System.err.println("Error detecting system policy location: "+e);
}
@@ -402,6 +415,7 @@ public class PolicyUtils {
}
}
catch (Exception e) {
+ if ( e instanceof SecurityException ) throw (SecurityException) e;
// TODO: log error
System.err.println("Error detecting security policy location: "+e);
}
@@ -502,7 +516,10 @@ public class PolicyUtils {
* @throws Exception any exception thrown by Constructor.newInstance()
*/
public static Permission instantiatePermission(Class<?> targetType,
- String targetName, String targetActions) throws Exception {
+ String targetName, String targetActions)
+ throws InstantiationException, IllegalAccessException,
+ IllegalArgumentException, InvocationTargetException
+ {
// let's guess the best order for trying constructors
Class[][] argTypes = null;
Modified: river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/util/ConcurrentCollections.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/util/ConcurrentCollections.java?rev=1173631&r1=1173630&r2=1173631&view=diff
==============================================================================
--- river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/util/ConcurrentCollections.java (original)
+++ river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/util/ConcurrentCollections.java Wed Sep 21 13:35:29 2011
@@ -1,262 +0,0 @@
-/*
- * 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.impl.util;
-
-import java.io.IOException;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.Set;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-/**
- *
- * @author Peter Firmstone.
- */
-public class ConcurrentCollections {
- // Not instantiable
- private ConcurrentCollections() {}
-
- public static <T> Set<T> multiReadSet(Set<T> s) {
- return new MultiReadSet<T>(s);
- }
-
- public static <T> Collection<T> multiReadCollection(Collection<T> c){
- return new MultiReadCollection<T>(c);
- }
-
- static class MultiReadCollection<E> implements Collection<E>, Serializable {
- private static final long serialVersionUID = 1L;
-
- final Collection<E> c; // Backing Collection
- final Object mutex; // Object on which to synchronize
- final ReadWriteLock rwlock;
- final Lock rl;
- final Lock wl;
-
- MultiReadCollection(Collection<E> c) {
- if (c==null) {
- throw new NullPointerException();
- }
- this.c = c;
- mutex = this;
- rwlock = new ReentrantReadWriteLock();
- rl = rwlock.readLock();
- wl = rwlock.writeLock();
- }
-
- Lock getReadLock(){
- return rl;
- }
-
- Lock getWriteLock(){
- return rl;
- }
-
- public int size() {
- rl.lock();
- try {
- return c.size();
- }
- finally {
- rl.unlock();
- }
- }
- public boolean isEmpty() {
- rl.lock();
- try {
- return c.isEmpty();
- }
- finally {
- rl.unlock();
- }
- }
- public boolean contains(Object o) {
- rl.lock();
- try {
- return c.contains(o);
- }
- finally {
- rl.unlock();
- }
- }
- public Object[] toArray() {
- rl.lock();
- try {
- return c.toArray();
- }
- finally {
- rl.unlock();
- }
- }
- public <T> T[] toArray(T[] a) {
- rl.lock();
- try {
- return c.toArray(a);
- }
- finally {
- rl.unlock();
- }
- }
-
- public Iterator<E> iterator() {
- rl.lock();
- try {
- return new CollectionIterator<E>(this);
- }
- finally {
- rl.unlock();
- }
- }
-
- public boolean add(E e) {
- wl.lock();
- try{
- return c.add(e);
- }finally{
- wl.unlock();
- }
- }
- public boolean remove(Object o) {
- wl.lock();
- try{
- return c.remove(o);
- }finally{
- wl.unlock();
- }
- }
-
- public boolean containsAll(Collection<?> coll) {
- rl.lock();
- try{
- return c.containsAll(coll);
- }finally{
- rl.unlock();
- }
- }
- public boolean addAll(Collection<? extends E> coll) {
- wl.lock();
- try{
- return c.addAll(coll);
- }finally{
- wl.unlock();
- }
- }
- public boolean removeAll(Collection<?> coll) {
- wl.lock();
- try{
- return c.removeAll(coll);
- }finally{
- wl.unlock();
- }
- }
- public boolean retainAll(Collection<?> coll) {
- wl.lock();
- try{
- return c.retainAll(coll);
- }finally{
- wl.unlock();
- }
- }
- public void clear() {
- wl.lock();
- try{
- c.clear();
- }finally{
- wl.unlock();
- }
- }
- @Override
- public String toString() {
- wl.lock();
- try{
- return c.toString();
- }finally{
- wl.unlock();
- }
- }
- private void writeObject(ObjectOutputStream s) throws IOException {
- rl.lock();
- try{
- s.defaultWriteObject();
- }finally{
- rl.unlock();
- }
- }
- }
-
- static class CollectionIterator<E> implements Iterator<E> {
- final MultiReadCollection col;
- final Iterator<E> iter;
- volatile E element;
- CollectionIterator(MultiReadCollection<E> c){
- col = c;
- Collection<E> copy = new ArrayList<E>(c.size());
- copy.addAll(c);
- iter = copy.iterator();
- }
-
- public boolean hasNext() {
- return iter.hasNext();
- }
-
- public E next() {
- element = iter.next();
- return element;
- }
-
- public void remove() {
- col.remove(element);
- }
-
- }
-
- static class MultiReadSet<E>
- extends MultiReadCollection<E>
- implements Set<E> {
- private static final long serialVersionUID = 1L;
-
- MultiReadSet(Set<E> s) {
- super(s);
- }
-
- @Override
- public boolean equals(Object o) {
- rl.lock();
- try{
- return c.equals(o);
- }finally{
- rl.unlock();
- }
- }
- @Override
- public int hashCode() {
- rl.lock();
- try{
- return c.hashCode();
- }finally{
- rl.unlock();
- }
- }
- }
-}
Modified: river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/util/ConcurrentSoftMap.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/util/ConcurrentSoftMap.java?rev=1173631&r1=1173630&r2=1173631&view=diff
==============================================================================
--- river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/util/ConcurrentSoftMap.java (original)
+++ river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/impl/util/ConcurrentSoftMap.java Wed Sep 21 13:35:29 2011
@@ -1,230 +0,0 @@
-/*
- * 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.impl.util;
-
-
-import java.lang.ref.ReferenceQueue;
-import java.lang.ref.SoftReference;
-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 ConcurrentSoftMap<K, V> implements ConcurrentMap<K, V> {
- // ConcurrentHashMap must be protected from null values;
- 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.
- * @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 SoftReference<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);
- }
-}