Java tutorial
/* * 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