net.unicon.alchemist.access.AccessBroker.java Source code

Java tutorial

Introduction

Here is the source code for net.unicon.alchemist.access.AccessBroker.java

Source

/*
 * Copyright (C) 2007 Unicon, Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this distribution.  It is also available here:
 * http://www.fsf.org/licensing/licenses/gpl.html
 *
 * As a special exception to the terms and conditions of version
 * 2 of the GPL, you may redistribute this Program in connection
 * with Free/Libre and Open Source Software ("FLOSS") applications
 * as described in the GPL FLOSS exception.  You should have received
 * a copy of the text describing the FLOSS exception along with this
 * distribution.
 */

package net.unicon.alchemist.access;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.dom4j.Attribute;
import org.dom4j.Element;

/**
 * Controls access over a collection of arbitrary entiries.  Implementations of
 * <code>AccessBroker</code> must provide a static <code>parse</code> method
 * that returns an <code>AccessBroker</code> instance based on XML configuration
 * data contained in a dom4j <code>Element</code>.
 */
public abstract class AccessBroker {

    // Static Members.
    private static Map instances = Collections.synchronizedMap(new HashMap());

    // Instance Members.
    private String handle;

    /*
     * Public API.
     */

    /**
     * Provides the broker defined by the specified <code>Element</code>.
     * First, this method checks the in-memory cache of brokers for one with the
     * handle indicated within the XML.  If an instance is not found, this
     * method attempts to create a new broker instance based on the
     * specifications within the XML.  If successful, this method stores the new
     * instance within the in-memory cache.
     *
     * @param e XML that defines the desired broker.
     * @return A broker with the characteristics specified in the XML.
     */
    public static AccessBroker parse(Element e) {

        // Assertions.
        if (e == null) {
            String msg = "Argument 'e [Element]' cannot be null.";
            throw new IllegalArgumentException(msg);
        }
        if (!e.getName().equals("access-broker")) {
            String msg = "Argument 'e [Element]' must be an <access-broker> " + "element.";
            throw new IllegalArgumentException(msg);
        }

        AccessBroker rslt = null;

        // Call parse() in the specified implementation.
        Attribute impl = e.attribute("impl");
        if (impl == null) {
            String msg = "Element <access-broker> is missing required " + "attribute 'impl'.";
            throw new IllegalArgumentException(msg);
        }
        try {
            Class c = Class.forName(impl.getValue());
            Method m = c.getDeclaredMethod("parse", new Class[] { Element.class });
            rslt = (AccessBroker) m.invoke(null, new Object[] { e });
        } catch (Throwable t) {
            String msg = "Unable to create the specified broker:  " + impl.getValue();
            throw new RuntimeException(msg, t);
        }

        return rslt;

    }

    /**
     * Provides the broker instance with the specified handle.  Throws
     * <code>IllegalArgumentException</code> if there is no broker with the
     * specified handle.
     *
     * @param handle The unique handle of the desired broker.
     * @return The broker with the specified handle.
     */
    public static AccessBroker getInstance(String handle) {

        // Assertions.
        if (handle == null) {
            String msg = "Argument 'handle' cannot be null.";
            throw new IllegalArgumentException(msg);
        }
        if (!instances.containsKey(handle)) {
            String msg = "Broker instance not found:  " + handle;
            throw new IllegalArgumentException(msg);
        }

        return (AccessBroker) instances.get(handle);

    }

    /**
     * Provides the handle for this broker instance.  Broker handles are unique
     * within the JVM.
     *
     * @return A handle uniquely identifying this broker instance.
     */
    public final String getHandle() {
        return handle;
    }

    /**
     * Sets the specified identity's access to the specified access rules for the
     * specified target.  This method will update an existing entry if possible
     * or create a new one if necessary.
     *
     * @param y The identity receiving access.
     * @param rules The level(s) of access being granted/denied.
     * @param r The object over which access is being granted/denied.
     * @return The new or updated access entry.
     */
    public abstract IAccessEntry setAccess(Identity y, AccessRule[] rules, Object target);

    /**
     * Deletes the specified entry from the broker.
     *
     * @param e The entry to remove.
     */
    public final void removeAccess(IAccessEntry e) {

        // Assertions.
        if (e == null) {
            String msg = "Argument 'e [IAccessEntry]' cannot be null.";
            throw new IllegalArgumentException(msg);
        }

        removeAccess(new IAccessEntry[] { e });

    }

    /**
     * Deletes the specified entries from the broker.
     *
     * @param entries The entries to remove.
     */
    public abstract void removeAccess(IAccessEntry[] entries);

    /**
     * Provides the entry governing the specified identity's rights over the
     * specified target, or <code>null</code> if no such entry exists.
     *
     * @param y An access principal.
     * @param target An object that is governed by access rights.
     * @return An entry defining the identity's access rights, or null if there
     * is no entry.
     */
    public abstract IAccessEntry getEntry(Identity y, Object target);

