/*
 * 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.lang.ref.*;
import java.util.*;
import org.w3c.dom.*;
import com.silverwrist.util.xml.*;
import com.silverwrist.dynamo.UserInfoNamespace;
import com.silverwrist.dynamo.event.*;
import com.silverwrist.dynamo.except.*;
import com.silverwrist.dynamo.iface.*;
import com.silverwrist.dynamo.security.SecurityReferenceMonitor;
import com.silverwrist.dynamo.util.*;

public class UserManagerObject
    implements NamedObject, ComponentInitialize, ComponentShutdown, UserManagement, UserProxyManagement,
               AuthenticatorRegistration, UserInfoNamespace, UserPropertyTranslatorInstall
{
  /*--------------------------------------------------------------------------------
   * Internal class implementing user proxies
   *--------------------------------------------------------------------------------
   */

  private class MyUserProxy extends UserProxy
  {
    /*====================================================================
     * Attributes
     *====================================================================
     */

    private DynamoUser m_real_user = null;

    /*====================================================================
     * Constructors
     *====================================================================
     */

    MyUserProxy(int uid)
    {
      super(uid);

    } // end constructor

    MyUserProxy(int uid, String username)
    {
      super(uid,username);

    } // end constructor

    /*====================================================================
     * Abstract implementations from class UserProxy
     *====================================================================
     */

    protected synchronized DynamoUser getRealUser()
    {
      if (m_real_user==null)
      { // need to retrieve the real user...
	try
	{ // get the real user...
	  m_real_user = UserManagerObject.this.getUser(m_uid);

	} // end try
	catch (DatabaseException e)
	{ // wrap it in a runtime exception type
	  throw new ProxyException(e);

	} // end catch

      } // end if

      return m_real_user;

    } // end getRealUser

  } // end class MyUserProxy

  /*--------------------------------------------------------------------------------
   * Internal class implementing group proxies
   *--------------------------------------------------------------------------------
   */

  private class MyGroupProxy extends GroupProxy
  {
    /*====================================================================
     * Attributes
     *====================================================================
     */

    private DynamoGroup m_real_group = null;

    /*====================================================================
     * Constructors
     *====================================================================
     */

    MyGroupProxy(int gid)
    {
      super(gid);

    } // end constructor

    MyGroupProxy(int gid, String groupname)
    {
      super(gid,groupname);

    } // end constructor

    /*====================================================================
     * Abstract implementations from class UserProxy
     *====================================================================
     */

    protected synchronized DynamoGroup getRealGroup()
    {
      if (m_real_group==null)
      { // need to retrieve the real group...
	try
	{ // get the real group...
	  m_real_group = UserManagerObject.this.getGroup(m_gid);

	} // end try
	catch (DatabaseException e)
	{ // wrap it in a runtime exception type
	  throw new ProxyException(e);

	} // end catch

      } // end if

      return m_real_group;

    } // end getRealGroup

  } // end class MyGroupProxy

  /*--------------------------------------------------------------------------------
   * Internal event listener class
   *--------------------------------------------------------------------------------
   */

  private class UpdateListener implements DynamicUpdateListener
  {
    /*====================================================================
     * Constructor
     *====================================================================
     */

    UpdateListener()
    { // do nothing
    } // end constructor

    /*====================================================================
     * Implementations from interface DynamicUpdateListener
     *====================================================================
     */

    public void updateReceived(DynamicUpdateEvent evt)
    {
      if (evt instanceof UserUpdateEvent)
      { // try to pass the event on to the user
	int uid = ((UserUpdateEvent)evt).getUID();
	Integer key = new Integer(uid);
	synchronized (m_users_sync)
	{ // try looking in the cache for a UserObject
	  IDNameCacheObject cobj = (IDNameCacheObject)(m_id_to_user.get(key));
	  if (cobj!=null)
	  { // got a cache object - retrieve the user value
	    UserObject target = (UserObject)(cobj.get());
	    if (target==null)
	    { // the UserObject got kicked out!  kick it out of the HashMaps
	      m_id_to_user.remove(key);
	      m_name_to_user.remove(cobj.getNameKey());
	      cobj.clear();
	      cobj = null;

	    } // end if
	    else if (target!=evt.getSource())
	      target.updated((UserUpdateEvent)evt);

	  } // end if
	  
	} // end synchronized block

      } // end if

    } // end updateReceived

  } // end class UpdateListener

  /*--------------------------------------------------------------------------------
   * Internal property serializer class
   *--------------------------------------------------------------------------------
   */

  private class UserGroupSerializer implements PropertySerializer
  {
    /*====================================================================
     * Constructor
     *====================================================================
     */

    UserGroupSerializer()
    { // do nothing
    } // end constructor

    /*====================================================================
     * Implementations from interface PropertySerializer
     *====================================================================
     */

    public String serializeProperty(Object value)
    {
      if (value instanceof DynamoUser)
	return "User:" + String.valueOf(((DynamoUser)value).getUID());

      if (value instanceof DynamoGroup)
	return "Group:" + String.valueOf(((DynamoGroup)value).getGID());

      return null;

    } // end serializeProperty

    public Object deserializeProperty(String value)
    {
      try
      { // look for our known prefixes
	if (value.startsWith("User:"))
	  return getUserProxy(Integer.parseInt(value.substring(5)));

	if (value.startsWith("Group:"))
	  return getGroupProxy(Integer.parseInt(value.substring(6)));

	return null;

      } // end try
      catch (NumberFormatException e)
      { // number parse blew up...
	return null;

      } // end catch

    } // end deserializeProperty

  } // end UserGroupSerializer

  /*--------------------------------------------------------------------------------
   * Internal class implementing the adapter for UserPropertyTranslator
   *--------------------------------------------------------------------------------
   */

  class UserPropertyAdapter implements UserPropertyTranslator
  {
    /*====================================================================
     * Attributes
     *====================================================================
     */

    private UserPropertyTranslator m_upt = null;

    /*====================================================================
     * Constructor
     *====================================================================
     */

    UserPropertyAdapter()
    { // do nothing
    } // end constructor

    /*====================================================================
     * Implementations from interface UserPropertyTranslator
     *====================================================================
     */

    public String getUserFullName(DynamoUser user)
    {
      if (m_upt!=null)
	return m_upt.getUserFullName(user);
      else
	return user.getName();

    } // end getUserFullName

    /*====================================================================
     * External operations
     *====================================================================
     */

    void setTranslator(UserPropertyTranslator upt)
    {
      m_upt = upt;

    } // end setTranslator

  } // end class UserPropertyAdapter

  /*--------------------------------------------------------------------------------
   * Static data members
   *--------------------------------------------------------------------------------
   */

  private static final int MAX_USERNAME = 64;
  private static final int MAX_GROUPNAME = 64;

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

  private UserPropertyAdapter m_adapter;                    // user property adapter
  private String m_name;                                    // object name
  private NamespaceCache m_ns_cache;                        // namespace cache object
  private SecurityReferenceMonitor m_srm;                   // security reference monitor
  private PostDynamicUpdate m_post;                         // dynamic update poster
  private UserManagerOps m_ops;                             // database operations object
  private UserObject m_anon_user = null;                    // permanent reference to anonymous user
  private Object m_anon_user_sync = new Object();           // synchronization on the above field
  private HashMap m_id_to_user = new HashMap();             // map from IDs (Integer) to objects
  private HashMap m_name_to_user = new HashMap();           // map from names (DynamoIDKey) to objects
  private ReferenceQueue m_rq = new ReferenceQueue();       // reference queue for old objects
  private Object m_users_sync = new Object();               // synchronization for user HashMaps
  private HashMap m_id_to_group = new HashMap();            // map from IDs (Integer) to groups
  private HashMap m_name_to_group = new HashMap();          // map from names (DynamoIDKey) to groups
  private ReferenceQueue m_group_rq = new ReferenceQueue(); // reference queue for group objects
  private Object m_groups_sync = new Object();              // synchronization for group HashMaps
  private Hashtable m_authenticators = new Hashtable();     // authenticators list
  private ComponentShutdown m_hook_init;                    // hook for init service provider
  private ComponentShutdown m_pszreg;                       // property serializer registration
  private ComponentShutdown m_evt_user;                     // event registration

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

  public UserManagerObject()
  {
    m_adapter = new UserPropertyAdapter(); // create the adapter

    // Add some default authenticators.
    m_authenticators.put(new QualifiedNameKey(NAMESPACE,AUTH_DEFAULT),new DefaultHashAuthenticator());

  } // end constructor

  /*--------------------------------------------------------------------------------
   * Implementations from interface NamedObject
   *--------------------------------------------------------------------------------
   */

  public String getName()
  {
    return m_name;

  } // end getName

  /*--------------------------------------------------------------------------------
   * Implementations from interface ComponentInitialize
   *--------------------------------------------------------------------------------
   */

  public void initialize(Element config_root, ServiceProvider services) throws ConfigException
  {
    String conn_name = null;
    String nscache_name = null;
    String srm_name = null;
    String connect_name = null;
    XMLLoader loader = XMLLoader.get();
    try
    { // verify the right node name
      loader.verifyNodeName(config_root,"object");

      // get the object's name
      m_name = loader.getAttribute(config_root,"name");

      // get the database configuration connection
      DOMElementHelper config_root_h = new DOMElementHelper(config_root);
      Element elt = loader.getSubElement(config_root_h,"database");
      conn_name = loader.getAttribute(elt,"connection");
      nscache_name = loader.getAttribute(elt,"namespaces");

      // get the security reference monitor reference
      elt = loader.getSubElement(config_root_h,"security");
      srm_name = loader.getAttribute(elt,"object");

      // get the name of the connection point to connect ourselves to
      elt = config_root_h.getSubElement("connect-proxy-services");
      if (elt!=null)
	connect_name = loader.getAttribute(elt,"cpoint");

    } // end try
    catch (XMLLoadException e)
    { // error loading XML config data
      throw new ConfigException(e);

    } // end catch

    // Get the database connection pool, namespace cache, and security reference monitor.
    DBConnectionPool pool = GetObjectUtils.getDatabaseConnection(services,conn_name);
    m_ns_cache =
        (NamespaceCache)(GetObjectUtils.getDynamoComponent(services,NamespaceCache.class,nscache_name));
    m_srm =
        (SecurityReferenceMonitor)(GetObjectUtils.getDynamoComponent(services,SecurityReferenceMonitor.class,
								     srm_name));
    m_post = (PostDynamicUpdate)(services.queryService(PostDynamicUpdate.class));

    // Get the operations object.
    m_ops = UserManagerOps.get(pool);

    // Connect our proxy services to the connection point.
    if (connect_name!=null)
    { // get the ConnectionBackend service and use it to jack in
      ConnectionBackEnd backend = (ConnectionBackEnd)(services.queryService(ConnectionBackEnd.class));
      backend.connectObject(connect_name,this);

    } // end if

    // Register event listeners that forward events to the right location.
    EventListenerRegistration reg =
        (EventListenerRegistration)(services.queryService(EventListenerRegistration.class));
    UpdateListener ul = new UpdateListener();
    m_evt_user = reg.registerDynamicUpdateListener(UserUpdateEvent.class,ul);

    // Register our property serializer to let User and Group objects be serialized.
    PropertySerializerRegistration psreg =
        (PropertySerializerRegistration)(services.queryService(PropertySerializerRegistration.class));
    m_pszreg = psreg.registerPropertySerializer(new UserGroupSerializer());

    // Add us to the initialization services.
    HookServiceProviders hooker = (HookServiceProviders)(services.queryService(HookServiceProviders.class));
    SingletonServiceProvider ssp = new SingletonServiceProvider("UserManagerObject",
								AuthenticatorRegistration.class,
								(AuthenticatorRegistration)this);
    m_hook_init = hooker.hookInitServiceProvider(ssp);

  } // end initialize

  /*--------------------------------------------------------------------------------
   * Implementations from interface ComponentShutdown
   *--------------------------------------------------------------------------------
   */

  public void shutdown()
  {
    m_evt_user.shutdown();
    m_evt_user = null;
    m_pszreg.shutdown();
    m_pszreg = null;
    m_hook_init.shutdown();
    m_hook_init = null;
    m_authenticators.clear();
    m_id_to_user.clear();
    m_name_to_user.clear();
    m_anon_user = null;
    m_ops.dispose();
    m_ops = null;
    m_post = null;
    m_srm = null;
    m_ns_cache = null;

  } // end shutdown

  /*--------------------------------------------------------------------------------
   * Implementations from interface UserManagement
   *--------------------------------------------------------------------------------
   */

  public DynamoUser getAnonymousUser() throws DatabaseException
  {
    synchronized (m_anon_user_sync)
    { // get the anonymous user, if it doesn't already exist
      if (m_anon_user==null)
      { // get the data for the anonymous user
	Map data = m_ops.getAnonymousUserData();
	if (data==null)
	  throw new DatabaseException(UserManagerObject.class,"UserMessages","anon.notFound");
	m_anon_user = new UserObject(data,true,m_ops.getObjectOps(),m_ns_cache,m_srm,this,m_post,m_adapter);

      } // end if

      return m_anon_user;

    } // end synchronized block

  } // end getAnonymousUser

  public DynamoUser getUser(int id) throws DatabaseException
  {
    DynamoUser rc = getAnonymousUser();
    if (rc.getUID()==id)
      return rc;

    rc = null;
    Integer key = new Integer(id);
    synchronized (m_users_sync)
    { // try to retrieve the user
      IDNameCacheObject cobj = (IDNameCacheObject)(m_id_to_user.get(key));
      if (cobj!=null)
      { // got a cache object - retrieve the user value
	rc = (DynamoUser)(cobj.get());
	if (rc==null)
	{ // the UserObject got kicked out!  kick it out of the HashMaps
	  m_id_to_user.remove(key);
	  m_name_to_user.remove(cobj.getNameKey());
	  cobj.clear();
	  cobj = null;

	} // end if

      } // end if

      if (rc==null)
      { // OK, we have to go to the database to find the user
	Map data = m_ops.getUserData(id);
	if (data!=null)
	{ // create UserObject and add it to our cache maps
	  UserObject uobj = new UserObject(data,false,m_ops.getObjectOps(),m_ns_cache,m_srm,this,m_post,
					   m_adapter);
	  rc = uobj;
	  cobj = new IDNameCacheObject(key,uobj.getName(),uobj,m_rq);
	  m_id_to_user.put(cobj.getIDKey(),cobj);
	  m_name_to_user.put(cobj.getNameKey(),cobj);

	} // end if
	// else user does not exist - return null

      } // end if

    } // end synchronized block

    return rc;

  } // end getUser

  public DynamoUser getUser(String username) throws DatabaseException
  {
    DynamoUser rc = getAnonymousUser();
    if (rc.getName().equalsIgnoreCase(username))
      return rc;

    rc = null;
    DynamoIDKey key = new DynamoIDKey(username);
    synchronized (m_users_sync)
    { // try to retrieve the user
      IDNameCacheObject cobj = (IDNameCacheObject)(m_name_to_user.get(key));
      if (cobj!=null)
      { // got a cache object - retrieve the user value
	rc = (DynamoUser)(cobj.get());
	if (rc==null)
	{ // the UserObject got kicked out!  kick it out of the HashMaps
	  m_id_to_user.remove(cobj.getIDKey());
	  m_name_to_user.remove(key);
	  cobj.clear();
	  cobj = null;

	} // end if

      } // end if

      if (rc==null)
      { // OK, we have to go to the database to find the user
	Map data = m_ops.getUserData(username);
	if (data!=null)
	{ // create user object and add it to our cache maps
	  UserObject uobj = new UserObject(data,false,m_ops.getObjectOps(),m_ns_cache,m_srm,this,m_post,
					   m_adapter);
	  rc = uobj;
	  cobj = new IDNameCacheObject(uobj.getUID(),key,uobj,m_rq);
	  m_id_to_user.put(cobj.getIDKey(),cobj);
	  m_name_to_user.put(cobj.getNameKey(),cobj);

	} // end if
	// else user does not exist - return null

      } // end if

    } // end synchronized block

    return rc;

  } // end getUser

  public DynamoUser createUser(String username, String email) throws DatabaseException
  {
    DynamoUser rc = getAnonymousUser();
    if (rc.getName().equalsIgnoreCase(username))
    { // this user already exists
      DatabaseException de = new DatabaseException(UserManagerObject.class,"UserMessages","user.exists");
      de.setParameter(0,username);
      throw de;

    } // end if

    DynamoIDKey key = new DynamoIDKey(username);
    if (!(key.isValid(MAX_USERNAME)))
    { // the user ID is invalid...
      DatabaseException de = new DatabaseException(UserManagerObject.class,"UserMessages","user.badID");
      de.setParameter(0,username);
      throw de;

    } // end if

    rc = null;
    synchronized (m_users_sync)
    { // see if the user is in our cache already
      IDNameCacheObject cobj = (IDNameCacheObject)(m_name_to_user.get(key));
      if (cobj!=null)
      { // it's in the cache - we're going to fail this operation.  But while we're here, if the
	// actual user object has been kicked out, expunge the reference.
	if (cobj.get()==null)
	{ // the UserObject got kicked out!  kick it out of the HashMaps
	  m_id_to_user.remove(cobj.getIDKey());
	  m_name_to_user.remove(key);
	  cobj.clear();
	  cobj = null;

	} // end if

	// throw the exception saying that we exist
	DatabaseException de = new DatabaseException(UserManagerObject.class,"UserMessages","user.exists");
	de.setParameter(0,username);
	throw de;

      } // end if

      // OK, at this point, we can't be any surer the user doesn't already exist without
      // hitting the database.  So create it.  (createNewUser will throw a DatabaseException if the
      // user already exists.)
      Map data = m_ops.createNewUser(username,email);
      UserObject uobj = new UserObject(data,false,m_ops.getObjectOps(),m_ns_cache,m_srm,this,m_post,m_adapter);
      rc = uobj;
      cobj = new IDNameCacheObject(uobj.getUID(),key,uobj,m_rq);
      m_id_to_user.put(cobj.getIDKey(),cobj);
      m_name_to_user.put(cobj.getNameKey(),cobj);

    } // end synchronized block

    return rc;  // new user created!

  } // end createUser

  public DynamoGroup getGroup(int id) throws DatabaseException
  {
    DynamoGroup rc = null;
    Integer key = new Integer(id);
    synchronized (m_groups_sync)
    { // try to retrieve the group
      IDNameCacheObject cobj = (IDNameCacheObject)(m_id_to_group.get(key));
      if (cobj!=null)
      { // got a cache object - retrieve the group value
	rc = (DynamoGroup)(cobj.get());
	if (rc==null)
	{ // the GroupObject got kicked out!  kick it out of the HashMaps
	  m_id_to_group.remove(key);
	  m_name_to_group.remove(cobj.getNameKey());
	  cobj.clear();
	  cobj = null;

	} // end if

      } // end if

      if (rc==null)
      { // OK, we have to go to the database to find the group
	Map data = m_ops.getGroupData(id);
	if (data!=null)
	{ // create GroupObject and add it to our cache maps
	  GroupObject gobj = new GroupObject(data,m_ops.getGroupOps(),m_ns_cache,m_srm,this);
	  rc = gobj;
	  cobj = new IDNameCacheObject(key,gobj.getName(),gobj,m_group_rq);
	  m_id_to_group.put(cobj.getIDKey(),cobj);
	  m_name_to_group.put(cobj.getNameKey(),cobj);

	} // end if
	// else group does not exist - return null

      } // end if

    } // end synchronized block

    return rc;

  } // end getGroup

  public DynamoGroup getGroup(String groupname) throws DatabaseException
  {
    DynamoGroup rc = null;
    DynamoIDKey key = new DynamoIDKey(groupname);
    synchronized (m_groups_sync)
    { // try to retrieve the group
      IDNameCacheObject cobj = (IDNameCacheObject)(m_name_to_group.get(key));
      if (cobj!=null)
      { // got a cache object - retrieve the group value
	rc = (DynamoGroup)(cobj.get());
	if (rc==null)
	{ // the GroupObject got kicked out!  kick it out of the HashMaps
	  m_id_to_group.remove(cobj.getIDKey());
	  m_name_to_group.remove(key);
	  cobj.clear();
	  cobj = null;

	} // end if

      } // end if

      if (rc==null)
      { // OK, we have to go to the database to find the group
	Map data = m_ops.getGroupData(groupname);
	if (data!=null)
	{ // create group object and add it to our cache maps
	  GroupObject gobj = new GroupObject(data,m_ops.getGroupOps(),m_ns_cache,m_srm,this);
	  rc = gobj;
	  cobj = new IDNameCacheObject(gobj.getGID(),key,gobj,m_group_rq);
	  m_id_to_group.put(cobj.getIDKey(),cobj);
	  m_name_to_group.put(cobj.getNameKey(),cobj);

	} // end if
	// else group does not exist - return null

      } // end if

    } // end synchronized block

    return rc;

  } // end getGroup

  public DynamoGroup createGroup(String groupname) throws DatabaseException
  {
    DynamoIDKey key = new DynamoIDKey(groupname);
    if (!(key.isValid(MAX_GROUPNAME)))
    { // the group ID is invalid...
      DatabaseException de = new DatabaseException(UserManagerObject.class,"UserMessages","group.badID");
      de.setParameter(0,groupname);
      throw de;

    } // end if

    DynamoGroup rc = null;
    synchronized (m_groups_sync)
    { // see if the group is in our cache already
      IDNameCacheObject cobj = (IDNameCacheObject)(m_name_to_group.get(key));
      if (cobj!=null)
      { // it's in the cache - we're going to fail this operation.  But while we're here, if the
	// actual group object has been kicked out, expunge the reference.
	if (cobj.get()==null)
	{ // the GroupObject got kicked out!  kick it out of the HashMaps
	  m_id_to_group.remove(cobj.getIDKey());
	  m_name_to_group.remove(key);
	  cobj.clear();
	  cobj = null;

	} // end if

	// throw the exception saying that we exist
	DatabaseException de = new DatabaseException(UserManagerObject.class,"UserMessages","group.exists");
	de.setParameter(0,groupname);
	throw de;

      } // end if

      // OK, at this point, we can't be any surer the group doesn't already exist without
      // hitting the database.  So create it.  (createNewGroup will throw a DatabaseException if the
      // group already exists.)
      Map data = m_ops.createNewGroup(groupname);
      GroupObject gobj = new GroupObject(data,m_ops.getGroupOps(),m_ns_cache,m_srm,this);
      rc = gobj;
      cobj = new IDNameCacheObject(gobj.getGID(),key,gobj,m_group_rq);
      m_id_to_group.put(cobj.getIDKey(),cobj);
      m_name_to_group.put(cobj.getNameKey(),cobj);

    } // end synchronized block

    return rc;  // new group created!

  } // end createNewGroup

  public void loadUserDefaults(DynamoUser user, Collection namespaces) throws DatabaseException
  {
    // Start by translating all the namespaces to namespace IDs.
    int[] nsids = new int[namespaces.size()];
    int i = 0;
    Iterator it = namespaces.iterator();
    while (it.hasNext())
      nsids[i++] = m_ns_cache.namespaceNameToId(it.next().toString());

    // Now feed it into the database operations.
    m_ops.loadUserDefaults(user.getUID(),nsids);

  } // end loadUserDefaults

  /*--------------------------------------------------------------------------------
   * Implementations from interface UserProxyManagement
   *--------------------------------------------------------------------------------
   */

  public DynamoUser getUserProxy(int uid)
  {
    synchronized (m_anon_user_sync)
    { // get the anonymous user, if it's there and matches
      if ((m_anon_user!=null) && (m_anon_user.getUID()==uid))
	return m_anon_user;

    } // end synchronized block

    Integer key = new Integer(uid);
    synchronized (m_users_sync)
    { // try to retrieve the user
      IDNameCacheObject cobj = (IDNameCacheObject)(m_id_to_user.get(key));
      if (cobj!=null)
      { // got a cache object - retrieve the user value
	DynamoUser rc = (DynamoUser)(cobj.get());
	if (rc==null)
	{ // the UserObject got kicked out!  kick it out of the HashMaps
	  m_id_to_user.remove(key);
	  m_name_to_user.remove(cobj.getNameKey());
	  rc = new MyUserProxy(uid,cobj.getNameKey().toString());
	  cobj.clear();
	  cobj = null;

	} // end if

	return rc;

      } // end if

      return new MyUserProxy(uid);  // just return a proxy object

    } // end synchronized block

  } // end getUserProxy

  public DynamoUser getUserProxy(int uid, String username)
  {
    synchronized (m_anon_user_sync)
    { // get the anonymous user, if it's there and matches
      if ((m_anon_user!=null) && (m_anon_user.getUID()==uid))
	return m_anon_user;

    } // end synchronized block

    Integer key = new Integer(uid);
    synchronized (m_users_sync)
    { // try to retrieve the user
      IDNameCacheObject cobj = (IDNameCacheObject)(m_id_to_user.get(key));
      if (cobj!=null)
      { // got a cache object - retrieve the user value
	DynamoUser rc = (DynamoUser)(cobj.get());
	if (rc==null)
	{ // the UserObject got kicked out!  kick it out of the HashMaps
	  m_id_to_user.remove(key);
	  m_name_to_user.remove(cobj.getNameKey());
	  rc = new MyUserProxy(uid,cobj.getNameKey().toString());
	  cobj.clear();
	  cobj = null;

	} // end if

	return rc;

      } // end if

      return new MyUserProxy(uid,username);  // just return a proxy object

    } // end synchronized block

  } // end getUserProxy

  public DynamoGroup getGroupProxy(int gid)
  {
    Integer key = new Integer(gid);
    synchronized (m_groups_sync)
    { // try to retrieve the group
      IDNameCacheObject cobj = (IDNameCacheObject)(m_id_to_group.get(key));
      if (cobj!=null)
      { // got a cache object - retrieve the group value
	DynamoGroup rc = (DynamoGroup)(cobj.get());
	if (rc==null)
	{ // the GroupObject got kicked out!  kick it out of the HashMaps
	  m_id_to_group.remove(key);
	  m_name_to_group.remove(cobj.getNameKey());
	  rc = new MyGroupProxy(gid,cobj.getNameKey().toString());
	  cobj.clear();
	  cobj = null;

	} // end if

	return rc;

      } // end if

      return new MyGroupProxy(gid);  // just return a proxy object

    } // end synchronized block

  } // end getGroupProxy

  public DynamoGroup getGroupProxy(int gid, String groupname)
  {
    Integer key = new Integer(gid);
    synchronized (m_groups_sync)
    { // try to retrieve the group
      IDNameCacheObject cobj = (IDNameCacheObject)(m_id_to_group.get(key));
      if (cobj!=null)
      { // got a cache object - retrieve the group value
	DynamoGroup rc = (DynamoGroup)(cobj.get());
	if (rc==null)
	{ // the GroupObject got kicked out!  kick it out of the HashMaps
	  m_id_to_group.remove(key);
	  m_name_to_group.remove(cobj.getNameKey());
	  rc = new MyGroupProxy(gid,cobj.getNameKey().toString());
	  cobj.clear();
	  cobj = null;

	} // end if

	return rc;

      } // end if

      return new MyGroupProxy(gid,groupname);  // just return a proxy object

    } // end synchronized block

  } // end getGroupProxy

  /*--------------------------------------------------------------------------------
   * Implementations from interface AuthenticatorLookup
   *--------------------------------------------------------------------------------
   */

  public Authenticator findAuthenticator(String method_namespace, String method)
  {
    return (Authenticator)(m_authenticators.get(new QualifiedNameKey(method_namespace,method)));

  } // end findAuthenticator

  /*--------------------------------------------------------------------------------
   * Implementations from interface AuthenticatorRegistration
   *--------------------------------------------------------------------------------
   */

  public ComponentShutdown registerAuthenticator(String method_namespace, String method, Authenticator auth)
      throws ConfigException
  {
    QualifiedNameKey key = new QualifiedNameKey(method_namespace,method);
    if (m_authenticators.containsKey(key))
    { // dump out here
      ConfigException ce = new ConfigException(UserManagerObject.class,"DatabaseMessages","auth.register");
      ce.setParameter(0,method_namespace);
      ce.setParameter(1,method);
      throw ce;

    } // end if

    m_authenticators.put(key,auth);
    return new ShutdownHashtableRemove(m_authenticators,key);

  } // end registerAuthenticator

  /*--------------------------------------------------------------------------------
   * Implementations from interface UserPropertyTranslatorInstall
   *--------------------------------------------------------------------------------
   */

  public void installUserPropertyTranslator(UserPropertyTranslator upt)
  {
    m_adapter.setTranslator(upt);

  } // end installUserPropertyTranslator

} // end class UserManagerObject
