[concurrency-interest] Non Blocking java.security.Policy - synchronized method is superclass.

Nathan Reynolds nathan.reynolds at oracle.com
Mon Jan 9 12:22:30 EST 2012


 > How much can this one synchronized method spoil scalability?

Depends upon the workload.  Some workloads will never hit the method and 
hence will never have a scalability problem.  Other workloads will hit 
the method but not heavily enough to be a concern.  A contrived workload 
which launches a bunch of threads and simply call this method in a tight 
loop will not scale.  So, the question really is: Is there a real 
workload out there that won't scale due to this method?

Nathan Reynolds 
<http://psr.us.oracle.com/wiki/index.php/User:Nathan_Reynolds> | 
Consulting Member of Technical Staff | 602.333.9091
Oracle PSR Engineering <http://psr.us.oracle.com/> | Server Technology

On 1/8/2012 4:40 AM, Peter Firmstone wrote:
> Appended is a new java.security.Policy implementation, it fully supports
> the existing java policy syntax and accepts alternate PolicyParser's.
>
> All state is immutable, except for 2 volatile references, referents
> replaced, not mutated, when the policy is updated.  One referent is an
> array containing PermissionGrant's (interface for immutable object
> representing a grant statement in a policy), the second a
> PermissionCollection containing the Policy Permissions.  The array is
> never mutated after creation, a reference to the array is copied before
> accessing the array or any array methods.
>
> The policy creates PermissionCollection's on demand for checking,
> Permission's are ordered using a PermissionComparator to ensure that for
> example, wildcard SocketPermission's are checked first, to avoid
> unnecessary DNS lookups.  Only the permission being checked and any
> UnresolvedPermission's are added to the PermissionCollection, limiting
> the size of the objects created.
>
> In existing policy implementations PermissionCollection's perform
> blocking operations.
>
> Also, after parsing policy files, PermissionGrant implementations avoid
> the need to open files or network connections to confirm URL's, eg
> CodeSource.implies is not called, but instead reimplemented using URI.
>
> Will this scale?  There but one smell:
>
> ProtectionDomain uses a synchronized method Policy.getPolicyNoCheck(),
> but this only retrieves a reference on 99% of occasions.
>
> For every permission check, the stack access control context is
> retrieved, every ProtectionDomain on the stack must be checked,
> ProtectionDomain's must call getPolicyNoCheck() to call
> Policy.implies(ProtectionDomain domain, Permission permission).
>
> To make this worse, I've got a SecurityManager that divides the
> ProtectionDomain.implies() calls into tasks and submits them to an
> executor (if there are 4 or more PD's in a context).  The
> SecurityManager is also non blocking, at least it will be when I use the
> new ConcurrentHashMap for the checked permission cache (avoids repeated
> security checks), for now the cache is implemented using the existing
> ConcurrentHashMap, but is mostly read in any case.  (P.S. This is the
> cache I'm using the Reference Collection's for.)
>
> How much can this one synchronized method spoil scalability?
>
> Cheers&  thanks in advance,
>
> Peter.
>
>
>
> /*
> *  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.
> */
>
> /**
>   * Default Policy implementation taken from Apache Harmony, refactored
> for
>   * concurrency.
>   *
>   * @author Alexey V. Varlamov
>   * @author Peter Firmstone
>   * @version $Revision$
>   */
>
> package net.jini.security.policy;
>
> import java.io.File;
> import java.net.URL;
> import java.security.AccessController;
> import java.security.AllPermission;
> import java.security.CodeSource;
> import java.security.Guard;
> import java.security.Permission;
> import java.security.PermissionCollection;
> import java.security.Permissions;
> import java.security.Policy;
> import java.security.PrivilegedActionException;
> import java.security.PrivilegedExceptionAction;
> import java.security.ProtectionDomain;
> import java.security.SecurityPermission;
> import java.security.UnresolvedPermission;
> import java.util.ArrayList;
> import java.util.Collection;
> import java.util.Enumeration;
> import java.util.Iterator;
> import java.util.List;
> import java.util.NavigableSet;
> import java.util.Properties;
> import java.util.TreeSet;
> import net.jini.security.PermissionComparator;
> 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;
>
>
> /**
> * Concurrent Policy implementation based on policy configuration files,
> * it is intended to provide concurrent implies() for greatly improved
> * throughput at the expense of single threaded performance.
> *
> * Set the following system properties to use this Policy instead of the
> * built in Java sun.security.provider.PolicyFile:
> * * net.jini.security.policy.PolicyFileProvider.basePolicyClass =
> * org.apache.river.security.concurrent.ConcurrentPolicyFile
> *
> *
> * This
> * implementation recognizes text files, consisting of clauses with the
> * following syntax:
> *
> *<pre>
> * keystore"some_keystore_url" [,"keystore_type"];
> *</pre>
> <pre>
> * grant [SignedBy"signer_names"] [, CodeBase"URL"]
> *  [, Principal [principal_class_name]"principal_name"]
> *  [, Principal [principal_class_name]"principal_name"] ... {
> *  permission permission_class_name ["target_name" ] [,
> "action"]
> *  [, SignedBy"signer_names"];
> *  permission ...
> *  };
> * *</pre>
> *
> * The<i>keystore</i>  clause specifies reference to a keystore, which
> is a
> * database of private keys and their associated digital certificates.
> The
> * keystore is used to look up the certificates of signers specified in
> the
> *<i>grant</i>  entries of the file. The policy file can contain any
> number of
> *<i>keystore</i>  entries which can appear at any ordinal position.
> However,
> * only the first successfully loaded keystore is used, others are
> ignored. The
> * keystore must be specified if some grant clause refers to a
> certificate's
> * alias.<br>
> * The<i>grant</i>  clause associates a CodeSource (consisting of an URL
> and a
> * set of certificates) of some executable code with a set of Permissions
> which
> * should be granted to the code. So, the CodeSource is defined by values
> of
> *<i>CodeBase</i>  and<i>SignedBy</i>  fields. The<i>CodeBase</i>
> value must
> * be in URL format, while<i>SignedBy</i>  value is a (comma-separated
> list of)
> * alias(es) to keystore certificates. These fields can be omitted to
> denote any
> * codebase and any signers (including case of unsigned code),
> respectively.
> *<br>
> * Also, the code may be required to be executed on behalf of some
> Principals
> * (in other words, code's ProtectionDomain must have the array of
> Principals
> * associated) in order to possess the Permissions. This fact is
> indicated by
> * specifying one or more<i>Principal</i>  fields in the<i>grant</i>
> clause.
> * Each Principal is specified as class/name pair; name and class can be
> either
> * concrete value or wildcard<i>*</i>. As a special case, the class
> value may
> * be omitted and then the name is treated as an alias to X.509
> Certificate, and
> * the Principal is assumed to be javax.security.auth.x500.X500Principal
> with a
> * name of subject's distinguished name from the certificate.<br>
> * The order between the<i>CodeBase</i>,<i>SignedBy</i>, and
> <i>Principal
> *</i>  fields does not matter. The policy file can contain any number of
> grant
> * clauses.<br>
> * Each<i>grant</i>  clause must contain one or more<i>permission</i>
> entry.
> * The permission entry consist of a fully qualified class name along
> with
> * optional<i>name</i>,<i>actions</i>  and<i>signedby</i>  values.
> Name and
> * actions are arguments to the corresponding constructor of the
> permission
> * class. SignedBy value represents the keystore alias(es) to
> certificate(s)
> * used to sign the permission class. That is, this permission entry is
> * effective (i.e., access control permission will be granted based on
> this
> * entry) only if the bytecode implementation of permission class is
> verified to
> * be correctly signed by the said alias(es).<br>
> *<br>
> * The policy content may be parameterized via property expansion.
> Namely,
> * expressions like<i>${key}</i>  are replaced by values of
> corresponding
> * system properties. Also, the special<i>slash</i>  key (i.e. ${/}) is
> * supported, it is a shortcut to"file.separator" key.
> Property
> * expansion is performed anywhere a double quoted string is allowed in
> the
> * policy file. However, this feature is controlled by security
> properties and
> * should be turned on by setting"policy.expandProperties"
> property
> * to<i>true</i>.<br>
> * If property expansion fails (due to a missing key), a corresponding
> entry is
> * ignored. For fields of<i>keystore</i>  and<i>grant</i>  clauses, the
> whole
> * clause is ignored, and for<i>permission</i>  entry, only that entry
> is
> * ignored.<br>
> *<br>
> * The policy also supports generalized expansion in permissions names,
> of
> * expressions like<i>${{protocol:data}}</i>. Currently the following
> * protocols supported:
> *<dl>
> *<dt>self
> *<dd>Denotes substitution to a principal information of the parental
> Grant
> * entry. Replaced by a space-separated list of resolved Principals
> (including
> * wildcarded), each formatted as<i>class"name"</i>. If
> parental
> * Grant entry has no Principals, the permission is ignored.
> *<dt>alias:<i>name</i>
> *<dd>Denotes substitution of a KeyStore alias. Namely, if a KeyStore
> has an
> * X.509 certificate associated with the specified name, then replaced by
> *<i>javax.security.auth.x500.X500Principal"<i>DN</i>"</i>
> * string, where<i>DN</i>  is a certificate's subject distinguished
> name.
> *</dl>
> *<br>
> *
> */
>
> public class ConcurrentPolicyFile extends Policy implements
> ConcurrentPolicy {
>
>     /**
>      * System property for dynamically added policy location.
>      */
>     private static final String JAVA_SECURITY_POLICY =
> "java.security.policy"; //$NON-NLS-1$
>
>     /**
>      * Prefix for numbered Policy locations specified in
> security.properties.
>      */
>     private static final String POLICY_URL_PREFIX = "policy.url.";
> //$NON-NLS-1$
>       // Reference must be defensively copied before access, once
> published, never mutated.
>     private volatile PermissionGrant [] grantArray;
>       // A specific parser for a particular policy file format.
>     private final PolicyParser parser;
>       private static final Guard guard = new
> SecurityPermission("getPolicy");
>       private final ProtectionDomain myDomain;
>       // reference must be defensively copied before access, once
> published, never mutated.
>     private volatile PermissionCollection myPermissions;
>       /**
>      * Default constructor, equivalent to
>      *<code>ConcurrentPolicyFile(new DefaultPolicyParser())</code>.
>      */
>     public ConcurrentPolicyFile() throws PolicyInitializationException {
>         this(new DefaultPolicyParser());
>     }
>
>     /**
>      * Extension constructor for plugging-in a custom parser.
>      * @param dpr
>      */
>     protected ConcurrentPolicyFile(PolicyParser dpr) throws
> PolicyInitializationException {
>         guard.checkGuard(null);
>         parser = dpr;
>         myDomain = this.getClass().getProtectionDomain();
>         /*
>          * The bootstrap policy makes implies decisions until this
> constructor
>          * has returned.  We don't need to lock.
>          */
>         try {
>             // Bug 4911907, do we need to do anything more?
>             // The permissions for this domain must be retrieved before
>             // construction is complete and this policy takes over.
>             initialize(); // Instantiates myPermissions.
>         } catch (SecurityException e){
>             throw e;
>         } catch (Exception e){
>             throw new PolicyInitializationException("PolicyInitialization
> failed", e);
>         }
>     }
>       private PermissionCollection convert(NavigableSet<Permission>
> permissions){
>         PermissionCollection pc = new Permissions();
>         // The descending iterator is for SocketPermission.
>         Iterator<Permission>  it = permissions.descendingIterator();
>         while (it.hasNext()) {
>             pc.add(it.next());
>         }
>         return pc;
>     }
>
>     /**
>      * Returns collection of permissions allowed for the domain
>      * according to the policy. The evaluated characteristics of the
>      * domain are it's codesource and principals; they are assumed
>      * to be<code>null</code>  if the domain is<code>null</code>.
>      *
>      * Each PermissionCollection returned is a unique instance.
>      *
>      * @param pd ProtectionDomain
>      * @see ProtectionDomain
>      */
>     @Override
>     public PermissionCollection getPermissions(ProtectionDomain pd) {
>         NavigableSet<Permission>  perms = new TreeSet<Permission>(new
> PermissionComparator());
>         PermissionGrant [] grantRefCopy = grantArray;
>         int l = grantRefCopy.length;
>         for ( int j =0; j<  l; j++ ){
>             PermissionGrant ge = grantRefCopy[j];
>             if (ge.implies(pd)){
>                 if (ge.isPrivileged()){// Don't stuff around finish early
> if you can.
>                     PermissionCollection pc = new Permissions();
>                     pc.add(new AllPermission());
>                     return pc;
>                 }
>                 Collection<Permission>  c = ge.getPermissions();
>                 Iterator<Permission>  i = c.iterator();
>                 while (i.hasNext()){
>                     Permission p = i.next();
>                     perms.add(p);
>                 }
>             }
>         }
>         // Don't forget to merge the static Permissions.
>         PermissionCollection staticPC = null;
>         if (pd != null) {
>             staticPC = pd.getPermissions();
>             if (staticPC != null){
>                 Enumeration<Permission>  e = staticPC.elements();
>                 while (e.hasMoreElements()){
>                     Permission p = e.nextElement();
>                     if (p instanceof AllPermission) {
>                         PermissionCollection pc = new Permissions();
>                         pc.add(p);
>                         return pc;
>                     }
>                     perms.add(p);
>                 }
>             }
>         }
>         return convert(perms);
>     }
>
>     /**
>      * Returns collection of permissions allowed for the codesource
>      * according to the policy.
>      * The evaluation assumes that current principals are undefined.
>      *
>      * This returns a java.security.Permissions collection, which allows
>      * ProtectionDomain to optimise for the AllPermission case, which
> avoids
>      * unnecessarily consulting the policy.
>      *
>      * If constructed with the four argument constructor,
> ProtectionDomain.implies
>      * first consults the Policy, then it's own internal Permissions
> collection,
>      * unless it has AllPermission, in which case it returns true without
>      * consulting the policy.
>      *
>      * @param cs CodeSource
>      * @see CodeSource
>      */
>     @Override
>     public PermissionCollection getPermissions(CodeSource cs) {
>         if (cs == null) throw new NullPointerException("CodeSource cannot
> be null");
>         NavigableSet<Permission>  perms = new TreeSet<Permission>(new
> PermissionComparator());
>         // for ProtectionDomain AllPermission optimisation.
>         PermissionGrant [] grantRefCopy = grantArray;
>         int l = grantRefCopy.length;
>         for ( int j =0; j<  l; j++ ){
>             PermissionGrant ge = grantRefCopy[j];
>             if (ge.implies(cs, null)){ // No Principal's
>                 if (ge.isPrivileged()){// Don't stuff around finish early
> if you can.
>                     PermissionCollection pc = new Permissions();
>                     pc.add(new AllPermission());
>                     return pc;
>                 }
>                 Collection<Permission>  c = ge.getPermissions();
>                 Iterator<Permission>  i = c.iterator();
>                 while (i.hasNext()){
>                     Permission p = i.next();
>                     perms.add(p);
>                 }
>             }
>         }
>         return convert(perms);
>     }
>       @Override
>     public boolean implies(ProtectionDomain domain, Permission
> permission) {
>         if (permission == null) throw new
> NullPointerException("permission not allowed to be null");
>         if (domain == myDomain) {
>             PermissionCollection pc = myPermissions;
>             return pc.implies(permission);
>         }
>         Class klass = permission.getClass();
>         // Need to have a list of Permission's we can sort if permission
> is SocketPermission.
>         NavigableSet<Permission>  perms = new TreeSet<Permission>(new
> PermissionComparator());
>         PermissionGrant [] grantRefCopy = grantArray;
>         int l = grantRefCopy.length;
>         for ( int j =0; j<  l; j++ ){
>             PermissionGrant ge = grantRefCopy[j];
>             if (ge.implies(domain)){
>                 if (ge.isPrivileged()) return true; // Don't stuff around
> finish early if you can.
>                 Collection<Permission>  c = ge.getPermissions();
>                 Iterator<Permission>  i = c.iterator();
>                 while (i.hasNext()){
>                     Permission p = i.next();
>                     // Don't make it larger than necessary.
>                     if (klass.isInstance(permission) || permission
> instanceof UnresolvedPermission){
>                         perms.add(p);
>                     }
>                 }
>             }
>         }
>         // Don't forget to merge the static Permissions.
>         PermissionCollection staticPC = null;
>         if (domain != null) {
>             staticPC =domain.getPermissions();
>             if (staticPC != null){
>                 Enumeration<Permission>  e = staticPC.elements();
>                 while (e.hasMoreElements()){
>                     Permission p = e.nextElement();
>                     // return early if possible.
>                     if (p instanceof AllPermission ) return true;
>                     // Don't make it larger than necessary, but don't
> worry about duplicates either.
>                     if (klass.isInstance(permission) || permission
> instanceof UnresolvedPermission){
>                         perms.add(p);
>                     }
>                 }
>             }
>         }
>         return convert(perms).implies(permission);
>     }
>
>     /**
>      * Gets fresh list of locations and tries to load all of them in
> sequence;
>      * failed loads are ignored. After processing all locations, old
> policy
>      * settings are discarded and new ones come into force.<br>
>      *
>      * @see PolicyUtils#getPolicyURLs(Properties, String, String)
>      */
>     @Override
>     public void refresh() {
>         try {
>             initialize();
>         } catch (Exception ex) {
>             System.err.println(ex);
>         }
>     }
>       private void initialize() throws Exception{
>         try {
>             Collection<PermissionGrant>  fresh =
> AccessController.doPrivileged(
>                 new
> PrivilegedExceptionAction<Collection<PermissionGrant>>(){
>                     public Collection<PermissionGrant>  run() throws
> SecurityException {
>                         Collection<PermissionGrant>  fresh = new
> ArrayList<PermissionGrant>(120);
>                         Properties system = System.getProperties();
>                         system.setProperty("/", File.separator);
> //$NON-NLS-1$
>                         URL[] policyLocations =
> PolicyUtils.getPolicyURLs(system,
>
> JAVA_SECURITY_POLICY,
>
> POLICY_URL_PREFIX);
>                         int l = policyLocations.length;
>                         for (int i = 0; i<  l; i++) {
>                             //TODO debug log
> //                                System.err.println("Parsing policy
> file: " + policyLocations[i]);
>                             try {
>                                 Collection<PermissionGrant>  pc = null;
>                                 pc = parser.parse(policyLocations[i],
> system);
>                                 fresh.addAll(pc);
>                             } catch (Exception 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 ) {
>                                     e.printStackTrace(System.out);
>                                     throw (SecurityException) e;
>                                 }
>                                 // ignore.
>                             }
>                         }
>                         return fresh;
>                     }
>                 }
>             );
>             // Volatile reference, publish after mutation complete.
>             grantArray = fresh.toArray(new
> PermissionGrant[fresh.size()]);
>             myPermissions = getPermissions(myDomain);
>         }catch (PrivilegedActionException e){
>             Throwable t = e.getCause();
>             if ( t instanceof Exception ) throw (Exception) t;
>             throw e;
>         }
>     }
>
>     public boolean isConcurrent() {
>         return true;
>     }
>
>     public PermissionGrant[] getPermissionGrants() {
>         PermissionGrant [] grants = grantArray; // copy volatile
> reference target.
>         return grants.clone();
>     }
>       public PermissionGrant[] getPermissionGrants(ProtectionDomain pd) {
>         PermissionGrant [] grants = grantArray; // copy volatile
> reference target.
>         int l = grants.length;
>         List<PermissionGrant>  applicable = new
> ArrayList<PermissionGrant>(l); // Always too large, never too small.
>         for (int i =0; i<  l; i++){
>             if (grants[i].implies(pd)){
>                 applicable.add(grants[i]);
>             }
>         }
>         return applicable.toArray(new
> PermissionGrant[applicable.size()]);
>     }
>
> }
>
> _______________________________________________
> Concurrency-interest mailing list
> Concurrency-interest at cs.oswego.edu
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://cs.oswego.edu/pipermail/concurrency-interest/attachments/20120109/ddeebd9a/attachment-0001.html>


More information about the Concurrency-interest mailing list