river-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From peter_firmst...@apache.org
Subject svn commit: r928394 [2/6] - in /incubator/river/jtsk/trunk: ./ qa/ qa/harness/policy/ qa/jtreg/net/jini/jeri/kerberos/UnitTests/ qa/jtreg/net/jini/jeri/transport/multihomed/ qa/jtreg/unittestlib/ qa/src/com/sun/jini/qa/harness/ qa/src/com/sun/jini/qa/r...
Date Sun, 28 Mar 2010 12:57:05 GMT
Modified: incubator/river/jtsk/trunk/src/net/jini/security/policy/DynamicPolicyProvider.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/net/jini/security/policy/DynamicPolicyProvider.java?rev=928394&r1=928393&r2=928394&view=diff
==============================================================================
--- incubator/river/jtsk/trunk/src/net/jini/security/policy/DynamicPolicyProvider.java (original)
+++ incubator/river/jtsk/trunk/src/net/jini/security/policy/DynamicPolicyProvider.java Sun Mar 28 12:57:03 2010
@@ -1,81 +1,139 @@
 /*
- * 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.
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
  */
 
 package net.jini.security.policy;
 
-import com.sun.jini.collection.WeakIdentityMap;
-import java.lang.ref.ReferenceQueue;
-import java.lang.ref.WeakReference;
+import java.security.AccessControlException;
 import java.security.AccessController;
 import java.security.CodeSource;
 import java.security.Permission;
 import java.security.PermissionCollection;
-import java.security.Permissions;
-import java.security.Principal;
 import java.security.Policy;
+import java.security.Principal;
 import java.security.PrivilegedAction;
 import java.security.ProtectionDomain;
 import java.security.Security;
-import java.util.ArrayList;
-import java.util.Arrays;
-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.NoSuchElementException;
-import java.util.Set;
-import net.jini.security.GrantPermission;
+//import java.util.ServiceLoader;
+import java.util.logging.Logger;
+import java.util.logging.Level;
+import sun.misc.Service;
+import org.apache.river.security.policy.spi.RevokeablePolicy;
+import org.apache.river.security.policy.spi.RevokeableDynamicPolicySpi;
 
 /**
- * Security policy provider that supports dynamic granting of permissions at
- * run-time.  This provider is designed as a wrapper to layer dynamic grant
- * functionality on top of an underlying policy provider.  If the underlying
- * provider does not implement the {@link DynamicPolicy} interface, then its
- * permission mappings are assumed to change only when its {@link
- * Policy#refresh refresh} method is called.  Permissions are granted on the
- * granularity of class loader; granting a permission requires (of the calling
- * context) {@link GrantPermission} for that permission.
- *
- * @author Sun Microsystems, Inc.
+ * This class replaces the existing DynamicPolicyProvider, the existing 
+ * implementation has been modified to partially
+ * implement RevokableDynamiPolicySpi but doing so in a manner compatible with
+ * Java 1.4.  In that implementation it will throw an exception for the revoke
+ * method unless the additional work required has sufficient
+ * demand, in which case it may be implemented for java 1.4 also.
+ * 
+ * I would have liked to use the java 6 ServiceLoader in preference
+ * to the Java 1.4 and cdc (java 1.4) foundation profile 1.1.2 compatible
+ * forbidden sun.misc.Service implementation, since ServiceLoader doesn't exist
+ * in Java 1.4, this might be better as an OSGi service.  A dependency could
+ * be resolved automatically based on the platform.
  * 
- * @since 2.0
+ * @author Peter Firmstone
  */
