You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@river.apache.org by pe...@apache.org on 2010/03/28 14:57:05 UTC

svn commit: r928394 [4/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...

Added: incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/DefaultPolicyParser.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/DefaultPolicyParser.java?rev=928394&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/DefaultPolicyParser.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/DefaultPolicyParser.java Sun Mar 28 12:57:03 2010
@@ -0,0 +1,478 @@
+/*
+ *  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.
+ */
+
+/**
+* @author Alexey V. Varlamov
+* @version $Revision$
+*/
+
+package org.apache.river.security.policy.util;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URL;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.Permission;
+import java.security.Principal;
+import java.security.UnresolvedPermission;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.apache.river.security.policy.util.DefaultPolicyScanner.GrantEntry;
+import org.apache.river.security.policy.util.DefaultPolicyScanner.KeystoreEntry;
+import org.apache.river.security.policy.util.DefaultPolicyScanner.PermissionEntry;
+import org.apache.river.security.policy.util.DefaultPolicyScanner.PrincipalEntry;
+
+
+/**
+ * This is a basic loader of policy files. It delegates lexical analysis to 
+ * a pluggable scanner and converts received tokens to a set of 
+ * {@link org.apache.harmony.security.PolicyEntry PolicyEntries}. 
+ * For details of policy format, see the 
+ * {@link org.apache.harmony.security.DefaultPolicy default policy description}.
+ * <br>
+ * For ordinary uses, this class has just one public method <code>parse()</code>, 
+ * which performs the main task.
+ * Extensions of this parser may redefine specific operations separately, 
+ * by overriding corresponding protected methods. 
+ * <br>
+ * This implementation is effectively thread-safe, as it has no field references 
+ * to data being processed (that is, passes all the data as method parameters).
+ * 
+ * @see org.apache.harmony.security.DefaultPolicy
+ * @see org.apache.harmony.security.DefaultPolicyScanner
+ * @see org.apache.harmony.security.PolicyEntry
+ */
+public class DefaultPolicyParser implements PolicyParser {
+
+    // Pluggable scanner for a specific file format
+    private final DefaultPolicyScanner scanner;
+
+    /** 
+     * Default constructor, 
+     * {@link org.apache.harmony.security.DefaultPolicyScanner DefaultPolicyScanner} 
+     * is used. 
+     */
+    public DefaultPolicyParser() {
+        scanner = new DefaultPolicyScanner();
+    }
+
+    /** 
+     * Extension constructor for plugging-in custom scanner.
+     */
+    public DefaultPolicyParser(DefaultPolicyScanner s) {
+        this.scanner = s;
+    }
+
+    /**
+     * This is the main business method. It manages loading process as follows:
+     * the associated scanner is used to parse the stream to a set of 
+     * {@link org.apache.harmony.security.DefaultPolicyScanner.GrantEntry composite tokens},
+     * then this set is iterated and each token is translated to a PolicyEntry.
+     * Semantically invalid tokens are ignored, the same as void PolicyEntries.
+     * <br>
+     * A policy file may refer to some KeyStore(s), and in this case the first
+     * valid reference is initialized and used in processing tokens.   
+     * 
+     * @param location an URL of a policy file to be loaded
+     * @param system system properties, used for property expansion
+     * @return a collection of PolicyEntry objects, may be empty
+     * @throws Exception IO error while reading location or file syntax error 
+     */
+    public Collection<PolicyEntry> parse(URL location, Properties system)
+            throws Exception {
+        boolean resolve = PolicyUtils.canExpandProperties();
+        Reader r = new BufferedReader(new InputStreamReader(
+                AccessController
+                        .doPrivileged(new PolicyUtils.URLLoader(location))));
+
+        Collection<GrantEntry> grantEntries = new HashSet<GrantEntry>();
+        List<KeystoreEntry> keystores = new ArrayList<KeystoreEntry>();
+
+        try {
+            scanner.scanStream(r, grantEntries, keystores); // modifies keystores
+        }
+        finally {
+            r.close();
+        }
+
+        //XXX KeyStore could be loaded lazily...
+        KeyStore ks = initKeyStore(keystores, location, system, resolve);
+
+        Collection<PolicyEntry> result = new HashSet<PolicyEntry>();
+        for (Iterator<GrantEntry> iter = grantEntries.iterator(); iter.hasNext();) {
+            DefaultPolicyScanner.GrantEntry ge = iter
+                    .next();
+            try {
+                PolicyEntry pe = resolveGrant(ge, ks, system, resolve);
+                if (!pe.isVoid()) {
+                    result.add(pe);
+                }
+            }
+            catch (Exception e) {
+                // TODO: log warning
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Translates GrantEntry token to PolicyEntry object. It goes step by step, 
+     * trying to resolve each component of the GrantEntry:
+     * <ul>
+     * <li> If <code>codebase</code> is specified, expand it and construct an URL.
+     * <li> If <code>signers</code> is specified, expand it and obtain 
+     * corresponding Certificates.
+     * <li> If <code>principals</code> collection is specified, iterate over it. 
+     * For each PrincipalEntry, expand name and if no class specified, 
+     * resolve actual X500Principal from a KeyStore certificate; otherwise keep it 
+     * as UnresolvedPrincipal. 
+     * <li> Iterate over <code>permissions</code> collection. For each PermissionEntry,
+     * try to resolve (see method 
+     * {@link #resolvePermission(DefaultPolicyScanner.PermissionEntry, DefaultPolicyScanner.GrantEntry, KeyStore, Properties, boolean) resolvePermission()}) 
+     * a corresponding permission. If resolution failed, ignore the PermissionEntry. 
+     * </ul>
+     * In fact, property expansion in the steps above is conditional and is ruled by
+     * the parameter <i>resolve</i>.  
+     * <br>
+     * Finally a new PolicyEntry is created, which associates the trinity 
+     * of resolved URL, Certificates and Principals to a set of granted Permissions.
+     * 
+     * @param ge GrantEntry token to be resolved
+     * @param ks KeyStore for resolving Certificates, may be <code>null</code> 
+     * @param system system properties, used for property expansion 
+     * @param resolve flag enabling/disabling property expansion
+     * @return resolved PolicyEntry
+     * @throws Exception if unable to resolve codebase, signers or principals 
+     * of the GrantEntry
+     * @see DefaultPolicyScanner.PrincipalEntry
+     * @see DefaultPolicyScanner.PermissionEntry
+     * @see org.apache.harmony.security.PolicyUtils
+     */
+    protected PolicyEntry resolveGrant(DefaultPolicyScanner.GrantEntry ge,
+            KeyStore ks, Properties system, boolean resolve) throws Exception {
+
+        URL codebase = null;
+        Certificate[] signers = null;
+        Set<Principal>principals = new HashSet<Principal>();
+        Set<Permission>permissions = new HashSet<Permission>();
+        if (ge.codebase != null) {
+            codebase = new URL(resolve ? PolicyUtils.expandURL(ge.codebase,
+                    system) : ge.codebase);
+        }
+        if (ge.signers != null) {
+            if (resolve) {
+                ge.signers = PolicyUtils.expand(ge.signers, system);
+            }
+            signers = resolveSigners(ks, ge.signers);
+        }
+        if (ge.principals != null) {
+            for (Iterator<PrincipalEntry> iter = ge.principals.iterator(); iter.hasNext();) {
+                DefaultPolicyScanner.PrincipalEntry pe = iter
+                        .next();
+                if (resolve) {
+                    pe.name = PolicyUtils.expand(pe.name, system);
+                }
+                if (pe.klass == null) {
+                    principals.add(getPrincipalByAlias(ks, pe.name));
+                } else {
+                    principals.add(new UnresolvedPrincipal(pe.klass, pe.name));
+                }
+            }
+        }
+        if (ge.permissions != null) {
+            for (Iterator<PermissionEntry> iter = ge.permissions.iterator(); iter.hasNext();) {
+                DefaultPolicyScanner.PermissionEntry pe = iter
+                        .next();
+                try {
+                    permissions.add(resolvePermission(pe, ge, ks, system,
+                            resolve));
+                }
+                catch (Exception e) {
+                    // TODO: log warning
+                }
+            }
+        }
+        return new PolicyEntry(new CodeSource(codebase, signers), principals,
+                permissions);
+    }
+
+    /**
+     * Translates PermissionEntry token to Permission object.
+     * First, it performs general expansion for non-null <code>name</code> and
+     * properties expansion for non-null <code>name</code>, <code>action</code> 
+     * and <code>signers</code>.
+     * Then, it obtains signing Certificates(if any), tries to find a class specified by 
+     * <code>klass</code> name and instantiate a corresponding permission object.
+     * If class is not found or it is signed improperly, returns UnresolvedPermission.
+     *
+     * @param pe PermissionEntry token to be resolved
+     * @param ge parental GrantEntry of the PermissionEntry 
+     * @param ks KeyStore for resolving Certificates, may be <code>null</code>
+     * @param system system properties, used for property expansion
+     * @param resolve flag enabling/disabling property expansion
+     * @return resolved Permission object, either of concrete class or UnresolvedPermission
+     * @throws Exception if failed to expand properties, 
+     * or to get a Certificate, 
+     * or to create an instance of a successfully found class 
+     */
+    protected Permission resolvePermission(
+            DefaultPolicyScanner.PermissionEntry pe,
+            DefaultPolicyScanner.GrantEntry ge, KeyStore ks, Properties system,
+            boolean resolve) throws Exception {
+        if (pe.name != null) {
+            pe.name = PolicyUtils.expandGeneral(pe.name,
+                    new PermissionExpander().configure(ge, ks));
+        }
+        if (resolve) {
+            if (pe.name != null) {
+                pe.name = PolicyUtils.expand(pe.name, system);
+            }
+            if (pe.actions != null) {
+                pe.actions = PolicyUtils.expand(pe.actions, system);
+            }
+            if (pe.signers != null) {
+                pe.signers = PolicyUtils.expand(pe.signers, system);
+            }
+        }
+        Certificate[] signers = (pe.signers == null) ? null : resolveSigners(
+                ks, pe.signers);
+        try {
+            Class<?> klass = Class.forName(pe.klass);
+            if (PolicyUtils.matchSubset(signers, klass.getSigners())) {
+                return PolicyUtils.instantiatePermission(klass, pe.name,
+                        pe.actions);
+            }
+        }
+        catch (ClassNotFoundException cnfe) {}
+        //maybe properly signed class will be loaded later
+        return new UnresolvedPermission(pe.klass, pe.name, pe.actions, signers);
+    }
+
+    /** 
+     * Specific handler for expanding <i>self</i> and <i>alias</i> protocols. 
+     */
+    class PermissionExpander implements PolicyUtils.GeneralExpansionHandler {
+
+        // Store KeyStore
+        private KeyStore ks;
+
+        // Store GrantEntry
+        private DefaultPolicyScanner.GrantEntry ge;
+
+        /** 
+         * Combined setter of all required fields. 
+         */
+        public PermissionExpander configure(DefaultPolicyScanner.GrantEntry ge,
+                KeyStore ks) {
+            this.ge = ge;
+            this.ks = ks;
+            return this;
+        }
+
+        /**
+         * Resolves the following protocols:
+         * <dl>
+         * <dt>self
+         * <dd>Denotes substitution to a principal information of the parental 
+         * GrantEntry. Returns a space-separated list of resolved Principals 
+         * (including wildcarded), formatting each as <b>class &quot;name&quot;</b>.
+         * If parental GrantEntry has no Principals, throws ExpansionFailedException.
+         * <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 returns 
+         * <b>javax.security.auth.x500.X500Principal &quot;<i>DN</i>&quot;</b> string, 
+         * where <i>DN</i> is a certificate's subject distinguished name.  
+         * </dl>
+         * @throws ExpansionFailedException - if protocol is other than 
+         * <i>self</i> or <i>alias</i>, or if data resolution failed 
+         */
+        public String resolve(String protocol, String data)
+                throws PolicyUtils.ExpansionFailedException {
+
+            if ("self".equals(protocol)) { //$NON-NLS-1$
+                //need expanding to list of principals in grant clause 
+                if (ge.principals != null && ge.principals.size() != 0) {
+                    StringBuilder sb = new StringBuilder();
+                    for (Iterator<PrincipalEntry> iter = ge.principals.iterator(); iter
+                            .hasNext();) {
+                        DefaultPolicyScanner.PrincipalEntry pr = iter
+                                .next();
+                        if (pr.klass == null) {
+                            // aliased X500Principal
+                            try {
+                                sb.append(pc2str(getPrincipalByAlias(ks,
+                                        pr.name)));
+                            }
+                            catch (Exception e) {
+                                throw new PolicyUtils.ExpansionFailedException(
+                                        Messages.getString("security.143", pr.name), e); //$NON-NLS-1$
+                            }
+                        } else {
+                            sb.append(pr.klass).append(" \"").append(pr.name) //$NON-NLS-1$
+                                    .append("\" "); //$NON-NLS-1$
+                        }
+                    }
+                    return sb.toString();
+                } else {
+                    throw new PolicyUtils.ExpansionFailedException(
+                            Messages.getString("security.144")); //$NON-NLS-1$
+                }
+            }
+            if ("alias".equals(protocol)) { //$NON-NLS-1$
+                try {
+                    return pc2str(getPrincipalByAlias(ks, data));
+                }
+                catch (Exception e) {
+                    throw new PolicyUtils.ExpansionFailedException(
+                            Messages.getString("security.143", data), e); //$NON-NLS-1$
+                }
+            }
+            throw new PolicyUtils.ExpansionFailedException(
+                    Messages.getString("security.145", protocol)); //$NON-NLS-1$
+        }
+
+        // Formats a string describing the passed Principal. 
+        private String pc2str(Principal pc) {
+            String klass = pc.getClass().getName();
+            String name = pc.getName();
+            StringBuilder sb = new StringBuilder(klass.length() + name.length()
+                    + 5);
+            return sb.append(klass).append(" \"").append(name).append("\"") //$NON-NLS-1$ //$NON-NLS-2$
+                    .toString();
+        }
+    }
+
+    /**
+     * Takes a comma-separated list of aliases and obtains corresponding 
+     * certificates.
+     * @param ks KeyStore for resolving Certificates, may be <code>null</code> 
+     * @param signers comma-separated list of certificate aliases, 
+     * must be not <code>null</code>
+     * @return an array of signing Certificates
+     * @throws Exception if KeyStore is <code>null</code> 
+     * or if it failed to provide a certificate  
+     */
+    protected Certificate[] resolveSigners(KeyStore ks, String signers)
+            throws Exception {
+        if (ks == null) {
+            throw new KeyStoreException(Messages.getString("security.146", //$NON-NLS-1$
+                    signers));
+        }
+
+        Collection<Certificate> certs = new HashSet<Certificate>();
+        StringTokenizer snt = new StringTokenizer(signers, ","); //$NON-NLS-1$
+        while (snt.hasMoreTokens()) {
+            //XXX cache found certs ??
+            certs.add(ks.getCertificate(snt.nextToken().trim()));
+        }
+        return certs.toArray(new Certificate[certs.size()]);
+    }
+
+    /**
+     * Returns a subject's X500Principal of an X509Certificate, 
+     * which is associated with the specified keystore alias. 
+     * @param ks KeyStore for resolving Certificate, may be <code>null</code>
+     * @param alias alias to a certificate
+     * @return X500Principal with a subject distinguished name
+     * @throws KeyStoreException if KeyStore is <code>null</code> 
+     * or if it failed to provide a certificate
+     * @throws CertificateException if found certificate is not 
+     * an X509Certificate 
+     */
+    protected Principal getPrincipalByAlias(KeyStore ks, String alias)
+            throws KeyStoreException, CertificateException {
+
+        if (ks == null) {
+            throw new KeyStoreException(
+                    Messages.getString("security.147", alias)); //$NON-NLS-1$
+        }
+        //XXX cache found certs ??
+        Certificate x509 = ks.getCertificate(alias);
+        if (x509 instanceof X509Certificate) {
+            return ((X509Certificate) x509).getSubjectX500Principal();
+        } else {
+            throw new CertificateException(Messages.getString("security.148", //$NON-NLS-1$
+                    alias, x509));
+        }
+    }
+
+    /**
+     * Returns the first successfully loaded KeyStore, from the specified list of
+     * possible locations. This method iterates over the list of KeystoreEntries;
+     * for each entry expands <code>url</code> and <code>type</code>,
+     * tries to construct instances of specified URL and KeyStore and to load 
+     * the keystore. If it is loaded, returns the keystore, otherwise proceeds to 
+     * the next KeystoreEntry. 
+     * <br>
+     * <b>Note:</b> an url may be relative to the policy file location or absolute.
+     * @param keystores list of available KeystoreEntries
+     * @param base the policy file location
+     * @param system system properties, used for property expansion
+     * @param resolve flag enabling/disabling property expansion
+     * @return the first successfully loaded KeyStore or <code>null</code>
+     */
+    protected KeyStore initKeyStore(List<KeystoreEntry>keystores,
+            URL base, Properties system, boolean resolve) {
+
+        for (int i = 0; i < keystores.size(); i++) {
+            try {
+                DefaultPolicyScanner.KeystoreEntry ke = keystores
+                        .get(i);
+                if (resolve) {
+                    ke.url = PolicyUtils.expandURL(ke.url, system);
+                    if (ke.type != null) {
+                        ke.type = PolicyUtils.expand(ke.type, system);
+                    }
+                }
+                if (ke.type == null || ke.type.length() == 0) {
+                    ke.type = KeyStore.getDefaultType();
+                }
+                KeyStore ks = KeyStore.getInstance(ke.type);
+                URL location = new URL(base, ke.url);
+                InputStream is = AccessController
+                        .doPrivileged(new PolicyUtils.URLLoader(location));
+                try {
+                    ks.load(is, null);
+                }
+                finally {
+                    is.close();
+                }
+                return ks;
+            }
+            catch (Exception e) {
+                // TODO: log warning
+            }
+        }
+        return null;
+    }
+}

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

Added: incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/DefaultPolicyScanner.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/DefaultPolicyScanner.java?rev=928394&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/DefaultPolicyScanner.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/DefaultPolicyScanner.java Sun Mar 28 12:57:03 2010
@@ -0,0 +1,519 @@
+/*
+ *  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.
+ */
+
+/**
+* @author Alexey V. Varlamov
+* @version $Revision$
+*/
+
+package org.apache.river.security.policy.util;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StreamTokenizer;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+
+
+/**
+ * This is a basic high-level tokenizer of policy files. It takes in a stream,
+ * analyzes data read from it and returns a set of structured tokens. <br>
+ * This implementation recognizes text files, consisting of clauses with the
+ * following syntax:
+ * 
+ * <pre>
+ * 
+ *     keystore &quot;some_keystore_url&quot;, &quot;keystore_type&quot;;
+ *  
+ * </pre>
+ * <pre>
+ * 
+ *     grant [SignedBy &quot;signer_names&quot;] [, CodeBase &quot;URL&quot;]
+ *      [, Principal [principal_class_name] &quot;principal_name&quot;]
+ *      [, Principal [principal_class_name] &quot;principal_name&quot;] ... {
+ *      permission permission_class_name [ &quot;target_name&quot; ] [, &quot;action&quot;] 
+ *      [, SignedBy &quot;signer_names&quot;];
+ *      permission ...
+ *      };
+ *  
+ * </pre>
+ * 
+ * For semantical details of this format, see the
+ * {@link org.apache.harmony.security.DefaultPolicy default policy description}.
+ * <br>
+ * Keywords are case-insensitive in contrast to quoted string literals.
+ * Comma-separation rule is quite forgiving, most commas may be just omitted.
+ * Whitespaces, line- and block comments are ignored. Symbol-level tokenization
+ * is delegated to java.io.StreamTokenizer. <br>
+ * <br>
+ * This implementation is effectively thread-safe, as it has no field references
+ * to data being processed (that is, passes all the data as method parameters).
+ * 
+ * @see org.apache.harmony.security.fortress.DefaultPolicyParser
+ */
+public class DefaultPolicyScanner {
+
+    /**
+     * Specific exception class to signal policy file syntax error.
+     * 
+     */
+    public static class InvalidFormatException extends Exception {
+
+        /**
+         * @serial
+         */
+        private static final long serialVersionUID = 5789786270390222184L;
+
+        /** 
+         * Constructor with detailed message parameter. 
+         */
+        public InvalidFormatException(String arg0) {
+            super(arg0);
+        }
+    }
+
+    /**
+     * Configures passed tokenizer accordingly to supported syntax.
+     */
+    protected StreamTokenizer configure(StreamTokenizer st) {
+        st.slashSlashComments(true);
+        st.slashStarComments(true);
+        st.wordChars('_', '_');
+        st.wordChars('$', '$');
+        return st;
+    }
+
+    /**
+     * Performs the main parsing loop. Starts with creating and configuring a
+     * StreamTokenizer instance; then tries to recognize <i>keystore </i> or
+     * <i>grant </i> keyword. When found, invokes read method corresponding to
+     * the clause and collects result to the passed collection.
+     * 
+     * @param r
+     *            policy stream reader
+     * @param grantEntries
+     *            a collection to accumulate parsed GrantEntries
+     * @param keystoreEntries
+     *            a collection to accumulate parsed KeystoreEntries
+     * @throws IOException
+     *             if stream reading failed
+     * @throws InvalidFormatException
+     *             if unexpected or unknown token encountered
+     */
+    public void scanStream(Reader r, Collection<GrantEntry> grantEntries,
+            List<KeystoreEntry> keystoreEntries) throws IOException,
+            InvalidFormatException {
+        StreamTokenizer st = configure(new StreamTokenizer(r));
+        //main parsing loop
+        parsing: while (true) {
+            switch (st.nextToken()) {
+            case StreamTokenizer.TT_EOF: //we've done the job
+                break parsing;
+
+            case StreamTokenizer.TT_WORD:
+                if (Util.equalsIgnoreCase("keystore", st.sval)) { //$NON-NLS-1$
+                    keystoreEntries.add(readKeystoreEntry(st));
+                } else if (Util.equalsIgnoreCase("grant", st.sval)) { //$NON-NLS-1$
+                    grantEntries.add(readGrantEntry(st));
+                } else {
+                    handleUnexpectedToken(st, Messages.getString("security.89")); //$NON-NLS-1$
+                }
+                break;
+
+            case ';': //just delimiter of entries
+                break;
+
+            default:
+                handleUnexpectedToken(st);
+                break;
+            }
+        }
+    }
+
+    /**
+     * Tries to read <i>keystore </i> clause fields. The expected syntax is
+     * 
+     * <pre>
+     * 
+     *     &quot;some_keystore_url&quot;[, &quot;keystore_type&quot;];
+     *  
+     * </pre>
+     * 
+     * @return successfully parsed KeystoreEntry
+     * @throws IOException
+     *             if stream reading failed
+     * @throws InvalidFormatException
+     *             if unexpected or unknown token encountered
+     */
+    protected KeystoreEntry readKeystoreEntry(StreamTokenizer st)
+            throws IOException, InvalidFormatException {
+        KeystoreEntry ke = new KeystoreEntry();
+        if (st.nextToken() == '"') {
+            ke.url = st.sval;
+            if ((st.nextToken() == '"')
+                    || ((st.ttype == ',') && (st.nextToken() == '"'))) {
+                ke.type = st.sval;
+            } else { // handle token in the main loop
+                st.pushBack();
+            }
+        } else {
+            handleUnexpectedToken(st, Messages.getString("security.8A")); //$NON-NLS-1$
+        }
+        return ke;
+    }
+
+    /**
+     * Tries to read <i>grant </i> clause. <br>
+     * First, it reads <i>codebase </i>, <i>signedby </i>, <i>principal </i>
+     * entries till the '{' (opening curly brace) symbol. Then it calls
+     * readPermissionEntries() method to read the permissions of this clause.
+     * <br>
+     * Principal entries (if any) are read by invoking readPrincipalEntry()
+     * method, obtained PrincipalEntries are accumulated. <br>
+     * The expected syntax is
+     * 
+     * <pre>
+     * 
+     *     [ [codebase &quot;url&quot;] | [signedby &quot;name1,...,nameN&quot;] | 
+     *          principal ...] ]* { ... }
+     *  
+     * </pre>
+     * 
+     * @return successfully parsed GrantEntry
+     * @throws IOException
+     *             if stream reading failed
+     * @throws InvalidFormatException
+     *             if unexpected or unknown token encountered
+     */
+    protected GrantEntry readGrantEntry(StreamTokenizer st) throws IOException,
+            InvalidFormatException {
+        GrantEntry ge = new GrantEntry();
+        parsing: while (true) {
+            switch (st.nextToken()) {
+
+            case StreamTokenizer.TT_WORD:
+                if (Util.equalsIgnoreCase("signedby", st.sval)) { //$NON-NLS-1$
+                    if (st.nextToken() == '"') {
+                        ge.signers = st.sval;
+                    } else {
+                        handleUnexpectedToken(st, Messages.getString("security.8B")); //$NON-NLS-1$
+                    }
+                } else if (Util.equalsIgnoreCase("codebase", st.sval)) { //$NON-NLS-1$
+                    if (st.nextToken() == '"') {
+                        ge.codebase = st.sval;
+                    } else {
+                        handleUnexpectedToken(st, Messages.getString("security.8C")); //$NON-NLS-1$
+                    }
+                } else if (Util.equalsIgnoreCase("principal", st.sval)) { //$NON-NLS-1$
+                    ge.addPrincipal(readPrincipalEntry(st));
+                } else {
+                    handleUnexpectedToken(st);
+                }
+                break;
+
+            case ',': //just delimiter of entries
+                break;
+
+            case '{':
+                ge.permissions = readPermissionEntries(st);
+                break parsing;
+
+            default: // handle token in the main loop
+                st.pushBack();
+                break parsing;
+            }
+        }
+
+        return ge;
+    }
+
+    /**
+     * Tries to read <i>Principal </i> entry fields. The expected syntax is
+     * 
+     * <pre>
+     * 
+     *     [ principal_class_name ] &quot;principal_name&quot;
+     *  
+     * </pre>
+     * 
+     * Both class and name may be wildcards, wildcard names should not
+     * surrounded by quotes.
+     * 
+     * @return successfully parsed PrincipalEntry
+     * @throws IOException
+     *             if stream reading failed
+     * @throws InvalidFormatException
+     *             if unexpected or unknown token encountered
+     */
+    protected PrincipalEntry readPrincipalEntry(StreamTokenizer st)
+            throws IOException, InvalidFormatException {
+        PrincipalEntry pe = new PrincipalEntry();
+        if (st.nextToken() == StreamTokenizer.TT_WORD) {
+            pe.klass = st.sval;
+            st.nextToken();
+        } else if (st.ttype == '*') {
+            pe.klass = PrincipalEntry.WILDCARD;
+            st.nextToken();
+        }
+        if (st.ttype == '"') {
+            pe.name = st.sval;
+        } else if (st.ttype == '*') {
+            pe.name = PrincipalEntry.WILDCARD;
+        } else {
+            handleUnexpectedToken(st, Messages.getString("security.8D")); //$NON-NLS-1$
+        }
+        return pe;
+    }
+
+    /**
+     * Tries to read a list of <i>permission </i> entries. The expected syntax
+     * is
+     * 
+     * <pre>
+     * 
+     *     permission permission_class_name
+     *          [ &quot;target_name&quot; ] [, &quot;action_list&quot;]
+     *          [, signedby &quot;name1,name2,...&quot;];
+     *  
+     * </pre>
+     * 
+     * List is terminated by '}' (closing curly brace) symbol.
+     * 
+     * @return collection of successfully parsed PermissionEntries
+     * @throws IOException
+     *             if stream reading failed
+     * @throws InvalidFormatException
+     *             if unexpected or unknown token encountered
+     */
+    protected Collection<PermissionEntry> readPermissionEntries(
+            StreamTokenizer st) throws IOException, InvalidFormatException {
+        Collection<PermissionEntry> permissions = new HashSet<PermissionEntry>();
+        parsing: while (true) {
+            switch (st.nextToken()) {
+
+            case StreamTokenizer.TT_WORD:
+                if (Util.equalsIgnoreCase("permission", st.sval)) { //$NON-NLS-1$
+                    PermissionEntry pe = new PermissionEntry();
+                    if (st.nextToken() == StreamTokenizer.TT_WORD) {
+                        pe.klass = st.sval;
+                        if (st.nextToken() == '"') {
+                            pe.name = st.sval;
+                            st.nextToken();
+                        }
+                        if (st.ttype == ',') {
+                            st.nextToken();
+                        }
+                        if (st.ttype == '"') {
+                            pe.actions = st.sval;
+                            if (st.nextToken() == ',') {
+                                st.nextToken();
+                            }
+                        }
+                        if (st.ttype == StreamTokenizer.TT_WORD
+                                && Util.equalsIgnoreCase("signedby", st.sval)) { //$NON-NLS-1$
+                            if (st.nextToken() == '"') {
+                                pe.signers = st.sval;
+                            } else {
+                                handleUnexpectedToken(st);
+                            }
+                        } else { // handle token in the next iteration
+                            st.pushBack();
+                        }
+                        permissions.add(pe);
+                        continue parsing;
+                    }
+                }
+                handleUnexpectedToken(st, Messages.getString("security.8E")); //$NON-NLS-1$
+                break;
+
+            case ';': //just delimiter of entries
+                break;
+
+            case '}': //end of list
+                break parsing;
+
+            default: // invalid token
+                handleUnexpectedToken(st);
+                break;
+            }
+        }
+
+        return permissions;
+    }
+
+    /**
+     * Formats a detailed description of tokenizer status: current token,
+     * current line number, etc.
+     */
+    protected String composeStatus(StreamTokenizer st) {
+        return st.toString();
+    }
+
+    /**
+     * Throws InvalidFormatException with detailed diagnostics.
+     * 
+     * @param st
+     *            a tokenizer holding the erroneous token
+     * @param message
+     *            a user-friendly comment, probably explaining expected syntax.
+     *            Should not be <code>null</code>- use the overloaded
+     *            single-parameter method instead.
+     */
+    protected final void handleUnexpectedToken(StreamTokenizer st,
+            String message) throws InvalidFormatException {
+        throw new InvalidFormatException(Messages.getString("security.8F", //$NON-NLS-1$
+                composeStatus(st), message));
+    }
+
+    /**
+     * Throws InvalidFormatException with error status: which token is
+     * unexpected on which line.
+     * 
+     * @param st
+     *            a tokenizer holding the erroneous token
+     */
+    protected final void handleUnexpectedToken(StreamTokenizer st)
+            throws InvalidFormatException {
+        throw new InvalidFormatException(Messages.getString("security.90", //$NON-NLS-1$
+                composeStatus(st)));
+    }
+
+    /**
+     * Compound token representing <i>keystore </i> clause. See policy format
+     * {@link org.apache.harmony.security.DefaultPolicy description}for details.
+     * 
+     * @see org.apache.harmony.security.fortress.DefaultPolicyParser
+     * @see org.apache.harmony.security.DefaultPolicyScanner
+     */
+    public static class KeystoreEntry {
+
+        /**
+         * The URL part of keystore clause.
+         */
+        public String url;
+
+        /**
+         * The typename part of keystore clause.
+         */
+        public String type;
+    }
+
+    /**
+     * Compound token representing <i>grant </i> clause. See policy format
+     * {@link org.apache.harmony.security.DefaultPolicy description}for details.
+     * 
+     * @see org.apache.harmony.security.fortress.DefaultPolicyParser
+     * @see org.apache.harmony.security.DefaultPolicyScanner
+     */
+    public static class GrantEntry {
+
+        /**
+         * The signers part of grant clause. This is a comma-separated list of
+         * certificate aliases.
+         */
+        public String signers;
+
+        /**
+         * The codebase part of grant clause. This is an URL from which code
+         * originates.
+         */
+        public String codebase;
+
+        /**
+         * Collection of PrincipalEntries of grant clause.
+         */
+        public Collection<PrincipalEntry> principals;
+
+        /**
+         * Collection of PermissionEntries of grant clause.
+         */
+        public Collection<PermissionEntry> permissions;
+
+        /**
+         * Adds specified element to the <code>principals</code> collection.
+         * If collection does not exist yet, creates a new one.
+         */
+        public void addPrincipal(PrincipalEntry pe) {
+            if (principals == null) {
+                principals = new HashSet<PrincipalEntry>();
+            }
+            principals.add(pe);
+        }
+
+    }
+
+    /**
+     * Compound token representing <i>principal </i> entry of a <i>grant </i>
+     * clause. See policy format
+     * {@link org.apache.harmony.security.DefaultPolicy description}for details.
+     * 
+     * @see org.apache.harmony.security.fortress.DefaultPolicyParser
+     * @see org.apache.harmony.security.DefaultPolicyScanner
+     */
+    public static class PrincipalEntry {
+
+        /**
+         * Wildcard value denotes any class and/or any name.
+         * Must be asterisk, for proper general expansion and 
+         * PrivateCredentialsPermission wildcarding
+         */
+        public static final String WILDCARD = "*"; //$NON-NLS-1$
+        
+        /**
+         * The classname part of principal clause.
+         */
+        public String klass;
+
+        /**
+         * The name part of principal clause.
+         */
+        public String name;
+    }
+
+    /**
+     * Compound token representing <i>permission </i> entry of a <i>grant </i>
+     * clause. See policy format
+     * {@link org.apache.harmony.security.DefaultPolicy description}for details.
+     * 
+     * @see org.apache.harmony.security.fortress.DefaultPolicyParser
+     * @see org.apache.harmony.security.DefaultPolicyScanner
+     */
+    public static class PermissionEntry {
+
+        /**
+         * The classname part of permission clause.
+         */
+        public String klass;
+
+        /**
+         * The name part of permission clause.
+         */
+        public String name;
+
+        /**
+         * The actions part of permission clause.
+         */
+        public String actions;
+
+        /**
+         * The signers part of permission clause. This is a comma-separated list
+         * of certificate aliases.
+         */
+        public String signers;
+    }
+}

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

Added: incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/Messages.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/Messages.java?rev=928394&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/Messages.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/Messages.java Sun Mar 28 12:57:03 2010
@@ -0,0 +1,250 @@
+/* 
+ * 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.
+ */
+
+/*
+ * THE FILE HAS BEEN AUTOGENERATED BY MSGTOOL TOOL.
+ * All changes made to this file manually will be overwritten 
+ * if this tool runs again. Better make changes in the template file.
+ */
+
+package org.apache.river.security.policy.util;
+
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+
+
+/**
+ * This class retrieves strings from a resource bundle and returns them,
+ * formatting them with MessageFormat when required.
+ * <p>
+ * It is used by the system classes to provide national language support, by
+ * looking up messages in the <code>
+ *    org.apache.harmony.security.internal.nls.messages
+ * </code>
+ * resource bundle. Note that if this file is not available, or an invalid key
+ * is looked up, or resource bundle support is not available, the key itself
+ * will be returned as the associated message. This means that the <em>KEY</em>
+ * should a reasonable human-readable (english) string.
+ * 
+ */
+public class Messages {
+
+    // ResourceBundle holding the system messages.
+    static private ResourceBundle bundle = null;
+
+    /**
+     * Retrieves a message which has no arguments.
+     * 
+     * @param msg
+     *            String the key to look up.
+     * @return String the message for that key in the system message bundle.
+     */
+    static public String getString(String msg) {
+        if (bundle == null) {
+            return msg;
+        }
+        try {
+            return bundle.getString(msg);
+        } catch (MissingResourceException e) {
+            return "Missing message: " + msg; //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Retrieves a message which takes 1 argument.
+     * 
+     * @param msg
+     *            String the key to look up.
+     * @param arg
+     *            Object the object to insert in the formatted output.
+     * @return String the message for that key in the system message bundle.
+     */
+    static public String getString(String msg, Object arg) {
+        return getString(msg, new Object[] { arg });
+    }
+
+    /**
+     * Retrieves a message which takes 1 integer argument.
+     * 
+     * @param msg
+     *            String the key to look up.
+     * @param arg
+     *            int the integer to insert in the formatted output.
+     * @return String the message for that key in the system message bundle.
+     */
+    static public String getString(String msg, int arg) {
+        return getString(msg, new Object[] { Integer.toString(arg) });
+    }
+
+    /**
+     * Retrieves a message which takes 1 character argument.
+     * 
+     * @param msg
+     *            String the key to look up.
+     * @param arg
+     *            char the character to insert in the formatted output.
+     * @return String the message for that key in the system message bundle.
+     */
+    static public String getString(String msg, char arg) {
+        return getString(msg, new Object[] { String.valueOf(arg) });
+    }
+
+    /**
+     * Retrieves a message which takes 2 arguments.
+     * 
+     * @param msg
+     *            String the key to look up.
+     * @param arg1
+     *            Object an object to insert in the formatted output.
+     * @param arg2
+     *            Object another object to insert in the formatted output.
+     * @return String the message for that key in the system message bundle.
+     */
+    static public String getString(String msg, Object arg1, Object arg2) {
+        return getString(msg, new Object[] { arg1, arg2 });
+    }
+
+    /**
+     * Retrieves a message which takes several arguments.
+     * 
+     * @param msg
+     *            String the key to look up.
+     * @param args
+     *            Object[] the objects to insert in the formatted output.
+     * @return String the message for that key in the system message bundle.
+     */
+    static public String getString(String msg, Object[] args) {
+        String format = msg;
+
+        if (bundle != null) {
+            try {
+                format = bundle.getString(msg);
+            } catch (MissingResourceException e) {
+            }
+        }
+
+        return format(format, args);
+    }
+    
+    /**
+     * Generates a formatted text string given a source string containing
+     * "argument markers" of the form "{argNum}" where each argNum must be in
+     * the range 0..9. The result is generated by inserting the toString of each
+     * argument into the position indicated in the string.
+     * <p>
+     * To insert the "{" character into the output, use a single backslash
+     * character to escape it (i.e. "\{"). The "}" character does not need to be
+     * escaped.
+     * 
+     * @param format
+     *            String the format to use when printing.
+     * @param args
+     *            Object[] the arguments to use.
+     * @return String the formatted message.
+     */
+    public static String format(String format, Object[] args) {
+        StringBuilder answer = new StringBuilder(format.length()
+                + (args.length * 20));
+        String[] argStrings = new String[args.length];
+        for (int i = 0; i < args.length; ++i) {
+            if (args[i] == null) {
+                argStrings[i] = "<null>"; 
+            }	//$NON-NLS-1$
+            else {
+                argStrings[i] = args[i].toString();
+            }
+        }
+        int lastI = 0;
+        for (int i = format.indexOf('{', 0); i >= 0; i = format.indexOf('{',
+                lastI)) {
+            if (i != 0 && format.charAt(i - 1) == '\\') {
+                // It's escaped, just print and loop.
+                if (i != 1) {
+                    answer.append(format.substring(lastI, i - 1));
+                }
+                answer.append('{');
+                lastI = i + 1;
+            } else {
+                // It's a format character.
+                if (i > format.length() - 3) {
+                    // Bad format, just print and loop.
+                    answer.append(format.substring(lastI, format.length()));
+                    lastI = format.length();
+                } else {
+                    int argnum = (byte) Character.digit(format.charAt(i + 1),
+                            10);
+                    if (argnum < 0 || format.charAt(i + 2) != '}') {
+                        // Bad format, just print and loop.
+						answer.append(format.substring(lastI, i + 1));
+						lastI = i + 1;
+                    } else {
+                        // Got a good one!
+                        answer.append(format.substring(lastI, i));
+                        if (argnum >= argStrings.length) {
+                            answer.append("<missing argument>"); 
+                        }	//$NON-NLS-1$
+                        else {
+                            answer.append(argStrings[argnum]);
+                        }
+						lastI = i + 3;
+                    }
+                }
+            }
+        }
+        if (lastI < format.length()) {
+            answer.append(format.substring(lastI, format.length()));
+        }
+        return answer.toString();
+    }
+
+    /**
+     * Changes the locale of the messages.
+     * 
+     * @param locale
+     *            Locale the locale to change to.
+     */
+    static public ResourceBundle setLocale(final Locale locale,
+            final String resource) {
+        try {
+            final ClassLoader loader = null;
+            return (ResourceBundle) AccessController
+                    .doPrivileged(new PrivilegedAction<Object>() {
+                        public Object run() {
+                            return ResourceBundle.getBundle(resource, locale,
+                                    loader != null ? loader : ClassLoader.getSystemClassLoader());
+                        }
+                    });
+        } catch (MissingResourceException e) {
+        }
+        return null;
+    }
+
+    static {
+        // Attempt to load the messages.
+        try {
+            bundle = setLocale(Locale.getDefault(),
+                    "org.apache.harmony.security.internal.nls.messages"); //$NON-NLS-1$
+        } catch (Throwable e) {
+            e.printStackTrace();
+        }
+    }
+}

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

Added: incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/NullPolicyParser.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/NullPolicyParser.java?rev=928394&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/NullPolicyParser.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/NullPolicyParser.java Sun Mar 28 12:57:03 2010
@@ -0,0 +1,24 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.apache.river.security.policy.util;
+
+import java.net.URL;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Properties;
+
+/**
+ * A null PolicyParser.
+ * Just in case you don't want to utilise any policy files, for whatever reason.
+ * @author Peter Firmstone.
+ */
+public class NullPolicyParser implements PolicyParser{
+
+    public Collection<PolicyEntry> parse(URL location, Properties system) throws Exception {
+        return new HashSet<PolicyEntry>();
+    }
+
+}

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

Added: incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/PolicyEntry.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/PolicyEntry.java?rev=928394&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/PolicyEntry.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/PolicyEntry.java Sun Mar 28 12:57:03 2010
@@ -0,0 +1,163 @@
+/*
+ *  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.
+ */
+
+/**
+* @author Alexey V. Varlamov
+* @author Peter Firmstone.
+* @version $Revision$
+*/
+
+package org.apache.river.security.policy.util;
+
+import java.net.URL;
+import java.security.CodeSigner;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+
+/**
+ * This class represents an elementary block of a security policy. It associates
+ * a CodeSource of an executable code, Principals allowed to execute the code,
+ * and a set of granted Permissions.
+ * 
+ * Immutable
+ * 
+ * 
+ */
+public class PolicyEntry {
+
+    // Store CodeSource
+    private final CodeSource cs;
+
+    // Array of principals 
+    private final List<Principal> principals;
+
+    // Permissions collection
+    private final Collection<Permission> permissions;
+    
+    private transient final int hashcode;
+
+    /**
+     * Constructor with initialization parameters. Passed collections are not
+     * referenced directly, but copied.
+     */
+    public PolicyEntry(CodeSource cs, Collection<? extends Principal> prs,
+            Collection<? extends Permission> permissions) {
+        this.cs = (cs != null) ? normalizeCodeSource(cs) : null;
+        if ( prs == null || prs.isEmpty()) {
+            this.principals = null;
+        }else{
+            this.principals = new ArrayList<Principal>();
+            this.principals.addAll(prs);
+        }
+        if (permissions == null || permissions.isEmpty()) {
+            this.permissions = null;
+        }else{
+            Set<Permission> perm = new HashSet<Permission>();
+            perm.addAll(permissions);
+            this.permissions = Collections.unmodifiableCollection(perm);
+        }
+        /* Effectively immutable, this will make any hash this is contained in perform.
+         * May need to consider Serializable for this class yet, we'll see.
+         */ 
+        hashcode = (this.cs.hashCode() + principals.hashCode() + this.permissions.hashCode())/3;
+    }
+
+    /**
+     * Checks if passed CodeSource matches this PolicyEntry. Null CodeSource of
+     * PolicyEntry implies any CodeSource; non-null CodeSource forwards to its
+     * imply() method.
+     */
+    public boolean impliesCodeSource(CodeSource codeSource) {
+        if (cs == null) {
+            return true;
+        }
+
+        if (codeSource == null) {
+            return false;
+        }
+        return cs.implies(normalizeCodeSource(codeSource));
+    }
+
+    private CodeSource normalizeCodeSource(CodeSource codeSource) {
+        URL codeSourceURL = PolicyUtils.normalizeURL(codeSource.getLocation());
+        CodeSource result = codeSource;
+
+        if (codeSourceURL != codeSource.getLocation()) {
+            // URL was normalized - recreate codeSource with new URL
+            CodeSigner[] signers = codeSource.getCodeSigners();
+            if (signers == null) {
+                result = new CodeSource(codeSourceURL, codeSource
+                        .getCertificates());
+            } else {
+                result = new CodeSource(codeSourceURL, signers);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Checks if specified Principals match this PolicyEntry. Null or empty set
+     * of Principals of PolicyEntry implies any Principals; otherwise specified
+     * array must contain all Principals of this PolicyEntry.
+     */
+    public boolean impliesPrincipals(Principal[] prs) {
+       // return PolicyUtils.matchSubset(principals, prs);
+        if ( principals == null || principals.isEmpty()) return true;
+        if ( prs == null || prs.length == 0 ) return false;
+        List<Principal> princp = Arrays.asList(prs);
+        return princp.containsAll(principals);      
+    }
+
+    /**
+     * Returns unmodifiable collection of permissions defined by this
+     * PolicyEntry, may be <code>null</code>.
+     */
+    public Collection<Permission> getPermissions() {
+        return permissions;
+    }
+
+    /**
+     * Returns true if this PolicyEntry defines no Permissions, false otherwise.
+     */
+    public boolean isVoid() {
+        return permissions == null || permissions.size() == 0;
+    }
+    
+    @Override
+    public boolean equals(Object o){
+        if (this == o) return true;
+        if ( !(o instanceof PolicyEntry)) return false;
+        PolicyEntry pe = (PolicyEntry) o;
+        if (cs.equals(pe.cs) && principals.equals(pe.principals) 
+                && permissions.equals(pe.permissions) ) return true;
+        return false;
+    }
+    
+    @Override
+    public int hashCode(){
+        return hashcode;        
+    }
+}

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

Added: incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/PolicyParser.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/PolicyParser.java?rev=928394&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/PolicyParser.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/PolicyParser.java Sun Mar 28 12:57:03 2010
@@ -0,0 +1,35 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.apache.river.security.policy.util;
+
+import java.net.URL;
+import java.util.Collection;
+import java.util.Properties;
+
+/**
+ *
+ * @author peter
+ */
+public interface PolicyParser {
+
+    /**
+     * This is the main business method. It manages loading process as follows:
+     * the associated scanner is used to parse the stream to a set of
+     * {@link org.apache.harmony.security.DefaultPolicyScanner.GrantEntry composite tokens},
+     * then this set is iterated and each token is translated to a PolicyEntry.
+     * Semantically invalid tokens are ignored, the same as void PolicyEntries.
+     * <br>
+     * A policy file may refer to some KeyStore(s), and in this case the first
+     * valid reference is initialized and used in processing tokens.
+     *
+     * @param location an URL of a policy file to be loaded
+     * @param system system properties, used for property expansion
+     * @return a collection of PolicyEntry objects, may be empty
+     * @throws Exception IO error while reading location or file syntax error
+     */
+    Collection<PolicyEntry> parse(URL location, Properties system) throws Exception;
+
+}

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

Added: incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/PolicyUtils.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/PolicyUtils.java?rev=928394&view=auto
==============================================================================
--- incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/PolicyUtils.java (added)
+++ incubator/river/jtsk/trunk/src/org/apache/river/security/policy/util/PolicyUtils.java Sun Mar 28 12:57:03 2010
@@ -0,0 +1,663 @@
+/*
+ *  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.
+ */
+
+/**
+* @author Alexey V. Varlamov
+ * @author Peter Firmstone
+* @version $Revision$
+*/
+
+package org.apache.river.security.policy.util;
+
+import java.io.File;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedExceptionAction;
+import java.security.Security;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.river.security.concurrent.ConcurrentPermissions;
+
+/**
+ * This class consist of a number of static methods, which provide a common functionality 
+ * for various policy and configuration providers. 
+ * 
+ */
+public class PolicyUtils {
+
+    // No reason to instantiate
+    private PolicyUtils() {}
+    
+    /**
+     * Auxiliary action for opening InputStream from specified location.
+     */
+    public static class URLLoader implements PrivilegedExceptionAction<InputStream> {
+
+        /** 
+         * URL of target location. 
+         */
+        public URL location;
+
+        /**
+         *  Constructor with target URL parameter. 
+         */
+        public URLLoader(URL location) {
+            this.location = location;
+        }
+
+        /** 
+         * Returns InputStream from the target URL.
+         */
+        public InputStream run() throws Exception {
+            return location.openStream();
+        }
+    }
+
+    /** 
+     * Auxiliary action for accessing system properties in a bundle. 
+     */
+    public static class SystemKit implements PrivilegedAction<Properties> {
+
+        /** 
+         * Returns system properties.
+         */
+        public Properties run() {
+            return System.getProperties();
+        }
+    }
+
+    /** 
+     * Auxiliary action for accessing specific system property. 
+     */
+    public static class SystemPropertyAccessor implements PrivilegedAction<String> {
+
+        /** 
+         * A key of a required system property.
+         */
+        public String key;
+
+        /** 
+         * Constructor with a property key parameter. 
+         */
+        public SystemPropertyAccessor(String key) {
+            this.key = key;
+        }
+
+        /** 
+         * Handy one-line replacement of 
+         * &quot;provide key and supply action&quot; code block, 
+         * for reusing existing action instance. 
+         */
+        public PrivilegedAction<String> key(String key) {
+            this.key = key;
+            return this;
+        }
+
+        /** 
+         * Returns specified system property. 
+         */
+        public String run() {
+            return System.getProperty(key);
+        }
+    }
+
+    /** 
+     * Auxiliary action for accessing specific security property. 
+     */
+    public static class SecurityPropertyAccessor implements PrivilegedAction<String> {
+
+        private String key;
+        
+        /** 
+         * Constructor with a property key parameter. 
+         */
+        public SecurityPropertyAccessor(String key) {
+            super();
+            this.key = key;
+        }
+
+        public PrivilegedAction<String> key(String key) {
+            this.key = key;
+            return this;
+        }
+        
+        /** 
+         * Returns specified security property. 
+         */
+        public String run() {
+            return Security.getProperty(key);
+        }
+    }
+    
+    /** 
+     * Auxiliary action for loading a provider by specific security property.
+     */
+    public static class ProviderLoader<T> implements PrivilegedAction<T> {
+
+        private String key;
+        
+        /**
+         * Acceptable provider superclass.
+         */
+        private Class<T> expectedType;
+        
+        /** 
+         * Constructor taking property key and acceptable provider 
+         * superclass parameters.
+         */
+        public ProviderLoader(String key, Class<T> expected) {
+            super();
+            this.key = key;
+            this.expectedType = expected;
+        }
+
+        /** 
+         * Returns provider instance by specified security property.
+         * The <code>key</code> should map to a fully qualified classname.
+         * 
+         * @throws SecurityException if no value specified for the key 
+         * in security properties or if an Exception has occurred 
+         * during classloading and instantiating.
+         */
+        public T run() {
+            String klassName = Security.getProperty(key);
+            if (klassName == null || klassName.length() == 0) {
+                throw new SecurityException(Messages.getString("security.14C", //$NON-NLS-1$
+                                            key));
+            }
+            // TODO accurate classloading
+            try {
+                Class<?> klass = Class.forName(klassName, true,
+                        Thread.currentThread().getContextClassLoader());
+                if (expectedType != null && klass.isAssignableFrom(expectedType)){
+                    throw new SecurityException(Messages.getString("security.14D", //$NON-NLS-1$
+                                              klassName, expectedType.getName()));
+                }
+                //FIXME expectedType.cast(klass.newInstance());
+                return (T)klass.newInstance();
+            }
+            catch (SecurityException se){
+                throw se;
+            }
+            catch (Exception e) {
+                // TODO log error ??
+                SecurityException se = new SecurityException(
+                        Messages.getString("security.14E", klassName)); //$NON-NLS-1$
+                se.initCause(e);
+                throw se;
+            }
+        }
+    }
+
+    /** 
+     * Specific exception to signal that property expansion failed 
+     * due to unknown key. 
+     */
+    public static class ExpansionFailedException extends Exception {
+
+        /**
+         * @serial
+         */
+        private static final long serialVersionUID = 2869748055182612000L;
+
+        /** 
+         * Constructor with user-friendly message parameter. 
+         */
+        public ExpansionFailedException(String message) {
+            super(message);
+        }
+
+        /** 
+         * Constructor with user-friendly message and causing error. 
+         */
+        public ExpansionFailedException(String message, Throwable cause) {
+            super(message, cause);
+        }
+    }
+
+    /**
+     * Substitutes all entries like ${some.key}, found in specified string, 
+     * for specified values.
+     * If some key is unknown, throws ExpansionFailedException. 
+     * @param str the string to be expanded
+     * @param properties available key-value mappings 
+     * @return expanded string
+     * @throws ExpansionFailedException
+     */
+    public static String expand(String str, Properties properties)
+            throws ExpansionFailedException {
+        final String START_MARK = "${"; //$NON-NLS-1$
+        final String END_MARK = "}"; //$NON-NLS-1$
+        final int START_OFFSET = START_MARK.length();
+        final int END_OFFSET = END_MARK.length();
+
+        StringBuilder result = new StringBuilder(str);
+        int start = result.indexOf(START_MARK);
+        while (start >= 0) {
+            int end = result.indexOf(END_MARK, start);
+            if (end >= 0) {
+                String key = result.substring(start + START_OFFSET, end);
+                String value = properties.getProperty(key);
+                if (value != null) {
+                    result.replace(start, end + END_OFFSET, value);
+                    start += value.length();
+                } else {
+                    throw new ExpansionFailedException(Messages.getString("security.14F", key)); //$NON-NLS-1$
+                }
+            }
+            start = result.indexOf(START_MARK, start);
+        }
+        return result.toString();
+    }
+
+    /**
+     * Handy shortcut for 
+     * <code>expand(str, properties).replace(File.separatorChar, '/')</code>.
+     * @see #expand(String, Properties)
+     */
+    public static String expandURL(String str, Properties properties)
+            throws ExpansionFailedException {
+        return expand(str, properties).replace(File.separatorChar, '/');
+    }
+    
+    /**
+     * Normalizes URLs to standard ones, eliminating pathname symbols.
+     * 
+     * @param codebase -
+     *            the original URL.
+     * @return - the normalized URL.
+     */
+    public static URL normalizeURL(URL codebase) {
+        if (codebase != null && "file".equals(codebase.getProtocol())) { //$NON-NLS-1$
+            try {
+                if (codebase.getHost().length() == 0) {
+                    String path = codebase.getFile();
+
+                    if (path.length() == 0) {
+                        // codebase is "file:"
+                        path = "*";
+                    }
+                    return filePathToURI(new File(path)
+                            .getAbsolutePath()).normalize().toURL();
+                } else {
+                    // codebase is "file://<smth>"
+                    return codebase.toURI().normalize().toURL();
+                }
+            } catch (Exception e) {
+                // Ignore
+            }
+        }
+        return codebase;
+    }
+
+    /**
+     * Converts a file path to URI without accessing file system
+     * (like {File#toURI()} does).
+     * 
+     * @param path -
+     *            file path.
+     * @return - the resulting URI.
+     * @throw URISyntaxException
+     */
+    public static URI filePathToURI(String path) throws URISyntaxException {
+        path = path.replace(File.separatorChar, '/');
+
+        if (!path.startsWith("/")) { //$NON-NLS-1$
+            return new URI("file", null, //$NON-NLS-1$
+                    new StringBuilder(path.length() + 1).append('/')
+                            .append(path).toString(), null, null);
+        }
+        return new URI("file", null, path, null, null); //$NON-NLS-1$
+    }
+
+    /**
+     * Instances of this interface are intended for resolving  
+     * generalized expansion expressions, of the form ${{protocol:data}}. 
+     * Such functionality is applicable to security policy files, for example.
+     * @see org.apache.harmony.security.PolicyUtils#expandGeneral(String, GeneralExpansionHandler)
+     */
+    public static interface GeneralExpansionHandler {
+
+        /**
+         * Resolves general expansion expressions of the form ${{protocol:data}}.
+         * @param protocol denotes type of resolution
+         * @param data data to be resolved, optional (may be null)
+         * @return resolved value, must not be null
+         * @throws PolicyUtils.ExpansionFailedException if expansion is impossible
+         */
+        String resolve(String protocol, String data)
+                throws ExpansionFailedException;
+    }
+
+    /**
+     * Substitutes all entries like ${{protocol:data}}, found in specified string, 
+     * for values resolved by passed handler.
+     * The data part may be empty, and in this case expression 
+     * may have simplified form, as ${{protocol}}.
+     * If some entry cannot be resolved, throws ExpansionFailedException;
+     * @param str the string to be expanded
+     * @param handler the handler to resolve data denoted by protocol  
+     * @return expanded string
+     * @throws ExpansionFailedException
+     */
+    public static String expandGeneral(String str,
+            GeneralExpansionHandler handler) throws ExpansionFailedException {
+        final String START_MARK = "${{"; //$NON-NLS-1$
+        final String END_MARK = "}}"; //$NON-NLS-1$
+        final int START_OFFSET = START_MARK.length();
+        final int END_OFFSET = END_MARK.length();
+
+        StringBuilder result = new StringBuilder(str);
+        int start = result.indexOf(START_MARK);
+        while (start >= 0) {
+            int end = result.indexOf(END_MARK, start);
+            if (end >= 0) {
+                String key = result.substring(start + START_OFFSET, end);
+                int separator = key.indexOf(':');
+                String protocol = (separator >= 0) ? key
+                        .substring(0, separator) : key;
+                String data = (separator >= 0) ? key.substring(separator + 1)
+                        : null;
+                String value = handler.resolve(protocol, data);
+                result.replace(start, end + END_OFFSET, value);
+                start += value.length();
+            }
+            start = result.indexOf(START_MARK, start);
+        }
+        return result.toString();
+    }
+
+    /** 
+     * A key to security properties, deciding whether usage of 
+     * dynamic policy location via system properties is allowed. 
+     * @see #getPolicyURLs(Properties, String, String)
+     */
+    public static final String POLICY_ALLOW_DYNAMIC = "policy.allowSystemProperty"; //$NON-NLS-1$
+
+    /** 
+     * A key to security properties, deciding whether expansion of 
+     * system properties is allowed 
+     * (in security properties values, policy files, etc).
+     * @see #expand(String, Properties) 
+     */
+    public static final String POLICY_EXPAND = "policy.expandProperties"; //$NON-NLS-1$
+
+    /** 
+     * Positive value of switching properties.
+     */
+    public static final String TRUE = "true"; //$NON-NLS-1$
+
+    /** 
+     * Negative value of switching properties.
+     */
+    public static final String FALSE = "false"; //$NON-NLS-1$
+
+    /** 
+     * Returns false if current security settings disable to perform 
+     * properties expansion, true otherwise.
+     * @see #expand(String, Properties)  
+     */
+    public static boolean canExpandProperties() {
+        return !Util.equalsIgnoreCase(FALSE,AccessController
+                .doPrivileged(new SecurityPropertyAccessor(POLICY_EXPAND)));
+    }
+
+    /**
+     * Obtains a list of locations for a policy or configuration provider.
+     * The search algorithm is as follows:
+     * <ol>
+     * <li> Look in security properties for keys of form <code>prefix + n</code>, 
+     * where <i>n</i> is an integer and <i>prefix</i> is a passed parameter. 
+     * Sequence starts with <code>n=1</code>, and keeps incrementing <i>n</i> 
+     * until next key is not found. <br> 
+     * For each obtained key, try to construct an URL instance. On success, 
+     * add the URL to the list; otherwise ignore it.
+     * <li>
+     *         If security settings do not prohibit (through 
+     *         {@link #POLICY_ALLOW_DYNAMIC the &quot;policy.allowSystemProperty&quot; property}) 
+     *         to use additional policy location, read the system property under the 
+     *         passed key parameter. If property exists, it may designate a file or 
+     *         an absolute URL. Thus, first check if there is a file with that name, 
+     *         and if so, convert the pathname to URL. Otherwise, try to instantiate   
+     *         an URL directly. If succeeded, append the URL to the list 
+     * <li>
+     *         If the additional location from the step above was specified to the 
+     *         system via &quot;==&quot; (i.e. starts with '='), discard all URLs above 
+     *         and use this only URL.
+     * </ol> 
+     * <b>Note:</b> all property values (both security and system) related to URLs are
+     * subject to {@link #expand(String, Properties) property expansion}, regardless 
+     * of the &quot;policy.expandProperties&quot; security setting.  
+     * 
+     * @param system system properties
+     * @param systemUrlKey key to additional policy location
+     * @param securityUrlPrefix prefix to numbered locations in security properties
+     * @return array of URLs to provider's configuration files, may be empty.
+     */
+    public static URL[] getPolicyURLs(final Properties system,
+            final String systemUrlKey, final String securityUrlPrefix) {
+
+        final SecurityPropertyAccessor security = new SecurityPropertyAccessor(
+                null);
+        final List<URL> urls = new ArrayList<URL>();
+        boolean dynamicOnly = false;
+        URL dynamicURL = null;
+
+        //first check if policy is set via system properties
+        if (!Util.equalsIgnoreCase(FALSE, AccessController
+                .doPrivileged(security.key(POLICY_ALLOW_DYNAMIC)))) {
+            String location = system.getProperty(systemUrlKey);
+            if (location != null) {
+                if (location.startsWith("=")) { //$NON-NLS-1$
+                    //overrides all other urls
+                    dynamicOnly = true;
+                    location = location.substring(1);
+                }
+                try {
+                    location = expandURL(location, system);
+                    // location can be a file, but we need an url...
+                    final File f = new File(location);
+                    dynamicURL = AccessController
+                            .doPrivileged(new PrivilegedExceptionAction<URL>() {
+
+                                public URL run() throws Exception {
+                                    if (f.exists()) {
+                                        return f.toURI().toURL();
+                                    } else {
+                                        return null;
+                                    }
+                                }
+                            });
+                    if (dynamicURL == null) {
+                        dynamicURL = new URL(location);
+                    }
+                }
+                catch (Exception e) {
+                    // TODO: log error
+                    // System.err.println("Error detecting system policy location: "+e);
+                }
+            }
+        }
+        //next read urls from security.properties 
+        if (!dynamicOnly) {
+            int i = 1;
+            while (true) {
+                String location = AccessController
+                        .doPrivileged(security.key(new StringBuilder(
+                                securityUrlPrefix).append(i++).toString()));
+                if (location == null) {
+                    break;
+                }
+                try {
+                    location = expandURL(location, system);
+                    URL anURL = new URL(location);
+                    if (anURL != null) {
+                        urls.add(anURL);
+                    }
+                }
+                catch (Exception e) {
+                    // TODO: log error
+                    // System.err.println("Error detecting security policy location: "+e);
+                }
+            }
+        }
+        if (dynamicURL != null) {
+            urls.add(dynamicURL);
+        }
+        return urls.toArray(new URL[urls.size()]);
+    }
+
+    /** 
+     * Converts common-purpose collection of Permissions to PermissionCollection.
+     *
+     * @param perms a collection containing arbitrary permissions, may be null
+     * @return mutable heterogeneous PermissionCollection containing all Permissions 
+     * from the specified collection
+     */
+    public static PermissionCollection 
+            toPermissionCollection(Collection<Permission> perms) {
+        PermissionCollection pc = new ConcurrentPermissions();
+        if (perms != null) {
+            for (Iterator<Permission> iter = perms.iterator(); iter.hasNext();) {
+                Permission element = iter.next();
+                pc.add(element);
+            }
+        }
+        return pc;
+    }
+    
+    /** 
+     * Converts common-purpose homegeneous or heterogeneous PermissionCollection 
+     * to a hetergeneous PermissionCollection.
+     *
+     * @param perms a PermissionCollection containing arbitrary permissions.
+     * @return mutable heterogeneous PermissionCollection containing all Permissions 
+     * from the specified PermissionCollection
+     */
+    public static PermissionCollection 
+            toConcurrentPermissions(PermissionCollection perms) {
+        if (perms instanceof ConcurrentPermissions) return perms;
+        PermissionCollection pc = new ConcurrentPermissions();
+        if (perms != null) {
+            Enumeration<Permission> iter = perms.elements();
+            while ( iter.hasMoreElements() ) {
+                Permission element = iter.nextElement();
+                pc.add(element);
+            }
+        }
+        return pc;
+    }
+
+    // Empty set of arguments to default constructor of a Permission.
+    private static final Class[] NO_ARGS = {};
+
+    // One-arg set of arguments to default constructor of a Permission.
+    private static final Class[] ONE_ARGS = { String.class };
+
+    // Two-args set of arguments to default constructor of a Permission.
+    private static final Class[] TWO_ARGS = { String.class, String.class };
+
+    /**
+     * Tries to find a suitable constructor and instantiate a new Permission
+     * with specified parameters.  
+     *
+     * @param targetType class of expected Permission instance
+     * @param targetName name of expected Permission instance
+     * @param targetActions actions of expected Permission instance
+     * @return a new Permission instance
+     * @throws IllegalArgumentException if no suitable constructor found
+     * @throws Exception any exception thrown by Constructor.newInstance()
+     */
+    public static Permission instantiatePermission(Class<?> targetType,
+            String targetName, String targetActions) throws Exception {
+
+        // let's guess the best order for trying constructors
+        Class[][] argTypes = null;
+        Object[][] args = null;
+        if (targetActions != null) {
+            argTypes = new Class[][] { TWO_ARGS, ONE_ARGS, NO_ARGS };
+            args = new Object[][] { { targetName, targetActions },
+                    { targetName }, {} };
+        } else if (targetName != null) {
+            argTypes = new Class[][] { ONE_ARGS, TWO_ARGS, NO_ARGS };
+            args = new Object[][] { { targetName },
+                    { targetName, targetActions }, {} };
+        } else {
+            argTypes = new Class[][] { NO_ARGS, ONE_ARGS, TWO_ARGS };
+            args = new Object[][] { {}, { targetName },
+                    { targetName, targetActions } };
+        }
+
+        // finally try to instantiate actual permission
+        for (int i = 0; i < argTypes.length; i++) {
+            try {
+                Constructor<?> ctor = targetType.getConstructor(argTypes[i]);
+                return (Permission)ctor.newInstance(args[i]);
+            }
+            catch (NoSuchMethodException ignore) {}
+        }
+        throw new IllegalArgumentException(
+                Messages.getString("security.150", targetType));//$NON-NLS-1$
+    }
+
+    /**
+     * Checks whether the objects from <code>what</code> array are all
+     * presented in <code>where</code> array.
+     * 
+     * @param what first array, may be <code>null</code> 
+     * @param where  second array, may be <code>null</code>
+     * @return <code>true</code> if the first array is <code>null</code>
+     * or if each and every object (ignoring null values) 
+     * from the first array has a twin in the second array; <code>false</code> otherwise
+     */
+    public static boolean matchSubset(Object[] what, Object[] where) {
+        if (what == null) {
+            return true;
+        }
+
+        for (int i = 0; i < what.length; i++) {
+            if (what[i] != null) {
+                if (where == null) {
+                    return false;
+                }
+                boolean found = false;
+                for (int j = 0; j < where.length; j++) {
+                    if (what[i].equals(where[j])) {
+                        found = true;
+                        break;
+                    }
+                }
+                if (!found) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+}

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