com.silverwrist.dynamo.db.GroupObject.java Source code

Java tutorial

Introduction

Here is the source code for com.silverwrist.dynamo.db.GroupObject.java

Source

/*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (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.mozilla.org/MPL/>.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
 * language governing rights and limitations under the License.
 * 
 * The Original Code is the Venice Web Communities System.
 * 
 * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
 * for Silverwrist Design Studios.  Portions created by Eric J. Bowersox are
 * Copyright (C) 2002 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.dynamo.db;

import java.security.Principal;
import java.security.acl.AclNotFoundException;
import java.util.*;
import org.apache.commons.collections.*;
import com.silverwrist.dynamo.Namespaces;
import com.silverwrist.dynamo.UserInfoNamespace;
import com.silverwrist.dynamo.except.*;
import com.silverwrist.dynamo.iface.*;
import com.silverwrist.dynamo.security.SecurityReferenceMonitor;
import com.silverwrist.dynamo.util.*;

class GroupObject implements DynamoGroup {
    /*--------------------------------------------------------------------------------
     * Static data members
     *--------------------------------------------------------------------------------
     */

    private static final String PERM_NAMESPACE = Namespaces.GROUP_PERMISSIONS_NAMESPACE;

    /*--------------------------------------------------------------------------------
     * Attributes
     *--------------------------------------------------------------------------------
     */

    private GroupObjectOps m_ops; // operations object
    private NamespaceCache m_ns_cache; // namespace cache object
    private SecurityReferenceMonitor m_srm; // security reference monitor
    private UserProxyManagement m_upm; // user proxy manager
    private int m_gid; // group ID
    private String m_groupname; // group name
    private int m_gaclid; // group ACL ID
    private ReferenceMap m_properties; // cached property values
    private Object m_properties_sync = new Object(); // synchronization for above
    private HashSet m_known_members = new HashSet(); // known members
    private HashSet m_known_nonmembers = new HashSet(); // known nonmembers
    private Object m_members_sync = new Object(); // synchronization for above

    /*--------------------------------------------------------------------------------
     * Constructor
     *--------------------------------------------------------------------------------
     */

    GroupObject(Map data, GroupObjectOps ops, NamespaceCache ns_cache, SecurityReferenceMonitor srm,
            UserProxyManagement upm) {
        m_ops = ops;
        m_ns_cache = ns_cache;
        m_srm = srm;
        m_upm = upm;
        m_gid = ((Integer) (data.get(UserManagerOps.HMKEY_GID))).intValue();
        m_groupname = (String) (data.get(UserManagerOps.HMKEY_GROUPNAME));
        m_gaclid = ((Integer) (data.get(UserManagerOps.HMKEY_GACLID))).intValue();
        m_properties = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.SOFT);

    } // end constructor

    /*--------------------------------------------------------------------------------
     * Internal operations
     *--------------------------------------------------------------------------------
     */

    private static final int getPrincipalUID(Principal p) {
        if (p instanceof DynamoUser)
            return ((DynamoUser) p).getUID();
        else
            return -1;

    } // end getPrincipalUID

    private final void testOperationWithoutSecurity() {
        if (m_gaclid == -1)
            return;
        throw new GroupRuntimeException(
                new DynamoSecurityException(GroupObject.class, "DatabaseMessages", "sec.needSec"));

    } // end testOperationWithoutSecurity

    private final void testPermission(DynamoUser caller, String perm_namespace, String perm_name,
            String fail_message) throws DatabaseException, DynamoSecurityException {
        if (m_gaclid == -1)
            return;
        if (caller == null)
            throw new DynamoSecurityException(GroupObject.class, "DatabaseMessages", "sec.needSec");
        if (caller.equals(m_srm.getAdminUser()))
            return; // Administrator can do anything
        if (m_srm.testPermission(m_gaclid, caller, perm_namespace, perm_name))
            return; // we have the right permission in the ACL
        DynamoSecurityException dse = new DynamoSecurityException(GroupObject.class, "DatabaseMessages",
                fail_message);
        dse.setParameter(0, m_groupname);
        throw dse;

    } // end testPermission

    private final void testPermission(DynamoUser caller, int target_uid, String perm_namespace, String perm_name,
            String perm2_name, String fail_message) throws DatabaseException, DynamoSecurityException {
        if (m_gaclid == -1)
            return;
        if (caller == null)
            throw new DynamoSecurityException(GroupObject.class, "DatabaseMessages", "sec.needSec");
        if (caller.equals(m_srm.getAdminUser()))
            return; // Administrator can do anything
        if ((caller.getUID() == target_uid) && m_srm.testPermission(m_gaclid, caller, perm_namespace, perm2_name))
            return; // we can "join group" or "unjoin group" if we have the right permissions
        if (m_srm.testPermission(m_gaclid, caller, perm_namespace, perm_name))
            return; // we have the right permission in the ACL
        DynamoSecurityException dse = new DynamoSecurityException(GroupObject.class, "DatabaseMessages",
                fail_message);
        dse.setParameter(0, m_groupname);
        throw dse;

    } // end testPermission

    private final void testAclOwner(DynamoUser caller) throws DatabaseException, DynamoSecurityException {
        if (m_gaclid == -1)
            return;
        if (caller == null)
            throw new DynamoSecurityException(GroupObject.class, "DatabaseMessages", "sec.needSec");
        if (caller.equals(m_srm.getAdminUser()))
            return; // Administrator can do anything
        if (m_srm.isOwnerOfAcl(m_gaclid, caller))
            return; // we own this ACL
        DynamoSecurityException dse = new DynamoSecurityException(GroupObject.class, "DatabaseMessages",
                "sec.setGroupAcl");
        dse.setParameter(0, m_groupname);
        throw dse;

    } // end testAclOwner

    private final boolean addUIDInternal(int uid) throws DatabaseException {
        Integer key = new Integer(uid);
        synchronized (m_members_sync) { // look in the cache first
            if (m_known_members.contains(key))
                return false; // we're already a known member

            // fiddle the database
            boolean rc = m_ops.addMember(m_gid, uid);
            m_known_members.add(key); // update the cache to reflect reality
            if (m_known_nonmembers != null)
                m_known_nonmembers.remove(key);
            return rc;

        } // end synchronized block

    } // end addUIDInternal

    private final boolean removeUIDInternal(int uid) throws DatabaseException {
        Integer key = new Integer(uid);
        synchronized (m_members_sync) { // look in the cache first
            if (m_known_nonmembers != null) { // are we a known nonmember?
                if (m_known_nonmembers.contains(key))
                    return false; // already a nonmember

            } // end if
            else { // in this circumstance, everyone not in the members set is a nonmember
                if (!(m_known_members.contains(key)))
                    return false; // already a nonmember

            } // end else

            // fiddle the database
            boolean rc = m_ops.removeMember(m_gid, uid);
            m_known_members.remove(key); // update the cache to reflect reality
            if (m_known_nonmembers != null)
                m_known_nonmembers.add(key);
            return rc;

        } // end synchronized block

    } // end removeUIDInternal

    /*--------------------------------------------------------------------------------
     * Implementations from interface Principal
     *--------------------------------------------------------------------------------
     */

    public boolean equals(Object another) {
        if (another == null)
            return false;
        if (another instanceof DynamoGroup)
            return (m_gid == ((DynamoGroup) another).getGID());
        return false;

    } // end equals

    public String toString() {
        return "group " + m_groupname;

    } // end toString

    public int hashCode() {
        return m_gid;

    } // end hashCode

    public String getName() {
        return m_groupname;

    } // end getName

    /*--------------------------------------------------------------------------------
     * Implementations from interface Group
     *--------------------------------------------------------------------------------
     */

    public boolean addMember(Principal user) {
        testOperationWithoutSecurity();
        try { // break down the operations...
            if (user instanceof DynamoUser)
                return this.addUIDInternal(((DynamoUser) user).getUID());
            else if (user instanceof DynamoGroup) { // add all UIDs contained in this group
                int[] uids = ((DynamoGroup) user).getUIDs();
                boolean rc = false;
                for (int i = 0; i < uids.length; i++)
                    rc = this.addUIDInternal(uids[i]) || rc;
                return rc;

            } // end else if
            else
                throw new IllegalArgumentException("expected DynamoUser or DynamoGroup");

        } // end try
        catch (DatabaseException e) { // wrap this exception and return it
            throw new GroupRuntimeException(e);

        } // end catch

    } // end addMember

    public boolean removeMember(Principal user) {
        testOperationWithoutSecurity();
        try { // break down the operations...
            if (user instanceof DynamoUser)
                return this.removeUIDInternal(((DynamoUser) user).getUID());
            else if (user instanceof DynamoGroup) { // remove all UIDs contained in this group
                int[] uids = ((DynamoGroup) user).getUIDs();
                boolean rc = false;
                for (int i = 0; i < uids.length; i++)
                    rc = this.removeUIDInternal(uids[i]) || rc;
                return rc;

            } // end else if
            else
                throw new IllegalArgumentException("expected DynamoUser or DynamoGroup");

        } // end try
        catch (DatabaseException e) { // wrap this exception and return it
            throw new GroupRuntimeException(e);

        } // end catch

    } // end removeMember

    public boolean isMember(Principal member) {
        try { // break down the operations...
            if (member instanceof DynamoUser)
                return this.testUID(((DynamoUser) member).getUID());
            else if (member instanceof DynamoGroup) { // test all UIDs contained within this group
                int[] uids = ((DynamoGroup) member).getUIDs();
                if (uids.length == 0)
                    return false;
                boolean rc = true;
                for (int i = 0; rc && (i < uids.length); i++)
                    rc = this.testUID(uids[i]);
                return rc;

            } // end else if
            else
                throw new IllegalArgumentException("expected DynamoUser or DynamoGroup");

        } // end try
        catch (DatabaseException e) { // wrap this exception and return it
            throw new GroupRuntimeException(e);

        } // end catch

    } // end isMember

    public Enumeration members() {
        try { // call down, get the UIDs, and use them to get proxies
            int[] uids = this.getUIDs();
            ArrayList tmp = new ArrayList(uids.length);
            for (int i = 0; i < uids.length; i++)
                tmp.add(m_upm.getUserProxy(uids[i]));
            return Collections.enumeration(tmp);

        } // end try
        catch (DatabaseException e) { // wrap this exception and return it
            throw new GroupRuntimeException(e);

        } // end catch

    } // end members

    /*--------------------------------------------------------------------------------
     * Implementations from interface ObjectProvider
     *--------------------------------------------------------------------------------
     */

    /**
     * Retrieves an object from this <CODE>ObjectProvider</CODE>.
     *
     * @param namespace The namespace to interpret the name relative to.
     * @param name The name of the object to be retrieved.
     * @return The object reference specified.
     */
    public Object getObject(String namespace, String name) {
        try { // convert the namespace name to an ID here
            PropertyKey key = new PropertyKey(m_ns_cache.namespaceNameToId(namespace), name);
            Object rc = null;
            synchronized (m_properties_sync) { // start by looking in the properties map
                rc = m_properties.get(key);
                if (rc == null) { // no use - need to try the database
                    rc = m_ops.getProperty(m_gid, key);
                    if (rc != null)
                        m_properties.put(key, rc);

                } // end if

            } // end synchronized block

            if (rc == null)
                throw new NoSuchObjectException(this.toString(), namespace, name);
            return rc;

        } // end try
        catch (DatabaseException e) { // translate into our NoSuchObjectException but retain the DatabaseException
            throw new NoSuchObjectException(this.toString(), namespace, name, e);

        } // end catch

    } // end getObject

    /*--------------------------------------------------------------------------------
     * Implementations from interface SecureObjectStore
     *--------------------------------------------------------------------------------
     */

    public Object setObject(DynamoUser caller, String namespace, String name, Object value)
            throws DatabaseException, DynamoSecurityException {
        testPermission(caller, namespace, "set.property", "sec.setGroupProperty");
        Object rc = null;
        // convert the namespace name to an ID here
        PropertyKey key = new PropertyKey(m_ns_cache.namespaceNameToId(namespace), name);
        synchronized (m_properties_sync) { // start by setting the database value
            rc = m_ops.setProperty(m_gid, key, value);

            // and cache it, too
            m_properties.put(key, value);

        } // end synchronized block

        return rc;

    } // end setObject

    public Object removeObject(DynamoUser caller, String namespace, String name)
            throws DatabaseException, DynamoSecurityException {
        testPermission(caller, namespace, "remove.property", "sec.removeGroupProperty");
        Object rc = null;
        // convert the namespace name to an ID here
        PropertyKey key = new PropertyKey(m_ns_cache.namespaceNameToId(namespace), name);
        synchronized (m_properties_sync) { // start by killing the database value
            rc = m_ops.removeProperty(m_gid, key);

            // and remove the cached value, too
            m_properties.remove(key);

        } // end synchronized block

        return rc;

    } // end removeObject

    public Collection getNamespaces() throws DatabaseException {
        // call through to the database to get the list of namespace IDs
        int[] ids = m_ops.getPropertyNamespaceIDs(m_gid);

        ArrayList rc = new ArrayList(ids.length);
        for (int i = 0; i < ids.length; i++)
            rc.add(m_ns_cache.namespaceIdToName(ids[i]));
        return Collections.unmodifiableList(rc);

    } // end getNamespaces

    public Collection getNamesForNamespace(String namespace) throws DatabaseException {
        // call through to the database to get the data for this namespace
        int nsid = m_ns_cache.namespaceNameToId(namespace);
        Map data = m_ops.getAllProperties(m_gid, nsid);

        // we both create the return value and cache the data values
        ArrayList rc = new ArrayList(data.size());
        synchronized (m_properties_sync) { // do the transfer...
            Iterator it = data.entrySet().iterator();
            while (it.hasNext()) { // copy one entry at a time
                Map.Entry ntry = (Map.Entry) (it.next());
                rc.add(ntry.getKey().toString());
                m_properties.put(new PropertyKey(nsid, ntry.getKey().toString()), ntry.getValue());

            } // end while

        } // end synchronized block

        return Collections.unmodifiableList(rc);

    } // end getNamesForNamespace

    /*--------------------------------------------------------------------------------
     * Implementations from interface DynamoGroup
     *--------------------------------------------------------------------------------
     */

    public int getGID() {
        return m_gid;

    } // end getGID

    public boolean addUID(DynamoUser caller, int uid) throws DatabaseException, DynamoSecurityException {
        testPermission(caller, uid, PERM_NAMESPACE, "add.member", "join.group", "sec.addGroupMember");
        return addUIDInternal(uid);

    } // end addUID

    public boolean removeUID(DynamoUser caller, int uid) throws DatabaseException, DynamoSecurityException {
        testPermission(caller, uid, PERM_NAMESPACE, "remove.member", "unjoin.group", "sec.removeGroupMember");
        return removeUIDInternal(uid);

    } // end removeUID

    public boolean testUID(int uid) throws DatabaseException {
        Integer key = new Integer(uid);
        synchronized (m_members_sync) { // look in the cache first
            if (m_known_members.contains(key))
                return true;
            if ((m_known_nonmembers == null) || m_known_nonmembers.contains(key))
                return false;

            // try the database
            boolean rc = m_ops.testMember(m_gid, uid);
            if (rc) // add cached information
                m_known_members.add(key);
            else if (m_known_nonmembers != null)
                m_known_nonmembers.add(key);
            return rc;

        } // end synchronized block

    } // end testUID

    public int[] getUIDs() throws DatabaseException {
        int[] rc = null;
        synchronized (m_members_sync) { // try the cache first
            if (m_known_nonmembers == null) { // we know we already contain the complete members list, so just get that
                rc = new int[m_known_members.size()];
                int i = 0;
                Iterator it = m_known_members.iterator();
                while (it.hasNext())
                    rc[i++] = ((Integer) (it.next())).intValue();

            } // end if
            else { // have to call down to the database to get the list
                rc = m_ops.getMembers(m_gid);

                // now we can cache the entire array and dump the nonmembers cache
                for (int i = 0; i < rc.length; i++)
                    m_known_members.add(new Integer(rc[i]));
                m_known_nonmembers.clear();
                m_known_nonmembers = null;

            } // end else

        } // end synchronized block

        return rc;

    } // end getUIDs

    public boolean addMember(DynamoUser caller, Principal member)
            throws DatabaseException, DynamoSecurityException {
        testPermission(caller, getPrincipalUID(member), PERM_NAMESPACE, "add.member", "join.group",
                "sec.addGroupMember");
        if (member instanceof DynamoUser)
            return this.addUIDInternal(((DynamoUser) member).getUID());
        else if (member instanceof DynamoGroup) { // add all UIDs contained in this group
            int[] uids = ((DynamoGroup) member).getUIDs();
            boolean rc = false;
            for (int i = 0; i < uids.length; i++)
                rc = this.addUIDInternal(uids[i]) || rc;
            return rc;

        } // end else if
        else
            throw new IllegalArgumentException("expected DynamoUser or DynamoGroup");

    } // end addMember

    public boolean removeMember(DynamoUser caller, Principal member)
            throws DatabaseException, DynamoSecurityException {
        testPermission(caller, getPrincipalUID(member), PERM_NAMESPACE, "remove.member", "unjoin.group",
                "sec.removeGroupMember");
        if (member instanceof DynamoUser)
            return this.removeUIDInternal(((DynamoUser) member).getUID());
        else if (member instanceof DynamoGroup) { // remove all UIDs contained in this group
            int[] uids = ((DynamoGroup) member).getUIDs();
            boolean rc = false;
            for (int i = 0; i < uids.length; i++)
                rc = this.removeUIDInternal(uids[i]) || rc;
            return rc;

        } // end else if
        else
            throw new IllegalArgumentException("expected DynamoUser or DynamoGroup");

    } // end removeMember

    public DynamoAcl getAcl() throws DatabaseException, AclNotFoundException {
        if (m_gaclid == -1)
            return null;
        else
            return m_srm.getAcl(m_gaclid);

    } // end getAcl

    public void setAcl(DynamoUser caller, DynamoAcl acl) throws DatabaseException, DynamoSecurityException {
        testAclOwner(caller);
        int new_id = ((acl == null) ? -1 : acl.getAclID());
        m_ops.setAclID(m_gid, new_id);
        m_gaclid = new_id;

    } // end setAcl

    public synchronized int getMemberCount() throws DatabaseException {
        return m_ops.getMemberCount(m_gid);

    } // end getMemberCount

    public List getMembers(int offset, int count) throws DatabaseException {
        int[] ids = m_ops.getMembers(m_gid, offset, count);
        ArrayList rc = new ArrayList(ids.length);
        for (int i = 0; i < ids.length; i++)
            rc.add(m_upm.getUserProxy(ids[i]));
        return Collections.unmodifiableList(rc);

    } // end getMembers

} // end class GroupObject