-public class DynamicPolicyProvider extends Policy implements DynamicPolicy {
-
+public class DynamicPolicyProvider extends Policy implements RevokeablePolicy {
+  
+    //Java 1.4 compatible
+    @SuppressWarnings("unchecked")
+    private synchronized static RevokeableDynamicPolicySpi getDynamicPolicy()
+    throws AccessControlException {
+        RevokeableDynamicPolicySpi rdps;
+        rdps = (RevokeableDynamicPolicySpi) AccessController.doPrivileged( 
+                new PrivilegedAction(){
+            public Object run(){       
+                Iterator sp = Service.providers(RevokeableDynamicPolicySpi.class);
+                    while (sp.hasNext()){
+                        RevokeableDynamicPolicySpi inst = 
+                                (RevokeableDynamicPolicySpi) sp.next();
+                        if (inst != null){
+                            return inst;
+                    }               
+                }
+                return null;
+            }
+        });
+        return rdps;
+    }
+   
+    private static final Logger logger = 
+            Logger.getLogger("net.jini.security.policy");
+    
+    /* If true, always grant permission */
+    @SuppressWarnings("unchecked")
+    private static volatile boolean grantAll =
+	((Boolean) AccessController.doPrivileged(
+	    new PrivilegedAction() {
+		public Object run() {
+		    return Boolean.valueOf(
+			Security.getProperty(
+			    "net.jini.security.policy.grantAllandLog"));
+		}
+	    })).booleanValue();
+            
     private static final String basePolicyClassProperty =
 	"net.jini.security.policy." +
 	"DynamicPolicyProvider.basePolicyClass";
-    private static final String defaultBasePolicyClass =
-	"net.jini.security.policy.PolicyFileProvider";
-    private static final ProtectionDomain sysDomain = (ProtectionDomain)
-	AccessController.doPrivileged(new PrivilegedAction() {
-	    public Object run() { return Object.class.getProtectionDomain(); }
-	});
-
-    private final Policy basePolicy;
-    private final boolean cacheBasePerms;
-    private final WeakIdentityMap domainPerms = new WeakIdentityMap();
-    private final WeakIdentityMap loaderGrants = new WeakIdentityMap();
-    private final Grants globalGrants = new Grants();
-
-    /**
+    
+    private static Policy getBasePolicy() throws PolicyInitializationException {
+        String cname = "net.jini.security.policy.PolicyFileProvider";
+        Policy basePolicy = null;
+        try {
+            String bpc = Security.getProperty(basePolicyClassProperty);
+            if (bpc != null) cname = bpc;
+            basePolicy = (Policy) Class.forName(cname).newInstance();
+        } catch (InstantiationException ex) {
+            if (logger.isLoggable(Level.SEVERE)){
+                logger.log(Level.SEVERE, null, ex);
+            }
+            throw new PolicyInitializationException(
+                    "Unable to create a new instance of: " +
+                    cname, ex);
+        } catch (IllegalAccessException ex) {
+            if (logger.isLoggable(Level.SEVERE)){
+                logger.logp(Level.SEVERE, "DynamicPolicyProviderImpl",
+                        "initialize()", "Unable to create a new instance of: " +
+                        cname, ex);
+            }
+            throw new PolicyInitializationException(
+                    "Unable to create a new instance of: " +
+                    cname, ex);
+        } catch (ClassNotFoundException ex) {
+            if (logger.isLoggable(Level.SEVERE)){
+                logger.log(Level.SEVERE, "Check " + cname + " is accessable" +
+                        "from your classpath", ex);
+            }
+            throw new PolicyInitializationException(
+                    "Unable to create a new instance of: " +
+                    cname, ex);
+        } catch (SecurityException ex) {
+            if (logger.isLoggable(Level.SEVERE)){
+                logger.log(Level.SEVERE,
+                        "You don't have sufficient permissions to create" +
+                        "a new instance of" + cname, ex);
+            }
+            throw new PolicyInitializationException(
+                    "Unable to create a new instance of: " +
+                    cname, ex);
+        } 
+        return basePolicy;
+    }
+   
+    // Try using an enum here for optional loading?  Or just get whatever is available?
+    private RevokeableDynamicPolicySpi instance; //= getDynamicPolicy();
+    
+    
+    // The original implementation wraps a base Policy we still need that
+    // don't return until the instance has been created properly.
+    // This is undesireable for revokeable permissions.
+     /**
      * Creates a new <code>DynamicPolicyProvider</code> instance that wraps a
      * default underlying policy.  The underlying policy is created as follows:
      * if the
@@ -93,7 +151,7 @@ public class DynamicPolicyProvider exten
      * Note that this constructor requires the appropriate
      * <code>"getProperty"</code> {@link java.security.SecurityPermission} to
      * read the
-     * <code>net.jini.security.policy.DynamicPolicyProvider.basePolicyClass</code>
+     * <code>net.jini.security.policy.DynamicPolicyProviderImpl.basePolicyClass</code>
      * security property, and may require <code>"accessClassInPackage.*"</code>
      * {@link RuntimePermission}s, depending on the package of the base policy
      * class.
@@ -102,28 +160,15 @@ public class DynamicPolicyProvider exten
      *          policy
      * @throws  SecurityException if there is a security manager and the
      *          calling context does not have adequate permissions to read the
-     *          <code>net.jini.security.policy.DynamicPolicyProvider.basePolicyClass</code>
+     *          <code>net.jini.security.policy.DynamicPolicyProviderImpl.basePolicyClass</code>
      *          security property, or if the calling context does not have
      *          adequate permissions to access the base policy class
      */
     public DynamicPolicyProvider() throws PolicyInitializationException {
-	String cname = Security.getProperty(basePolicyClassProperty);
-	if (cname == null) {
-	    cname = defaultBasePolicyClass;
-	}
-	try {
-	    basePolicy = (Policy) Class.forName(cname).newInstance();
-	} catch (SecurityException e) {
-	    throw e;
-	} catch (Exception e) {
-	    throw new PolicyInitializationException(
-		"unable to construct base policy", e);
-	}
-	cacheBasePerms = !(basePolicy instanceof DynamicPolicy);
-	ensureDependenciesResolved();
+        this(getBasePolicy());
     }
-
-    /**
+    
+   /**
      * Creates a new <code>DynamicPolicyProvider</code> instance that wraps
      * around the given non-<code>null</code> base policy object.
      *
@@ -133,446 +178,135 @@ public class DynamicPolicyProvider exten
      * 		<code>null</code>
      */
     public DynamicPolicyProvider(Policy basePolicy) {
-	if (basePolicy == null) {
-	    throw new NullPointerException();
-	}
-	this.basePolicy = basePolicy;
-	cacheBasePerms = !(basePolicy instanceof DynamicPolicy);
-	ensureDependenciesResolved();
-    }
-
-    /**
-     * Behaves as specified by {@link Policy#getPermissions(CodeSource)}.
-     */
-    public PermissionCollection getPermissions(CodeSource source) {
-	PermissionCollection pc = basePolicy.getPermissions(source);
-	Permission[] pa = globalGrants.get(null);
-	for (int i = 0; i < pa.length; i++) {
-	    Permission p = pa[i];
-	    if (!pc.implies(p)) {
-		pc.add(p);
-	    }
-	}
-	return pc;
-    }
-
-    /**
-     * Behaves as specified by {@link Policy#getPermissions(ProtectionDomain)}.
-     */
-    public PermissionCollection getPermissions(ProtectionDomain domain) {
-	return getDomainPermissions(domain).getPermissions(domain);
-    }
-
-    /**
-     * Behaves as specified by {@link Policy#implies}.
-     */
-    public boolean implies(ProtectionDomain domain, Permission permission) {
-	return getDomainPermissions(domain).implies(permission, domain);
-    }
-
-    /**
-     * Behaves as specified by {@link Policy#refresh}.
-     */
-    public void refresh() {
-	basePolicy.refresh();
-	if (cacheBasePerms) {
-	    synchronized (domainPerms) {
-		domainPerms.clear();
-	    }
-	}
+            if (basePolicy == null) {
+                throw new NullPointerException();
+            }
+        try {
+            instance = getDynamicPolicy();
+        } catch (AccessControlException ex) {
+            if (logger.isLoggable(Level.CONFIG)) {
+               logger.logp(Level.CONFIG,
+                   DynamicPolicyProvider.class.toString(), 
+                   "DynamicPolicyProvider(Policy basePolicy) constructor",
+                   "If you see this message, it means that you need to grant" +
+                   "the java.lang.RuntimePermission accessClassInPackage.sun.misc" +
+                   "in your Policy file in order to take advantage of " +
+                   "the RevokeableDynamicPolicyProviderSpi", ex);           
+            }
+        }           
+        if (instance == null) {            
+            instance = new DynamicPolicyProviderImpl();            
+        }
+        try {
+            instance.basePolicy(basePolicy);
+            instance.initialize();
+        } catch (PolicyInitializationException ex) {
+            logger.log(Level.SEVERE, "This should never happen, since" +
+                    "basePolicy is not null", ex);
+        }
+        instance.ensureDependenciesResolved();    
     }
 
-    // documentation inherited from DynamicPolicy.grantSupported
     public boolean grantSupported() {
-	return true;
+        return instance.grantSupported();
     }
 
-    // documentation inherited from DynamicPolicy.grant
-    public void grant(Class cl, 
-		      Principal[] principals, 
-		      Permission[] permissions) 
-    {
-	if (cl != null) {
-	    checkDomain(cl);
-	}
-	if (principals != null && principals.length > 0) {
-	    principals = (Principal[]) principals.clone();
-	    checkNullElements(principals);
-	}
-	if (permissions == null || permissions.length == 0) {
-	    return;
-	}
-	permissions = (Permission[]) permissions.clone();
-	checkNullElements(permissions);
-
-	SecurityManager sm = System.getSecurityManager();
-	if (sm != null) {
-	    sm.checkPermission(new GrantPermission(permissions));
-	}
-
-	Grants g = (cl != null) ?
-	    getLoaderGrants(getClassLoader(cl)) : globalGrants;
-	g.add(principals, permissions);
+    public void grant(Class cl, Principal[] principals, Permission[] permissions) {
+        instance.grant(cl, principals, permissions);
     }
 
-    // documentation inherited from DynamicPolicy.getGrants
     public Permission[] getGrants(Class cl, Principal[] principals) {
-	if (cl != null) {
-	    checkDomain(cl);
-	}
-	if (principals != null && principals.length > 0) {
-	    principals = (Principal[]) principals.clone();
-	    checkNullElements(principals);
-	}
-
-	List l = Arrays.asList(globalGrants.get(principals));
-	if (cl != null) {
-	    l = new ArrayList(l);
-	    l.addAll(Arrays.asList(
-			 getLoaderGrants(getClassLoader(cl)).get(principals)));
-	}
-	PermissionCollection pc = new Permissions();
-	for (Iterator i = l.iterator(); i.hasNext(); ) {
-	    Permission p = (Permission) i.next();
-	    if (!pc.implies(p)) {
-		pc.add(p);
-	    }
-	}
-	l = Collections.list(pc.elements());
-	return (Permission[]) l.toArray(new Permission[l.size()]);
+        return instance.getGrants(cl, principals);
     }
 
-    /**
-     * Ensures that any classes depended on by this policy provider are
-     * resolved.  This is to preclude lazy resolution of such classes during
-     * operation of the provider, which can result in deadlock as described by
-     * bug 4911907.
-     */
-    private void ensureDependenciesResolved() {
-	// force class resolution by pre-invoking method called by implies()
-	getDomainPermissions(sysDomain);
-    }
-
-    private DomainPermissions getDomainPermissions(ProtectionDomain pd) {
-	DomainPermissions dp;
-	synchronized (domainPerms) {
-	    dp = (DomainPermissions) domainPerms.get(pd);
-	}
-	if (dp == null) {
-	    dp = new DomainPermissions(pd);
-	    globalGrants.register(dp);
-	    if (pd != null) {
-		getLoaderGrants(pd.getClassLoader()).register(dp);
-	    }
-	    synchronized (domainPerms) {
-		domainPerms.put(pd, dp);
-	    }
-	}
-	return dp;
-    }
-
-    private Grants getLoaderGrants(ClassLoader ldr) {
-	synchronized (loaderGrants) {
-	    Grants g = (Grants) loaderGrants.get(ldr);
-	    if (g == null) {
-		loaderGrants.put(ldr, g = new Grants());
-	    }
-	    return g;
-	}
+    public void revoke(Class cl, Principal[] principals, Permission[] permissions) {
+        instance.revoke(cl, principals, permissions);
     }
-
-    private static ClassLoader getClassLoader(final Class cl) {
-	return (ClassLoader) AccessController.doPrivileged(
-	    new PrivilegedAction() {
-		public Object run() { return cl.getClassLoader(); }
-	    });
-    }
-
-    private static void checkDomain(final Class cl) {
-	ProtectionDomain pd = (ProtectionDomain) AccessController.doPrivileged(
-	    new PrivilegedAction() {
-		public Object run() { return cl.getProtectionDomain(); }
-	    });
-	if (pd != sysDomain && pd.getClassLoader() == null) {
-	    throw new UnsupportedOperationException(
-		"ungrantable protection domain");
-	}
+    
+    @Override
+    public PermissionCollection getPermissions(CodeSource codesource) {
+	return instance.getPermissions(codesource);
     }
-
-    private static void checkNullElements(Object[] array) {
-	for (int i = 0; i < array.length; i++) {
-	    if (array[i] == null) {
-		throw new NullPointerException();
-	    }
-	}
-    }
-
+    
     /**
-     * Class which holds permissions and principals of a ProtectionDomain. The
-     * domainPerms map associates ProtectionDomain instances to instances of
-     * this class.
+     * Return a PermissionCollection object containing the set of
+     * permissions granted to the specified ProtectionDomain.
+     *
+     * <p> Applications are discouraged from calling this method
+     * since this operation may not be supported by all policy implementations.
+     * Applications should rely on the <code>implies</code> method
+     * to perform policy checks.
+     *
+     * <p> The default implementation of this method first retrieves
+     * the permissions returned via <code>getPermissions(CodeSource)</code>
+     * (the CodeSource is taken from the specified ProtectionDomain),
+     * as well as the permissions located inside the specified ProtectionDomain.
+     * All of these permissions are then combined and returned in a new
+     * PermissionCollection object.  If <code>getPermissions(CodeSource)</code>
+     * returns Policy.UNSUPPORTED_EMPTY_COLLECTION, then this method
+     * returns the permissions contained inside the specified ProtectionDomain
+     * in a new PermissionCollection object.
+     *
+     * <p> This method can be overridden if the policy implementation
+     * supports returning a set of permissions granted to a ProtectionDomain.
+     *
+     * @param domain the ProtectionDomain to which the returned
+     *		PermissionCollection has been granted.
+     *
+     * @return a set of permissions granted to the specified ProtectionDomain.
+     *		If this operation is supported, the returned
+     *		set of permissions must be a new mutable instance
+     *		and it must support heterogeneous Permission types.
+     *		If this operation is not supported,
+     *		Policy.UNSUPPORTED_EMPTY_COLLECTION is returned.
+     *
+     * @since 1.4
      */
-    private class DomainPermissions {
-
-	private final Set principals;
-	private final PermissionCollection perms;
-	private final List grants = new ArrayList();
-
-	DomainPermissions(ProtectionDomain pd) {
-	    Principal[] pra;
-	    principals = (pd != null && (pra = pd.getPrincipals()).length > 0)
-		? new HashSet(Arrays.asList(pra)) : Collections.EMPTY_SET;
-	    perms = cacheBasePerms ? basePolicy.getPermissions(pd) : null;
-	}
-
-	Set getPrincipals() {
-	    return principals;
-	}
-
-	synchronized void add(Permission[] pa) {
-	    for (int i = 0; i < pa.length; i++) {
-		Permission p = pa[i];
-		grants.add(p);
-		if (perms != null) {
-		    perms.add(p);
-		}
-	    }
-	}
-
-	synchronized PermissionCollection getPermissions(ProtectionDomain d) {
-	    return getPermissions(true, d);
-	}
-
-	synchronized boolean implies(Permission p, ProtectionDomain domain) {
-	    if (perms != null) {
-		return perms.implies(p);
-	    }
-	    if (basePolicy.implies(domain, p)) {
-		return true;
-	    }
-	    if (grants.isEmpty()) {
-		return false;
-	    }
-	    return getPermissions(false, domain).implies(p);
-	}
-
-	private PermissionCollection getPermissions(boolean compact,
-						    ProtectionDomain domain)
-        {
-	    // base policy permission collection may not be enumerable
-	    assert Thread.holdsLock(this);
-	    PermissionCollection pc = basePolicy.getPermissions(domain);
-	    for (Iterator i = grants.iterator(); i.hasNext(); ) {
-		Permission p = (Permission) i.next();
-		if (!(compact && pc.implies(p))) {
-		    pc.add(p);
-		}
-	    }
-	    return pc;
-	}
+    @Override
+    public PermissionCollection getPermissions(ProtectionDomain domain) {
+        return instance.getPermissions(domain);
     }
-
+    
     /**
-     * Class which tracks dynamic permission grants.
+     * Evaluates the global policy for the permissions granted to
+     * the ProtectionDomain and tests whether the permission is 
+     * granted.
+     *
+     * @param domain the ProtectionDomain to test
+     * @param permission the Permission object to be tested for implication.
+     *
+     * @return true if "permission" is a proper subset of a permission
+     * granted to this ProtectionDomain.
+     *
+     * @see java.security.ProtectionDomain
+     * @since 1.4
      */
-    private static class Grants {
-
-	private final Map principalGrants = new HashMap();
-	private final WeakGroup scope;
-
-	Grants() {
-	    PrincipalGrants pg = new PrincipalGrants();
-	    principalGrants.put(Collections.EMPTY_SET, pg);
-	    scope = pg.scope;
-	}
-
-	synchronized void add(Principal[] pra, Permission[] pa) {
-	    Set prs = (pra != null && pra.length > 0) ?
-		new HashSet(Arrays.asList(pra)) : Collections.EMPTY_SET;
-
-	    PrincipalGrants pg = (PrincipalGrants) principalGrants.get(prs);
-	    if (pg == null) {
-		pg = new PrincipalGrants();
-		for (Iterator i = scope.iterator(); i.hasNext();) {
-		    DomainPermissions dp = (DomainPermissions) i.next();
-		    if (containsAll(dp.getPrincipals(), prs)) {
-			pg.scope.add(dp);
-		    }
-		}
-		principalGrants.put(prs, pg);
-	    }
-	    
-	    ArrayList l = new ArrayList();
-	    for (int i = 0; i < pa.length; i++) {
-		Permission p = pa[i];
-		if (!pg.perms.implies(p)) {
-		    pg.perms.add(p);
-		    l.add(p);
-		}
-	    }
-	    
-	    if (l.size() > 0) {
-		pa = (Permission[]) l.toArray(new Permission[l.size()]);
-		for (Iterator i = pg.scope.iterator(); i.hasNext();) {
-		    ((DomainPermissions) i.next()).add(pa);
-		}
-	    }
-	}
-
-	synchronized Permission[] get(Principal[] pra) {
-	    Set prs = (pra != null && pra.length > 0) ?
-		new HashSet(Arrays.asList(pra)) : Collections.EMPTY_SET;
-	    List l = new ArrayList();
-
-	    for (Iterator i = principalGrants.entrySet().iterator();
-		 i.hasNext(); )
-	    {
-		Map.Entry me = (Map.Entry) i.next();
-		if (containsAll(prs, (Set) me.getKey())) {
-		    PrincipalGrants pg = (PrincipalGrants) me.getValue();
-		    l.addAll(Collections.list(pg.perms.elements()));
-		}
-	    }
-	    return (Permission[]) l.toArray(new Permission[l.size()]);
-	}
-
-	synchronized void register(DomainPermissions dp) {
-	    Set prs = dp.getPrincipals();
-	    for (Iterator i = principalGrants.entrySet().iterator(); 
-		 i.hasNext(); ) 
-	    {
-		Map.Entry me = (Map.Entry) i.next();
-		if (containsAll(prs, (Set) me.getKey())) {
-		    PrincipalGrants pg = (PrincipalGrants) me.getValue();
-		    pg.scope.add(dp);
-		    List l = Collections.list(pg.perms.elements());
-		    dp.add((Permission[]) l.toArray(new Permission[l.size()]));
-		}
-	    }
-	}
-
-	private static boolean containsAll(Set s1, Set s2) {
-	    return (s1.size() >= s2.size()) && s1.containsAll(s2);
-	}
-	
-	private static class PrincipalGrants {
-	    final WeakGroup scope = new WeakGroup();
-	    final PermissionCollection perms = new Permissions();
-	    PrincipalGrants() {}
-	}
+    @Override
+    public boolean implies(ProtectionDomain domain, Permission permission) {
+        if (grantAll == false) {
+            return instance.implies(domain, permission);
+        }
+        boolean result = instance.implies(domain, permission);
+        if (result == false){
+            logger.logp(Level.INFO, "instance.getClass().getName()",
+                    "implies(ProtectionDomain domain, Permission permission",
+                    "domain.toString(), permission.toString() returned false");
+            return true;
+        }
+        return result;
+    }
+    
+    @Override
+    public void refresh() {
+        instance.refresh();
     }
 
-    /**
-     * Grouping of non-null, weakly-referenced objects. The structure is a
-     * doubly linked list. The resulting structure is not thread safe and
-     * must be synchronized externally.
-     */
-    private static class WeakGroup {
-	private final ReferenceQueue rq = new ReferenceQueue();
-	private final Node head;
-	private final Node tail;
-	
-	WeakGroup() {
-	    head = Node.createEmptyList();
-	    tail = head.getNext();
-	}
-	
-	void add(Object obj) {
-	    if (obj == null) {
-		throw new NullPointerException();
-	    }
-	    processQueue();
-	    Node newNode = new Node(obj, rq);
-	    newNode.insertAfter(head);
-	}
-	
-	Iterator iterator() {
-	    processQueue();
-	    return new Iterator() {
-		private Node curNode = head.getNext();
-		private Object nextObj = getNext();
-
-		public Object next() {
-		    if (nextObj == null) {
-			throw new NoSuchElementException();
-		    }
-		    Object obj = nextObj;
-		    nextObj = getNext();
-		    return obj;
-		}
-		
-		public boolean hasNext() {
-		    return nextObj != null;
-		}
-		
-		public void remove() {
-		    throw new UnsupportedOperationException();
-		}
-		
-		private Object getNext() {
-		    while (curNode != tail) {
-			Object obj = curNode.get();
-			if (obj != null) {
-			    curNode = curNode.getNext();
-			    return obj;
-			} else {
-			    curNode.enqueue();
-			    curNode = curNode.getNext();
-			}
-		    }
-		    return null;
-		}
-	    };
-	}
-	
-	private void processQueue() {
-	    Node n;
-	    while ((n = (Node) rq.poll()) != null) {
-		n.remove();
-	    }
-	}
-
-	private static class Node extends WeakReference {
-	    private Node next;
-	    private Node prev;
-	    
-	    static Node createEmptyList() {
-		Node head = new Node(null);
-		Node tail = new Node(null);
-		head.next = tail;
-		tail.prev = head;
-		return head;
-	    }
-	    
-	    // Constructor for initialization of head and tail nodes which
-	    // should never be enqueued. 
-	    private Node(Object obj) {
-		super(obj);
-	    }
+    public boolean revokeSupported() {
+        return instance.revokeSupported();
+    }
 
-	    Node(Object obj, ReferenceQueue rq) {
-		super(obj, rq);
-	    }
-	    
-	    /**
-	     * Inserts this node between <code>pred</code> and its successor
-	     */
-	    void insertAfter(Node pred) {
-		Node succ = pred.next;
-		next = succ;
-		prev = pred;
-		pred.next = this;
-		succ.prev = this;
-	    }
-	    
-	    void remove() {
-		prev.next = next;
-		next.prev = prev;
-	    }
-	    
-	    Node getNext() {
-		return next;
-	    }
-	}
+    public Object parameters() throws UnsupportedOperationException {
+        throw new UnsupportedOperationException("Not supported yet.");
     }
+   
 }

Copied: incubator/river/jtsk/trunk/src/net/jini/security/policy/DynamicPolicyProviderImpl.java (from r921640, incubator/river/jtsk/trunk/src/net/jini/security/policy/DynamicPolicyProvider.java)
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/net/jini/security/policy/DynamicPolicyProviderImpl.java?p2=incubator/river/jtsk/trunk/src/net/jini/security/policy/DynamicPolicyProviderImpl.java&p1=incubator/river/jtsk/trunk/src/net/jini/security/policy/DynamicPolicyProvider.java&r1=921640&r2=928394&rev=928394&view=diff
==============================================================================
--- incubator/river/jtsk/trunk/src/net/jini/security/policy/DynamicPolicyProvider.java (original)
+++ incubator/river/jtsk/trunk/src/net/jini/security/policy/DynamicPolicyProviderImpl.java Sun Mar 28 12:57:03 2010
@@ -41,7 +41,10 @@ import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 import net.jini.security.GrantPermission;
+import org.apache.river.security.policy.spi.RevokeableDynamicPolicySpi;
 
 /**
  * Security policy provider that supports dynamic granting of permissions at
@@ -57,93 +60,71 @@ import net.jini.security.GrantPermission
  * 
  * @since 2.0
  */
-public class DynamicPolicyProvider extends Policy implements DynamicPolicy {
+class DynamicPolicyProviderImpl extends Policy implements RevokeableDynamicPolicySpi {
 
-    private static final String basePolicyClassProperty =
-	"net.jini.security.policy." +
-	"DynamicPolicyProvider.basePolicyClass";
-    private static final String defaultBasePolicyClass =
-	"net.jini.security.policy.PolicyFileProvider";
     private static final ProtectionDomain sysDomain = (ProtectionDomain)
 	AccessController.doPrivileged(new PrivilegedAction() {
 	    public Object run() { return Object.class.getProtectionDomain(); }
 	});
 
-    private final Policy basePolicy;
-    private final boolean cacheBasePerms;
-    private final WeakIdentityMap domainPerms = new WeakIdentityMap();
-    private final WeakIdentityMap loaderGrants = new WeakIdentityMap();
-    private final Grants globalGrants = new Grants();
-
-    /**
-     * Creates a new <code>DynamicPolicyProvider</code> instance that wraps a
-     * default underlying policy.  The underlying policy is created as follows:
-     * if the
-     * <code>net.jini.security.policy.DynamicPolicyProvider.basePolicyClass</code>
-     * security property is set, then its value is interpreted as the class
-     * name of the base (underlying) policy provider; otherwise, a default
-     * class name of
-     * <code>"net.jini.security.policy.PolicyFileProvider"</code>
-     * is used.  The base policy is then instantiated using the no-arg public
-     * constructor of the named class.  If the base policy class is not found,
-     * is not instantiable via a public no-arg constructor, or if invocation of
-     * its constructor fails, then a <code>PolicyInitializationException</code>
-     * is thrown.
-     * <p>
-     * Note that this constructor requires the appropriate
-     * <code>"getProperty"</code> {@link java.security.SecurityPermission} to
-     * read the
-     * <code>net.jini.security.policy.DynamicPolicyProvider.basePolicyClass</code>
-     * security property, and may require <code>"accessClassInPackage.*"</code>
-     * {@link RuntimePermission}s, depending on the package of the base policy
-     * class.
-     *
-     * @throws  PolicyInitializationException if unable to construct the base
-     *          policy
-     * @throws  SecurityException if there is a security manager and the
-     *          calling context does not have adequate permissions to read the
-     *          <code>net.jini.security.policy.DynamicPolicyProvider.basePolicyClass</code>
-     *          security property, or if the calling context does not have
-     *          adequate permissions to access the base policy class
-     */
-    public DynamicPolicyProvider() throws PolicyInitializationException {
-	String cname = Security.getProperty(basePolicyClassProperty);
-	if (cname == null) {
-	    cname = defaultBasePolicyClass;
-	}
-	try {
-	    basePolicy = (Policy) Class.forName(cname).newInstance();
-	} catch (SecurityException e) {
-	    throw e;
-	} catch (Exception e) {
-	    throw new PolicyInitializationException(
-		"unable to construct base policy", e);
-	}
-	cacheBasePerms = !(basePolicy instanceof DynamicPolicy);
-	ensureDependenciesResolved();
-    }
-
-    /**
-     * Creates a new <code>DynamicPolicyProvider</code> instance that wraps
-     * around the given non-<code>null</code> base policy object.
-     *
-     * @param   basePolicy base policy object containing information about
-     *          non-dynamic grants
-     * @throws  NullPointerException if <code>basePolicy</code> is
-     * 		<code>null</code>
-     */
-    public DynamicPolicyProvider(Policy basePolicy) {
-	if (basePolicy == null) {
-	    throw new NullPointerException();
-	}
-	this.basePolicy = basePolicy;
-	cacheBasePerms = !(basePolicy instanceof DynamicPolicy);
-	ensureDependenciesResolved();
+    private volatile Policy basePolicy;
+    private volatile boolean cacheBasePerms;
+    private volatile boolean initialized;
+    // REMIND: do something with WeakIdentityMap and Concurrency, note
+    // this means different implementation methods not a drop in.
+    private final Map domainPerms;
+    private final Map loaderGrants;
+    private final Grants globalGrants;
+    private static final Logger logger = Logger.getLogger(
+            "net.jini.security.policy.DynamicPolicyProviderImpl");
+
+    /**
+     * A new uninitialized instance.
+     */
+    DynamicPolicyProviderImpl() {
+       domainPerms = new WeakIdentityMap();
+       loaderGrants = new WeakIdentityMap();
+       globalGrants = new Grants();
+       basePolicy = null;
+       cacheBasePerms = false;
+       initialized = false;
+    }
+    
+    /**
+     * This method is only called once, on an uninitialized instance
+     * further attempts will fail.
+     * @param basePolicy
+     * @return success
+     */
+    public boolean basePolicy(Policy basePolicy){
+        if (this.basePolicy == null){
+            this.basePolicy = basePolicy;
+            return true;
+        }
+        return false;
+    }
+    
+    /**
+     * This method completes construction of the Implementation, considered
+     * safe since it is called through the Service Provider Interface 
+     * and cannot be accessed otherwise.
+     * @throws net.jini.security.policy.PolicyInitializationException
+     * @throws java.lang.InstantiationException
+     */
+    public void initialize() throws PolicyInitializationException {
+        if (initialized == true) {
+            return;
+        }
+        if (basePolicy == null) throw new PolicyInitializationException("Base Policy hasn't " +
+                "been set cannot initialize", new Exception("Failed to initialize"));      
+        cacheBasePerms = !(basePolicy instanceof DynamicPolicy);  
+        initialized = true;
     }
 
     /**
      * Behaves as specified by {@link Policy#getPermissions(CodeSource)}.
      */
+    @Override
     public PermissionCollection getPermissions(CodeSource source) {
 	PermissionCollection pc = basePolicy.getPermissions(source);
 	Permission[] pa = globalGrants.get(null);
@@ -159,6 +140,7 @@ public class DynamicPolicyProvider exten
     /**
      * Behaves as specified by {@link Policy#getPermissions(ProtectionDomain)}.
      */
+    @Override
     public PermissionCollection getPermissions(ProtectionDomain domain) {
 	return getDomainPermissions(domain).getPermissions(domain);
     }
@@ -248,7 +230,7 @@ public class DynamicPolicyProvider exten
      * operation of the provider, which can result in deadlock as described by
      * bug 4911907.
      */
-    private void ensureDependenciesResolved() {
+    public void ensureDependenciesResolved() {
 	// force class resolution by pre-invoking method called by implies()
 	getDomainPermissions(sysDomain);
     }
@@ -344,6 +326,8 @@ public class DynamicPolicyProvider exten
 	}
 
 	synchronized boolean implies(Permission p, ProtectionDomain domain) {
+//            System.out.println("Permission: " + p.toString() + 
+//                    " ProtectionDomain: " + domain.toString());
 	    if (perms != null) {
 		return perms.implies(p);
 	    }
@@ -575,4 +559,16 @@ public class DynamicPolicyProvider exten
 	    }
 	}
     }
+
+    public void revoke(Class cl, Principal[] principals, Permission[] permissions) {
+        throw new UnsupportedOperationException("Revoke not supported.");
+    }
+
+    public boolean revokeSupported() {
+        return false;
+    }
+
+    public Object parameters() throws UnsupportedOperationException {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
 }

Modified: incubator/river/jtsk/trunk/src/net/jini/security/policy/PolicyFileProvider.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/net/jini/security/policy/PolicyFileProvider.java?rev=928394&r1=928393&r2=928394&view=diff
==============================================================================
--- incubator/river/jtsk/trunk/src/net/jini/security/policy/PolicyFileProvider.java (original)
+++ incubator/river/jtsk/trunk/src/net/jini/security/policy/PolicyFileProvider.java Sun Mar 28 12:57:03 2010
@@ -104,8 +104,14 @@ public class PolicyFileProvider extends 
      */
     public PolicyFileProvider() throws PolicyInitializationException {
 	policyFile = null;
-
-	String cname = Security.getProperty(basePolicyClassProperty);
+        String cname = Security.getProperty(basePolicyClassProperty);
+//        @SuppressWarnings("unchecked")
+//	String cname = (String) AccessController.doPrivileged(
+//                new PrivilegedAction() {
+//            public Object run() {
+//                return System.getProperty(basePolicyClassProperty);
+//            }
+//        });
 	if (cname == null) {
 	    cname = defaultBasePolicyClass;
 	}

Modified: incubator/river/jtsk/trunk/src/net/jini/security/proxytrust/BasicUntrustedObjectSecurityContext.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/net/jini/security/proxytrust/BasicUntrustedObjectSecurityContext.java?rev=928394&r1=928393&r2=928394&view=diff
==============================================================================
--- incubator/river/jtsk/trunk/src/net/jini/security/proxytrust/BasicUntrustedObjectSecurityContext.java (original)
+++ incubator/river/jtsk/trunk/src/net/jini/security/proxytrust/BasicUntrustedObjectSecurityContext.java Sun Mar 28 12:57:03 2010
@@ -62,6 +62,15 @@ public final class BasicUntrustedObjectS
      * neither modified nor retained; subsequent changes to that argument
      * have no effect on the instance created.
      *
+     * If additional permissions are passed in these will be added to a new 
+     * ProtectionDomain and set readonly, additional Permission's may be
+     * granted by a Policy, however any permissions granted at construction
+     * time will always be valid and cannot be revoked.
+     * 
+     * If permissions is null or empty then all BasicUntrustedObjectSecurityContext
+     * instances will share the same ProtectionDomain with all other instances
+     * without additional permissions.
+     *
      * @param permissions additional permissions to allow, or
      * <code>null</code> if no additional permissions should be allowed
      * @throws NullPointerException if any element of the argument is

Added: incubator/river/jtsk/trunk/src/org/apache/river/security/RevokePermission.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/security/RevokePermission.java?rev=928394&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/security/RevokePermission.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/security/RevokePermission.java Sun Mar 28 12:57:03 2010
@@ -0,0 +1,59 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.apache.river.security;
+
+import java.security.Permission;
+
+/**
+ * RevokePermission allows for a permission to be granted at runtime or
+ * revoked.  A Thread invoking this Permission must have the permission that
+ * is to be granted.  A RevokePermission cannot grant itself a permission
+ * it doesn't already have.
+ * 
+ * It should cache all revokes, such that a refresh operation, doesn't add
+ * any revoked permissions.  I'm not sure about grant's though, should they be
+ * refreshed and require re granting if they didn't exist in the configuration
+ * -- seems logical.
+ * 
+ * 
+ * @author peter
+ */
+public class RevokePermission extends Permission {
+    private static final long serialVersionUID = 1L;
+    private final String actions;
+    
+    public RevokePermission(String name){
+        super(name);
+        actions = "";
+    }
+
+    public RevokePermission(String name, String actions){
+        super(name);
+        this.actions = actions;
+    }
+    
+    @Override
+    public boolean implies(Permission permission) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public int hashCode() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public String getActions() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+    
+
+}

Propchange: incubator/river/jtsk/trunk/src/org/apache/river/security/RevokePermission.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/river/jtsk/trunk/src/org/apache/river/security/RevokeablePermissionCollection.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/security/RevokeablePermissionCollection.java?rev=928394&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/security/RevokeablePermissionCollection.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/security/RevokeablePermissionCollection.java Sun Mar 28 12:57:03 2010
@@ -0,0 +1,89 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.apache.river.security;
+
+import java.security.Permission;
+import java.util.Enumeration;
+
+/**
+ *
+ * @author Peter Firmstone
+ * @see PermissionCollection
+ * @see Permission
+ */
+public interface RevokeablePermissionCollection {
+    /**
+     * This method is slow and to be used only when correctness is perferred
+     * over performance.
+     * 
+     * In fact revokeAll() followed by add() is the preferred method.
+     * 
+     * However when the new Permission set depends upon the old and there is
+     * a possibility that the old set may be updated with an add during processing
+     * this method will fail and as such not loose a particular required permission.
+     * 
+     * Partial success may occur in which case some of the permissions will be
+     * revoked and others not. However only a subset of those permissions that 
+     * are requested to be revoked, shall be.  This should not be a problem as
+     * the required permissions will still exist in the set and a simple retry
+     * should suffice if the return was 0.
+     * 
+     * Attempt to revoke a Permission, if an intervening write occurs an
+     * integer value of 0 is returned, the suggested strategy is to try again,
+     * however if several attempts are likely to fail use revokeAll instead.
+     * 
+     * A return value of -1 indicates that the Permission cannot be revoked
+     * from the collection.  In this case use revokeAll.  It may be possible
+     * to correct this condition by utilising a finer grained permission set.
+     * 
+     * A return value of 1 indicates success.
+     * 
+     * @param permissions
+     * @return result, 0 for intervening write, -1 failed not possible, 1 for true
+     */
+    public int revoke(Permission ... permissions);
+
+    /**
+     * Due to the overlapping nature of Permission's, attempts to revoke
+     * a permission may fail, in wich case it is best to remove all
+     * permissions related by class and later add the required Permissions.
+     * This method should always succeed.
+     * 
+     * This method should only revoke Permission's related to an individual
+     * class type.
+     * 
+     * @param permission 
+     */
+    public void revokeAll(Permission permission);
+    
+    /**
+     * @see PermissionCollection.add(Permission permission)
+     * @param permission
+     */
+    public void add(Permission permission);
+    /**
+     * @see PermissionCollection.elements()
+     * @return
+     */
+    public Enumeration<Permission> elements();
+    /**
+     * @see PermissionCollection.implies()
+     * @param permission
+     * @return
+     */
+    public boolean implies(Permission permission);
+    /**
+     * @see PermissionCollection.isReadOnly()
+     * @return true or false
+     */
+    public boolean isReadOnly();
+    /**
+     * @see PermissionCollection.setReadOnly()
+     */
+    public void setReadOnly();
+    @Override
+    public String toString();
+}

Propchange: incubator/river/jtsk/trunk/src/org/apache/river/security/RevokeablePermissionCollection.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentDynamicPolicyProvider.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentDynamicPolicyProvider.java?rev=928394&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentDynamicPolicyProvider.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentDynamicPolicyProvider.java Sun Mar 28 12:57:03 2010
@@ -0,0 +1,440 @@
+/*
+ * 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.security.concurrent;
+
+import net.jini.security.policy.*;
+import org.apache.river.util.concurrent.WeakIdentityMap;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Principal;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.security.Security;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import net.jini.security.GrantPermission;
+import org.apache.river.security.policy.spi.RevokeableDynamicPolicySpi;
+
+/**
+ * Security policy provider that supports dynamic granting of permissions at
+ * run-time.  This provider is designed as a wrapper to layer dynamic grant
+ * functionality on top of an underlying policy provider.  If the underlying
+ * provider does not implement the {@link DynamicPolicy} interface, then its
+ * permission mappings are assumed to change only when its {@link
+ * Policy#refresh refresh} method is called.  Permissions are granted on the
+ * granularity of class loader; granting a permission requires (of the calling
+ * context) {@link GrantPermission} for that permission.
+ *
+ * @author Sun Microsystems, Inc.
+ * 
+ * @since 2.0
+ */
+class ConcurrentDynamicPolicyProvider extends Policy implements RevokeableDynamicPolicySpi {
+
+    private static final String basePolicyClassProperty =
+            "net.jini.security.policy." +
+            "DynamicPolicyProvider.basePolicyClass";
+    private static final String defaultBasePolicyClass =
+            "org.apache.river.security.concurrent.ConcurrentPolicyFile";
+    @SuppressWarnings("unchecked")
+    private static final ProtectionDomain sysDomain = (ProtectionDomain) AccessController.doPrivileged(new PrivilegedAction() {
+
+        public Object run() {
+            return Object.class.getProtectionDomain();
+        }
+    });
+    private volatile Policy basePolicy;
+    private volatile boolean cacheBasePerms;
+    private volatile boolean initialized;
+    // REMIND: do something with WeakIdentityMap and Concurrency, note
+    // this means different implementation methods not a drop in.
+    private final ConcurrentMap<ProtectionDomain, DomainPermissions> domainPerms;
+    private final ConcurrentMap<ClassLoader, Grants> loaderGrants;
+    private final Grants globalGrants;
+    private static final Logger logger = Logger.getLogger(
+            "net.jini.security.policy.DynamicPolicyProviderImpl");
+
+    /**
+     * A new uninitialized instance.
+     */
+    ConcurrentDynamicPolicyProvider() {
+        domainPerms = new WeakIdentityMap<ProtectionDomain, DomainPermissions>();
+        loaderGrants = new WeakIdentityMap<ClassLoader, Grants>();
+        globalGrants = new Grants();
+        basePolicy = null;
+        cacheBasePerms = false;
+        initialized = false;
+    }
+
+    /**
+     * This method is only called once, on an uninitialized instance
+     * further attempts will fail.
+     * @param basePolicy
+     * @return success
+     */
+    public boolean basePolicy(Policy basePolicy) {
+        if (this.basePolicy == null) {
+            this.basePolicy = basePolicy;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * This method completes construction of the Implementation, considered
+     * safe since it is called through the Service and cannot be accessed
+     * otherwise.
+     * @throws net.jini.security.policy.PolicyInitializationException
+     * @throws java.lang.InstantiationException
+     */
+    public void initialize() throws PolicyInitializationException {
+        if (initialized == true) {
+            return;
+        }
+        if (basePolicy == null) {
+            String cname = Security.getProperty(basePolicyClassProperty);
+            if (cname == null) {
+                cname = defaultBasePolicyClass;
+            }
+            try {
+                basePolicy = (Policy) Class.forName(cname).newInstance();
+            } catch (InstantiationException ex) {
+                if (logger.isLoggable(Level.SEVERE)) {
+                    logger.log(Level.SEVERE, null, ex);
+                }
+                throw new PolicyInitializationException(
+                        "Unable to create a new instance of: " +
+                        cname, ex);
+            } catch (IllegalAccessException ex) {
+                if (logger.isLoggable(Level.SEVERE)) {
+                    logger.logp(Level.SEVERE, "DynamicPolicyProviderImpl",
+                            "initialize()", "Unable to create a new instance of: " +
+                            cname, ex);
+                }
+                throw new PolicyInitializationException(
+                        "Unable to create a new instance of: " +
+                        cname, ex);
+            } catch (ClassNotFoundException ex) {
+                if (logger.isLoggable(Level.SEVERE)) {
+                    logger.log(Level.SEVERE, "Check " + cname + " is accessable" +
+                            "from your classpath", ex);
+                }
+                throw new PolicyInitializationException(
+                        "Unable to create a new instance of: " +
+                        cname, ex);
+            } catch (SecurityException ex) {
+                if (logger.isLoggable(Level.SEVERE)) {
+                    logger.log(Level.SEVERE,
+                            "You don't have sufficient permissions to create" +
+                            "a new instance of" + cname, ex);
+                }
+                throw new PolicyInitializationException(
+                        "Unable to create a new instance of: " +
+                        cname, ex);
+            }
+        }
+        cacheBasePerms = !(basePolicy instanceof DynamicPolicy);
+        initialized = true;
+    }
+
+    /**
+     * Behaves as specified by {@link Policy#getPermissions(CodeSource)}.
+     */
+    @Override
+    public PermissionCollection getPermissions(CodeSource source) {
+        PermissionCollection pc = basePolicy.getPermissions(source);
+        Permission[] pa = globalGrants.get(null);
+        for (int i = 0; i < pa.length; i++) {
+            Permission p = pa[i];
+            if (!pc.implies(p)) {
+                pc.add(p);
+            }
+        }
+        return pc;
+    }
+
+    /**
+     * Behaves as specified by {@link Policy#getPermissions(ProtectionDomain)}.
+     */
+    @Override
+    public PermissionCollection getPermissions(ProtectionDomain domain) {
+        return getDomainPermissions(domain).getPermissions(domain);
+    }
+
+    /**
+     * Behaves as specified by {@link Policy#implies}.
+     */
+    public boolean implies(ProtectionDomain domain, Permission permission) {
+        return getDomainPermissions(domain).implies(permission, domain);
+    }
+
+    /**
+     * Behaves as specified by {@link Policy#refresh}.
+     */
+    public void refresh() {
+        basePolicy.refresh();
+        if (cacheBasePerms) {
+            domainPerms.clear();
+        }
+    }
+
+    // documentation inherited from DynamicPolicy.grantSupported
+    public boolean grantSupported() {
+        return true;
+    }
+
+    // documentation inherited from DynamicPolicy.grant
+    public void grant(Class cl,
+            Principal[] principals,
+            Permission[] permissions) {
+        if (cl != null) {
+            checkDomain(cl);
+        }
+        if (principals != null && principals.length > 0) {
+            principals = (Principal[]) principals.clone();
+            checkNullElements(principals);
+        }
+        if (permissions == null || permissions.length == 0) {
+            return;
+        }
+        permissions = (Permission[]) permissions.clone();
+        checkNullElements(permissions);
+
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkPermission(new GrantPermission(permissions));
+        }
+
+        Grants g = (cl != null) ? getLoaderGrants(getClassLoader(cl)) : globalGrants;
+        g.add(principals, permissions);
+    }
+
+    // documentation inherited from DynamicPolicy.getGrants
+    public Permission[] getGrants(Class cl, Principal[] principals) {
+        if (cl != null) {
+            checkDomain(cl);
+        }
+        if (principals != null && principals.length > 0) {
+            principals = (Principal[]) principals.clone();
+            checkNullElements(principals);
+        }
+
+        List l = Arrays.asList(globalGrants.get(principals));
+        if (cl != null) {
+            l = new ArrayList(l);
+            l.addAll(Arrays.asList(
+                    getLoaderGrants(getClassLoader(cl)).get(principals)));
+        }
+        PermissionCollection pc = new Permissions();
+        for (Iterator i = l.iterator(); i.hasNext();) {
+            Permission p = (Permission) i.next();
+            if (!pc.implies(p)) {
+                pc.add(p);
+            }
+        }
+        l = Collections.list(pc.elements());
+        return (Permission[]) l.toArray(new Permission[l.size()]);
+    }
+
+    /**
+     * Ensures that any classes depended on by this policy provider are
+     * resolved.  This is to preclude lazy resolution of such classes during
+     * operation of the provider, which can result in deadlock as described by
+     * bug 4911907.
+     */
+    public void ensureDependenciesResolved() {
+        // force class resolution by pre-invoking method called by implies()
+        getDomainPermissions(sysDomain);
+    }
+
+    private DomainPermissions getDomainPermissions(ProtectionDomain pd) {
+        DomainPermissions dp = domainPerms.get(pd);
+        if (dp == null) {
+            dp = new DomainPermissions(pd);
+            DomainPermissions exists = null;
+            if (pd != null) {
+                // If a concurrent thread has created a new copy, we get that instead;
+                exists = domainPerms.putIfAbsent(pd, dp);
+                if (exists != null) {
+                    dp = exists;
+                }
+                getLoaderGrants(pd.getClassLoader()).register(dp);
+            }
+        }
+
+        synchronized (domainPerms) {
+            dp = (DomainPermissions) domainPerms.get(pd);
+        }
+        if (dp == null) {
+            dp = new DomainPermissions(pd);
+            globalGrants.register(dp);
+            if (pd != null) {
+                getLoaderGrants(pd.getClassLoader()).register(dp);
+            }
+            synchronized (domainPerms) {
+                domainPerms.put(pd, dp);
+            }
+        }
+        return dp;
+    }
+
+    private Grants getLoaderGrants(ClassLoader ldr) {
+        if (ldr == null) {
+            throw new NullPointerException("ClassLoader cannot be null");
+        }
+        Grants g = loaderGrants.get(ldr);
+        if (g == null) {
+            g = new Grants();
+            Grants exists = loaderGrants.putIfAbsent(ldr, g);
+            if (exists != null) {
+                g = exists;
+            }
+        }
+        return g;
+    }
+
+    private static ClassLoader getClassLoader(final Class cl) {
+        return (ClassLoader) AccessController.doPrivileged(
+                new PrivilegedAction() {
+
+                    public Object run() {
+                        return cl.getClassLoader();
+                    }
+                });
+    }
+
+    private static void checkDomain(final Class cl) {
+        ProtectionDomain pd = (ProtectionDomain) AccessController.doPrivileged(
+                new PrivilegedAction() {
+
+                    public Object run() {
+                        return cl.getProtectionDomain();
+                    }
+                });
+        if (pd != sysDomain && pd.getClassLoader() == null) {
+            throw new UnsupportedOperationException(
+                    "ungrantable protection domain");
+        }
+    }
+
+    private static void checkNullElements(Object[] array) {
+        for (int i = 0; i < array.length; i++) {
+            if (array[i] == null) {
+                throw new NullPointerException();
+            }
+        }
+    }
+
+    /**
+     * Class which holds permissions and principals of a ProtectionDomain. The
+     * domainPerms map associates ProtectionDomain instances to instances of
+     * this class.
+     * 
+     * This isn't the best designed class, the Set escapes, it isn't immutable
+     * or synchronized although it is backed by a fixed size array, so one 
+     * could potentially change the Principals, it is only read however 
+     * within this implementation and is private, so we'll leave it for now,
+     * with this caution.
+     */
+    class DomainPermissions {
+
+        private final Set principals;
+        private final PermissionCollection perms;
+        private final List grants = new ArrayList();
+
+        @SuppressWarnings("unchecked")
+        DomainPermissions( ProtectionDomain pd) {
+            Principal[] pra;
+            principals = (pd != null && (pra = pd.getPrincipals()).length > 0)
+                    ? new HashSet(Arrays.asList(pra)) : Collections.EMPTY_SET;
+            perms = cacheBasePerms ? basePolicy.getPermissions(pd) : null;
+        }
+
+        Set getPrincipals() {
+            return principals;
+        }
+
+        @SuppressWarnings("unchecked")
+        synchronized void add(Permission[] pa) {
+            for (int i = 0; i < pa.length; i++) {
+                Permission p = pa[i];
+                grants.add(p);
+                if (perms != null) {
+                    perms.add(p);
+                }
+            }
+        }
+
+        synchronized PermissionCollection getPermissions(ProtectionDomain d) {
+            return getPermissions(true, d);
+        }
+
+        synchronized boolean implies(Permission p, ProtectionDomain domain) {
+//            System.out.println("Permission: " + p.toString() + 
+//                    " ProtectionDomain: " + domain.toString());
+            if (perms != null) {
+                return perms.implies(p);
+            }
+            if (basePolicy.implies(domain, p)) {
+                return true;
+            }
+            if (grants.isEmpty()) {
+                return false;
+            }
+            return getPermissions(false, domain).implies(p);
+        }
+
+        private PermissionCollection getPermissions(boolean compact,
+                ProtectionDomain domain) {
+            // base policy permission collection may not be enumerable
+            assert Thread.holdsLock(this);
+            PermissionCollection pc = basePolicy.getPermissions(domain);
+            for (Iterator i = grants.iterator(); i.hasNext();) {
+                Permission p = (Permission) i.next();
+                if (!(compact && pc.implies(p))) {
+                    pc.add(p);
+                }
+            }
+            return pc;
+        }
+    }
+
+    public void revoke(Class cl, Principal[] principals, Permission[] permissions) {
+        throw new UnsupportedOperationException("Revoke not supported.");
+    }
+
+    public boolean revokeSupported() {
+        return false;
+    }
+
+    public Object parameters() throws UnsupportedOperationException {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+}

Propchange: incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentDynamicPolicyProvider.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentPermissions.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentPermissions.java?rev=928394&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentPermissions.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentPermissions.java Sun Mar 28 12:57:03 2010
@@ -0,0 +1,332 @@
+/*
+ * 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.security.concurrent;
+
+import java.io.Serializable;
+import java.security.AllPermission;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.ProtectionDomain;
+import java.security.UnresolvedPermission;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import org.apache.river.security.RevokeablePermissionCollection;
+
+
+/**
+ * Example Implementation alternative of Permissions implemented for concurrency, 
+ * note this couldn't extend Permissions as it is declared final.
+ * 
+ * Note that if there is heavy contention for one Permission class
+ * type, due to synchronization, concurrency will
+ * suffer.  This is due to the original PermissionsCollection spec requiring
+ * that all implementations do their own synchronization, this is a design
+ * mistake, similar to Vector. 
+ * 
+ * This is an example class without optimisation, it will be slower
+ * for single threaded applications and consume more memory.
+ * 
+ * It would also be possible to create an unlock method that permitted
+ * adding or revoking permissions to this collection while it is referenced
+ * from within a PermissionDomain, however that would break the contract of
+ * setReadOnly().  This might make sense if implementing a SecurityManager or
+ * it might not, it's just an observation that Permissions defined in policy
+ * files, which are not dynamically granted, are not revokeable as a
+ * Policy only augments the PermissionCollection associated with (referenced by)
+ * a PermissionDomain.  However it would probably be best to extend
+ * ProtectionDomain to alter this behaviour as it merges PermissionCollection's,
+ * so you would end up with the Permissions implementation again, unless using the
+ * constructor that sets the permissions as static, which is totally
+ * contradictory.  So the best way to make a Permission revokeable is to 
+ * grant it dynamically.
+ * 
+ * TODO: Serialization properly
+ * @version 0.4 2009/11/10
+ * 
+ * @author Peter Firmstone
+ * @serial permsMap
+ */
+public final class ConcurrentPermissions extends PermissionCollection 
+implements Serializable, RevokeablePermissionCollection  {
+
+    private static final long serialVersionUID=1L;
+    /* unresolved is never returned or allowed to escape, it's elements() method
+     * isn't used to return an Enumeration yet 
+     * Duplicate Permissions could potentially be returned if unresolved is
+     * enumerated first.
+     * For a ProtectionDomain object, duplicates are dropped by 
+     * java.security.AccessControlContext
+     * This creates issues with java.security.AccessControlContext and
+     * causes it to throw an exception.
+     */ 
+    private transient final PermissionPendingResolutionCollection unresolved;
+    private ConcurrentHashMap<Class<?>, PermissionCollection> permsMap;
+    private transient volatile boolean allPermission;
+    
+    /* Let Permissions, UnresolvedPermission and 
+     * UnresolvedPermissionCollection resolve all unresolved permission's
+     * it saves reimplementing package private methods.  This is done by adding
+     * a Permissions object instance to handle all UnresolvedPermissions.
+     */    
+    
+    public ConcurrentPermissions(){
+        permsMap = new ConcurrentHashMap<Class<?>, PermissionCollection>();
+        // Bite the bullet, get the pain out of the way in the beginning!
+        unresolved = new PermissionPendingResolutionCollection();
+        allPermission = false;      
+    }
+    
+    /**
+     * Threadsafe
+     * @param permission
+     */   
+    @Override
+    public void add(Permission permission) {
+        if (permission == null){return;}
+        if (super.isReadOnly()) {
+            throw new SecurityException("attempt to add a Permission to a readonly Permissions object");
+        } 
+        if (permission instanceof AllPermission) {allPermission = true;}
+        if (permission instanceof UnresolvedPermission) {          
+            unresolved.add(new PermissionPendingResolution((UnresolvedPermission)permission));            
+        }
+        // this get saves unnecessary object creation.
+        PermissionCollection pc = permsMap.get(permission.getClass());
+        if (pc != null){                       
+            pc.add(permission);
+            return;             
+        }
+        // A null instance occurs when a PermissionCollection was absent.
+        pc = permsMap.putIfAbsent(permission.getClass(), new MultiReadPermissionCollection(permission));                                  
+        // If This is the first time the PermissionCollection was added
+        // we must still add the permission, see Add the permission below.
+        // The putIfAbsent will succeed, and as a result pc is null, provided another
+        // thread doesn't add the permission between this put and the last get.
+        // Just in case, we check the return value of the putIfAbsent, if
+        // pc != null then another thread has alreaddy added a PermissionCollection
+        if (pc != null){            
+            pc.add(permission);
+            return;
+        }
+        // Add the permission if it missed out due to unlucky timing.
+        pc = permsMap.get(permission.getClass());
+        pc.add(permission);        
+    }    
+    
+    /**
+     * Returns true if Permission is implied for this PermissionDomain.
+     * Threadsafe this method is also a mutator method for internal state
+     * 
+     * @see Permission
+     * @param permission
+     * @return boolean
+     */
+    @Override
+    public boolean implies(Permission permission) {
+        if (permission == null){return false;}
+        if (allPermission == true){return true;}
+        if (permission instanceof UnresolvedPermission){return false;}        
+        PermissionCollection pc = permsMap.get(permission.getClass()); // To stop unnecessary object creation
+        if (pc != null && pc.implies(permission)) { return true;}
+        if (unresolved.awaitingResolution() == 0 ) { return false; }
+        PermissionCollection existed = null;
+        if (pc == null){
+            pc = new MultiReadPermissionCollection(permission); // once added it cannot be removed atomically.
+            existed = permsMap.putIfAbsent(permission.getClass(), pc);
+            if (existed != null) {
+                pc = existed;
+                }
+        }
+        unresolved.resolveCollection(permission, pc); // drop the returned collection its the same as the one passed in.
+        return pc.implies(permission);
+    }
+    
+    /**
+     * This Enumeration is not intended for concurrent access,
+     * PermissionCollection's underlying state is protected by defensive copying, 
+     * it wont affect the thread safety of ConcurrentPermission.
+     * 
+     * Any number of these Enumerations may be utilised , each accessed by 
+     * a separate thread.
+     * 
+     * This Enumeration may contain duplicates
+     * 
+     * @return Enumeration<Permission>
+     */
+    @Override
+    public Enumeration<Permission> elements() {
+        Enumeration<PermissionCollection> perms = permsMap.elements();
+        return new PermissionEnumerator(perms);                 
+    }
+    
+    /**
+     * Attempt to resolve any unresolved permissions whose class is visible
+     * from within this protection domain.
+     * @param pd 
+     */
+    public void resolve(ProtectionDomain pd){
+        if (unresolved.awaitingResolution() == 0){return;}
+        Enumeration<Permission> perms = unresolved.resolvePermissions(pd);
+        while (perms.hasMoreElements()){
+            add(perms.nextElement());
+        }
+    }
+    
+    /*
+     * This Enumeration is not intended for concurrent access,
+     * PermissionCollection's underlying state is protected by defensive copying, 
+     * it wont affect the thread safety of ConcurrentPermission.
+     * 
+     * This enumeration doesn't include unresolved permissions is that a problem?
+     * 
+     * Any number of these Enumerations may be utilised , each accessed by 
+     * a separate thread.
+     * 
+     * @author Peter Firmstone
+     */
+    private final static class PermissionEnumerator implements Enumeration<Permission> {
+        private final Enumeration<PermissionCollection> epc;
+        private volatile Enumeration<Permission> currentPermSet;
+
+        PermissionEnumerator(Enumeration<PermissionCollection> epc){
+            this.epc = epc;
+            currentPermSet = getNextPermSet();
+        }
+
+        private Enumeration<Permission> getNextPermSet(){
+            Set<Permission> permissionSet = new HashSet<Permission>();
+            if (epc.hasMoreElements()){
+                PermissionCollection pc = epc.nextElement();               
+                /* Local copy of the set containing a snapshot of 
+                 * references to Permission objects present at an instant in time,
+                 * we can Enumerate over, without contention or exception.  
+                 * We only take what we need as we need it, minimising memory.
+                 * Each object gets its own Enumeration.
+                 */
+                if ( pc instanceof Permissions){
+                    synchronized (pc){
+                        Enumeration<Permission> e = pc.elements();
+                        while (e.hasMoreElements()) {
+                            permissionSet.add(e.nextElement());
+                        }
+                    }
+                } else {
+                    Enumeration<Permission> e = pc.elements();
+                    while (e.hasMoreElements()) {
+                        permissionSet.add(e.nextElement());
+                    }
+                }
+            }
+            return Collections.enumeration(permissionSet);
+        }
+
+        public boolean hasMoreElements() {        
+            if (currentPermSet.hasMoreElements()){return true;}          
+            currentPermSet = getNextPermSet();
+            return currentPermSet.hasMoreElements();           
+        }
+
+        public Permission nextElement() {
+            if (hasMoreElements()){              
+                return currentPermSet.nextElement();               
+            } else {
+                throw new NoSuchElementException("PermissionEnumerator");
+            }
+        }
+    }
+
+    public int revoke(Permission ... permissions) {
+        
+        // build a hashmap to put all permissions into, grouped by class.
+        HashMap<Class<?>, Integer> results = new HashMap<Class<?>, Integer>();
+        HashMap<Class<?>, ArrayList<Permission>> groups = new HashMap<Class<?>, ArrayList<Permission>>();
+        int size = permissions.length;
+        for (int i = 0; i < size; i++ ){
+            Permission permission = permissions[i];
+            //Make sure that any unresolved permissions have been resolved first
+            //Don't bother revoking if it doesn't imply
+            if ( !implies(permission)){
+                continue;
+            }
+            ArrayList<Permission> permissionClassGroup = groups.get(permission.getClass());
+            if (permissionClassGroup == null){
+                permissionClassGroup = new ArrayList<Permission>();
+                groups.put(permissions[i].getClass(), permissionClassGroup);
+            }
+            permissionClassGroup.add(permission); // still holds a reference
+        }
+        // process each group by class, if all succeed return 1, if one or more has
+        // an issue with an intervening write return 0.
+        // If any fail, return -1
+        Set<Class<?>> classes = groups.keySet();
+        Iterator<Class<?>> itr = classes.iterator();
+        while (itr.hasNext()){
+            Class<?> permissionClass = itr.next();
+            RevokeablePermissionCollection current = 
+                    (RevokeablePermissionCollection) permsMap.get(permissionClass);
+            int result = 1; // If it doesn't exist then it must succeed.
+            if (current != null){
+                ArrayList<Permission> permissionGroupToRevoke = groups.get(permissionClass);
+                Permission permissionList[] = new Permission[permissionGroupToRevoke.size()];
+                permissionGroupToRevoke.toArray(permissionList);
+                result = current.revoke( permissionList );
+            }
+            results.put(permissionClass, result);
+        }
+        // Now determine the results and return
+        int result = 1;
+        Iterator<Integer> itresult = results.values().iterator();
+        while (itresult.hasNext()){
+            int thisResult = itresult.next();
+            if (thisResult < result) { result = thisResult; }
+        }
+        return result;
+    }
+    
+    
+    public void revokeAll(Permission permission) {
+        PermissionCollection pc = permsMap.get(permission.getClass());
+        if ( pc == null ){
+            pc = new MultiReadPermissionCollection(permission);
+            PermissionCollection exists = permsMap.putIfAbsent(permission.getClass(),pc);
+            if ( exists != null ){
+                pc = exists;
+            }
+        unresolved.resolveCollection(permission, pc);          
+        }
+        // Any permissions added by other threads in the interim will be
+        // revoked when this method completes.
+        // We won't avoid permission loss by only emptying the collection either,
+        // however since
+        // the intent will be probably to temporarily revoke a permission
+        // when a proxy is required to be refreshed, it should be more efficient.
+        //permsMap.remove(permission.getClass(), pc);
+        RevokeablePermissionCollection rpc = (RevokeablePermissionCollection) pc;
+        rpc.revokeAll(permission);
+    }
+
+}

Propchange: incubator/river/jtsk/trunk/src/org/apache/river/security/concurrent/ConcurrentPermissions.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message