    /**
     * Provides the entries governing the specified identity's rights as
     * defined by this broker instance.
     *
     * @param y An access identity.
     * @return The entries defining the principal's access rights.
     */
    public final IAccessEntry[] getEntries(Identity y) {

        // Assertions.
        if (y == null) {
            String msg = "Argument 'y [Identity]' cannot be null.";
            throw new IllegalArgumentException(msg);
        }

        Principal p = new Principal(new Identity[] { y });
        return getEntries(p, new AccessRule[0]);

    }

    /**
     * Provides the entries governing the specified identity's rights as
     * defined by this broker instance.
     *
     * @param y An access identity.
     * @return The entries defining the principal's access rights.
     */
    public final IAccessEntry[] getEntries(Principal p) {

        // Assertions.
        if (p == null) {
            String msg = "Argument 'y [Identity]' cannot be null.";
            throw new IllegalArgumentException(msg);
        }

        return getEntries(p, new AccessRule[0]);

    }

    /**
     * Provides the entries governing the specified identity's rights as
     * defined by this broker instance where those rights include the specified
     * access rule(s).  In other words, this method will return the complete
     * entry if it includes one or more of the specified rule(s).  If a
     * zero-length rules array is specified, this method will return all entries
     * (i.e. any rule).
     *
     * @param y An access identity.
     * @param rules The rule(s) of access to include, or include all rules if
     * the array is zero-length.
     * @return The entries defining the principal's access rights.
     */
    public final IAccessEntry[] getEntries(Identity y, AccessRule[] rules) {

        // Assertions.
        if (y == null) {
            String msg = "Argument 'y [Identity]' cannot be null.";
            throw new IllegalArgumentException(msg);
        }
        if (rules == null) {
            String msg = "Argument 'rules' cannot be null.";
            throw new IllegalArgumentException(msg);
        }

        Principal p = new Principal(new Identity[] { y });
        return getEntries(p, rules);

    }

    /**
     * Provides the entries governing the specified principal's rights as
     * defined by this broker instance where those rights include the specified
     * access rule(s).  In other words, this method will return the complete
     * entry if it includes one or more of the specified rule(s).  If a
     * zero-length rules array is specified, this method will return all entries
     * (i.e. any rule).
     *
     * @param p An access principal.
     * @param rules The rule(s) of access to include, or include all rules if
     * the array is zero-length.
     * @return The entries defining the principal's access rights.
     */
    public abstract IAccessEntry[] getEntries(Principal p, AccessRule[] rules);

    /**
     * Provides the entries governing access rights over the specified target.
     *
     * @param target An object over which access is governed by this broker
     * instance.
     * @return The entries defining access rights over the target.
     */
    public final IAccessEntry[] getEntries(Object target) {

        // Assertions.
        if (target == null) {
            String msg = "Argument 'target' cannot be null.";
            throw new IllegalArgumentException(msg);
        }

        return getEntries(target, new AccessRule[0]);

    }

    /**
     * Provides the entries governing access rights over the specified target
     * where one or more of the specified access rule(s) is included.  In other
     * words, this method will return the complete entry if it includes one or
     * more of the specified rule(s).  If a zero-length rules array is
     * specified, this method will return all entries (i.e. any rule).
     *
     * @param target An object over which access is governed by this broker
     * instance.
     * @param rules The rule(s) of access to include, or include all rules if
     * the array is zero-length.
     * @return The entries defining access rights over the target.
     */
    public abstract IAccessEntry[] getEntries(Object target, AccessRule[] rules);

    /**
     * Provides all the entries in the broker.
     *
     * @return All the entries defined in the broker.
     */
    public abstract IAccessEntry[] getEntries();

    /*
     * Protected API.
     */

    /**
     * Constructs a new <code>AccessBroker</code> using information from the
     * specified XML.
     *
     * @param e XML that defines the desired broker.
     * @return A broker with the characteristics specified in the XML.
     */
    protected AccessBroker(Element e) {

        // Assertions.
        if (e == null) {
            String msg = "Argument 'e [Element]' cannot be null.";
            throw new IllegalArgumentException(msg);
        }
        if (!e.getName().equals("access-broker")) {
            String msg = "Argument 'e [Element]' must be an <access-broker> " + "element.";
            throw new IllegalArgumentException(msg);
        }

        // Handle.
        Attribute h = e.attribute("handle");
        if (h == null) {
            String msg = "Element <access-broker> is missing required " + "attribute 'handle'.";
            throw new IllegalArgumentException(msg);
        }
        String val = h.getValue().trim();
        if (val.length() == 0) {
            String msg = "Attribute 'handle' cannot be zero-length or contain " + "only whitespace.";
            throw new IllegalArgumentException(msg);
        }
        this.handle = val;

        // Add to collection.
        instances.put(val, this);

    }

    /*
     * Implementation.
     */

    private static Object evaluateUrl(String url) {

        // Assertions.
        if (url == null) {
            String msg = "Argument 'url' cannot be null.";
            throw new IllegalArgumentException(msg);
        }

        Object rslt = null;
        try {

            String[] tokens = url.split("/");
            String className = tokens[2];
            Class c = Class.forName(className);
            Method m = c.getDeclaredMethod("fromUrl", new Class[] { String.class });
            rslt = m.invoke(null, new Object[] { url });

        } catch (Throwable t) {
            String msg = "Unable to evaluate the specified entity:  " + url;
            throw new RuntimeException(msg, t);
        }

        return rslt;

    }

    /*
     * Nested Types.
     */

    protected static final class AccessEntryImpl implements IAccessEntry {

        // Instance Members.
        private final Identity id;
        private final AccessRule[] rules;
        private final Object target;

        /*
         * Public API.
         */

        public static IAccessEntry parse(Element e) {

            // Assertions.
            if (e == null) {
                String msg = "Argument 'e [Element]' cannot be null.";
                throw new IllegalArgumentException(msg);
            }
            if (!e.getName().equals("entry")) {
                String msg = "Argument 'e [Element]' must be an <entry> " + "element.";
                throw new IllegalArgumentException(msg);
            }

            // Identity.
            Identity id = null;
            Element y = (Element) e.selectSingleNode("identity");
            if (y != null) {
                IdentityType type = null;
                Attribute p = y.attribute("type");
                if (p == null) {
                    String msg = "Element <identity> is missing required " + "attribute 'type'.";
                    throw new IllegalArgumentException(msg);
                }
                if (p.getValue().equalsIgnoreCase("GROUP")) {
                    type = IdentityType.GROUP;
                } else {
                    type = IdentityType.USER;
                }
                id = new Identity(y.getText(), type);
            } else {
                String msg = "Element <entry> is missing required child " + "element <identity>.";
                throw new IllegalArgumentException(msg);
            }

            // AccessRules.
            y = (Element) e.selectSingleNode("access");
            if (y == null) {
                String msg = "Element <access> is missing. ";
                throw new IllegalArgumentException(msg);
            }
            AccessRule[] rules = AccessRule.parse(y);

            // Target.
            Object target = null;
            Attribute r = e.attribute("target");
            if (r != null) {
                target = evaluateUrl(r.getValue());
            } else if (e.selectSingleNode("target/*") != null) {
                Element n = (Element) e.selectSingleNode("target/*");
                Attribute impl = n.attribute("impl");
                if (impl == null) {
                    String msg = "Missing required attribute 'impl'.";
                    throw new IllegalArgumentException(msg);
                }
                try {
                    Class c = Class.forName(impl.getValue());
                    Method m = c.getDeclaredMethod("parse", new Class[] { Element.class });
                    target = m.invoke(null, new Object[] { n });
                } catch (Throwable t) {
                    String msg = "Unable to parse the specified target.";
                    throw new RuntimeException(msg, t);
                }

            } else {
                String msg = "Element <entry> must either have a 'target' "
                        + "attribute or contain a <target> element.";
                throw new IllegalArgumentException(msg);
            }

            return new AccessEntryImpl(id, rules, target);

        }

        public AccessEntryImpl(Identity id, AccessRule[] rules, Object target) {

            // Assertions.
            if (id == null) {
                String msg = "Argument 'id' cannot be null.";
                throw new IllegalArgumentException(msg);
            }
            if (rules == null) {
                String msg = "Argument 'rules' cannot be null.";
                throw new IllegalArgumentException(msg);
            }
            if (rules.length == 0) {
                String msg = "Argument 'rules' must contain at least one " + "element.";
                throw new IllegalArgumentException(msg);
            }
            if (target == null) {
                String msg = "Argument 'target' cannot be null.";
                throw new IllegalArgumentException(msg);
            }

            // Instance Members.
            this.id = id;
            this.rules = new AccessRule[rules.length];
            System.arraycopy(rules, 0, this.rules, 0, rules.length);
            this.target = target;

        }

        public Identity getIdentity() {
            return id;
        }

        public AccessRule[] getAccessRules() {
            AccessRule[] rslt = new AccessRule[rules.length];
            System.arraycopy(rules, 0, rslt, 0, rules.length);
            return rslt;
        }

        public Object getTarget() {
            return target;
        }

        public IAccessElement getAccessId() {
            throw new UnsupportedOperationException("This method will be removed.");
        }

        public int[] getTypes() {
            throw new UnsupportedOperationException("This method will be removed.");
        }

        public String getOwner() {
            throw new UnsupportedOperationException("This method will be removed.");
        }

    }

}