/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/urm/org/apache/slide/urm/authenticator/userdb/impl/jndi/URMUserDBManagerSpiJndiBridge.java,v 1.4 2005/03/02 10:53:35 eckehard Exp $
 * $Revision: 1.4 $
 * $Date: 2005/03/02 10:53:35 $
 *
 * ====================================================================
 *
 * Copyright 1999-2004 The Apache Software Foundation.
 *
 * Licensed 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.
 */

package org.apache.slide.urm.authenticator.userdb.impl.jndi;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.slide.urm.URMForbiddenException;
import org.apache.slide.urm.URMNotImplementedException;
import org.apache.slide.urm.authenticator.URMAuthenticatorException;
import org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi;
import org.apache.slide.urm.common.URMConfigurationException;
import org.apache.slide.urm.common.URMConfigurator;
import org.apache.slide.urm.common.URMDeleteException;
import org.apache.slide.urm.common.URMInsertException;
import org.apache.slide.urm.common.URMInternalServerException;
import org.apache.slide.urm.common.URMUpdateException;
import org.apache.slide.urm.common.impl.URMConfiguratorUtil;
import org.apache.slide.urm.utils.validatorcache.URMListener;

/**
 * @author zsa
 */
public abstract class URMUserDBManagerSpiJndiBridge implements URMUserDBManagerSpi
{
    private static org.apache.log4j.Logger msLogger =
        org.apache.log4j.Logger.getLogger(URMUserDBManagerSpiJndiBridge.class.getName());
        
    private static final String ATTR_NAME       = "name";
    private static final String ATTR_VALUE      = "value";
    private static final String PROP_NAME       = "name";
    private static final String PROP_KEY        = "key";
    private static final String PROP_WRITEABLE  = "writeable";
    private static final String PROP_FORMAT     = "format";
    
    private static final String LDAP_ATTR_CN_REPLACE_STR = "{{cn}}";
    
    protected class LdapConfiguration {
        protected String mUserTypePropPrefix   = "cn=";
        protected String mUserTypePropPostfix  = null;
        protected String mGroupTypePropPrefix  = "cn=";
        protected String mGroupTypePropPostfix = null;
        protected String mPersonBaseBindDn     = null;
        protected String mGroupBaseBindDn      = null;
        protected String[] mPersonObjClass       = {"top","person","organizationalperson","inetorgperson"};
        protected String[] mGroupObjClass        = {"top","groupofuniquenames"};
        protected String mPasswordField        = "userPassword";
        protected String mMemberAttrOfGroup    = "uniqueMember";
        protected String mGroupIdField         = "cn";
        protected String mUserIdField          = "cn";
        protected String mProviderUrl          = null;
        
        protected Map mDefaultSetUserAttrs  = new HashMap();
        protected Map mDefaultSetGroupAttrs = new HashMap();
        protected String[] mAvailableUserAttrNames = null;
        protected String[] mAvailableGroupAttrNames = null;
    
        private Set mWriteableUserPropKeys = new HashSet();
        protected Map mAvailableUserPropKeysNATIVE_KEYS = new HashMap();
        private Map mAvailableUserPropKeysABSTRACT_KEYS = new HashMap();
        private Set mWriteableGroupPropKeys = new HashSet();
        protected Map mAvailableGroupPropKeysNATIVE_KEYS = new HashMap();
        private Map mAvailableGroupPropKeysABSTRACT_KEYS = new HashMap();
        
        protected long mAuthCacheSize = 6;
        protected long mAuthCacheTime = 10;
    }

    protected LdapConfiguration mDirParams;
    
    // performance caches
    private HashtableCache[] mAdminCaches;
    
    private class URMLdapPropMapEntry {
        private String mPrefix;
        private String mPostfix;
        private String mNativeAttrname;
        private String mAbstractAttrname;
        private int mPrefixLen;
        private int mPostfixLen;
        
        private URMLdapPropMapEntry (String prefix, String postfix, String nativeAttrname, String abstractAttrname) {
            mPrefix  = prefix;
            mPrefixLen = mPrefix != null ? mPrefix.length() : 0;
            mPostfix = postfix;
            mPostfixLen = mPostfix != null ? mPostfix.length() : 0;
            mNativeAttrname = nativeAttrname;
            mAbstractAttrname = abstractAttrname;
        }
        
        String getCompleteValue (String value) {
            if (mPrefix != null)
                return mPrefix + value + mPostfix;
            else
                return value;
        }
        
        String getExtractedValue (String completeValue) {
            if (mPrefix != null) {
                int fulllen = completeValue.length();
                try {
                    return completeValue.substring(mPrefixLen, fulllen - mPostfixLen);
                } catch (IndexOutOfBoundsException e) {
                    return completeValue;
                }
            }
            else
                return completeValue;
        }
    }

    protected URMUserDBManagerSpiJndiBridge(URMUserDBManagerSpiJndiBridge inObj) {
        this.mDirParams = inObj.mDirParams;
        this.mAdminCaches = inObj.mAdminCaches;
    }
    
    protected URMUserDBManagerSpiJndiBridge(URMConfigurator userDbConf)
                throws URMConfigurationException, URMAuthenticatorException {
        mDirParams = new LdapConfiguration();
        Iterator attrs = URMConfiguratorUtil.getAttributeList(userDbConf, msLogger);
        
        URMConfigurator authconf = userDbConf.getSubConfigurator("/Authenticator");
        URMConfigurator attrsconf = authconf != null ? authconf.getSubConfigurator("Attributes") : null;
        Properties attrprops = attrsconf != null ? URMConfiguratorUtil.getNodeAttrsAsProperties(attrsconf, "Attribute", "name", "value") : null;
        if (attrprops != null) {
            String serverHost = "localhost", serverPort = "389";
            String pval = attrprops.getProperty("serverType");
            if (pval != null) {
                mDirParams.mDefaultSetUserAttrs.put("sn", LDAP_ATTR_CN_REPLACE_STR);
                if (pval.equalsIgnoreCase("SunOneDirectory")) {
                    mDirParams.mUserTypePropPrefix  = "uid=";
                    mDirParams.mUserIdField = "uid";
                    mDirParams.mDefaultSetUserAttrs.put("cn", LDAP_ATTR_CN_REPLACE_STR);
                }
            }
            if ((pval = attrprops.getProperty("personBindDn")) != null)
                mDirParams.mUserTypePropPostfix = "," + (mDirParams.mPersonBaseBindDn = pval);
            if ((pval = attrprops.getProperty("groupBindDn")) != null)
                mDirParams.mGroupTypePropPostfix = "," + (mDirParams.mGroupBaseBindDn = pval);
            if ((pval = attrprops.getProperty("userIdField")) != null)
                mDirParams.mUserTypePropPrefix = (mDirParams.mUserIdField = pval) + "=";
            if ((pval = attrprops.getProperty("groupIdField")) != null)
                mDirParams.mGroupTypePropPrefix = (mDirParams.mGroupIdField = pval) + "=";
            if ((pval = attrprops.getProperty("personObjClass")) != null)
                mDirParams.mPersonObjClass = pval.split(",");
            if ((pval = attrprops.getProperty("groupObjClass")) != null)
                mDirParams.mGroupObjClass = pval.split(",");
            if ((pval = attrprops.getProperty("serverHost")) != null)
                serverHost = pval;
            if ((pval = attrprops.getProperty("serverPort")) != null)
                serverPort = pval;
            if ((pval = attrprops.getProperty("cacheSize")) != null && pval.length() > 0)
                mDirParams.mAuthCacheSize = Integer.parseInt(pval);
            if ((pval = attrprops.getProperty("cacheTime")) != null && pval.length() > 0)
                mDirParams.mAuthCacheTime = Integer.parseInt(pval);
            mDirParams.mProviderUrl = "ldap://"+serverHost+":"+serverPort;
        }
        /*if (authconf != null && (attrs = URMConfiguratorUtil.getAttributeList(authconf, msLogger)) != null) {
            String serverHost = "localhost", serverPort = "389";
            while (attrs.hasNext()) {
                URMConfigurator attrconf = ((URMConfigurator)attrs.next());
                Properties attr = attrconf.getProperties();
                if (attr == null)
                    continue;
                String attrname = (String)attr.get(ATTR_NAME);
                if (attrname.equals("serverType")) {
                    String stype = (String)attr.getProperty(ATTR_VALUE);
                    mDirParams.mDefaultSetUserAttrs.put("sn", LDAP_ATTR_CN_REPLACE_STR);
                    if (stype.equalsIgnoreCase("SunOneDirectory")) {
                        mDirParams.mUserTypePropPrefix  = "uid=";
                        mDirParams.mUserIdField = "uid";
                        mDirParams.mDefaultSetUserAttrs.put("cn", LDAP_ATTR_CN_REPLACE_STR);
                    }
                }
                else if (attrname.equals("personBindDn"))
                    mDirParams.mUserTypePropPostfix = "," + (mDirParams.mPersonBaseBindDn = attr.getProperty(ATTR_VALUE));
                else if (attrname.equals("groupBindDn"))
                    mDirParams.mGroupTypePropPostfix = "," + (mDirParams.mGroupBaseBindDn = attr.getProperty(ATTR_VALUE));
                else if (attrname.equals("userIdField"))
                    mDirParams.mUserTypePropPrefix = (mDirParams.mUserIdField = attr.getProperty(ATTR_VALUE)) + "=";
                else if (attrname.equals("groupIdField"))
                    mDirParams.mGroupTypePropPrefix = (mDirParams.mGroupIdField = attr.getProperty(ATTR_VALUE)) + "=";
                else if (attrname.equals("personObjClass"))
                    mDirParams.mPersonObjClass = attr.getProperty(ATTR_VALUE).split(",");
                else if (attrname.equals("groupObjClass"))
                    mDirParams.mGroupObjClass = attr.getProperty(ATTR_VALUE).split(",");
                else if (attrname.equals("serverHost"))
                    serverHost = attr.getProperty(ATTR_VALUE);
                else if (attrname.equals("serverPort"))
                    serverPort = attr.getProperty(ATTR_VALUE);
            }
            mDirParams.mProviderUrl = "ldap://"+serverHost+":"+serverPort;
        }*/

        URMConfigurator udbconf = authconf.getSubConfigurator("Administrator/UserDatabase");
        attrsconf = udbconf != null ? udbconf.getSubConfigurator("Attributes") : null;
        Properties cacheprops = attrsconf != null ? URMConfiguratorUtil.getNodeAttrsAsProperties(attrsconf, "Attribute", "name", "value") : null;
        int cachesize = 0, cachetime = 0;
        if (cacheprops != null) {
            try {
                String stmp = cacheprops.getProperty("allCacheSize");
                if (stmp != null && stmp.length() > 0)
                    cachesize = Integer.parseInt(stmp);
                stmp = cacheprops.getProperty("allCacheTime");
                if (stmp != null && stmp.length() > 0)
                    cachetime = Integer.parseInt(stmp);
            } catch (NumberFormatException e) { }
        }
        mAdminCaches = new HashtableCache[END_CACHE_IDX];
        for(int i = 0; i < END_CACHE_IDX; ++i)
            mAdminCaches[i] = new HashtableCache(cachesize, cachetime);
            
        if (udbconf == null)
            return;
        URMConfigurator nconf = udbconf.getSubConfigurator("NativeProperties");
        if (nconf == null) 
            return;
        // user property names
        URMConfigurator conf = nconf.getSubConfigurator("User");
        if (conf != null) {
            Iterator props = URMConfiguratorUtil.getPropertyList(conf, msLogger);
            while (props != null && props.hasNext()) {
                URMConfigurator propconf = ((URMConfigurator)props.next());
                Properties prop = propconf.getProperties();
                if (prop == null)
                    continue;
                // LDAP key value
                String propname = prop.getProperty(PROP_NAME);
                if (propname == null)
                    continue;
    //            mAvailableUserNamePropKeys.add(propname);
                // WVCM Key value
                String propkey = prop.getProperty(PROP_KEY);
                if (propkey == null)
                    continue;
                URMLdapPropMapEntry prop_map_entry = null;
                String propformat =  prop.getProperty(PROP_FORMAT);
                if (propformat != null) {
                    if (propformat.equals("{user}"))
                        prop_map_entry = new URMLdapPropMapEntry(mDirParams.mUserTypePropPrefix, 
                                            mDirParams.mUserTypePropPostfix, propname, propkey);
                    else if (propformat.equals("{group}"))
                        prop_map_entry = new URMLdapPropMapEntry(mDirParams.mGroupTypePropPrefix, 
                                            mDirParams.mGroupTypePropPostfix, propname, propkey);
                    else {
                        if (propformat.indexOf(LDAP_ATTR_CN_REPLACE_STR) >= 0)
                            mDirParams.mDefaultSetUserAttrs.put(propname, propformat);
                        prop_map_entry = new URMLdapPropMapEntry(null, null, propname, propkey);
                    }
                }
                else 
                    prop_map_entry = new URMLdapPropMapEntry(null, null, propname, propkey);
                Set keys = (Set) mDirParams.mAvailableUserPropKeysNATIVE_KEYS.get(propname);
                if (keys == null) keys = new HashSet();
                keys.add(prop_map_entry);
                mDirParams.mAvailableUserPropKeysNATIVE_KEYS.put(propname, keys);
                //zsa//mAvailableUserPropKeysABSTRACT_KEYS.put(propkey, propname);
                mDirParams.mAvailableUserPropKeysABSTRACT_KEYS.put(propkey, prop_map_entry);
//              mAvailableUserPropKeys.put(propkey, propname);
                String writeable = prop.getProperty(PROP_WRITEABLE);
                if (writeable != null && writeable.equalsIgnoreCase("true")) {
                    mDirParams.mWriteableUserPropKeys.add(propkey);
                }
            }
        }
        Set set = mDirParams.mAvailableUserPropKeysNATIVE_KEYS.keySet();
        if (set.size() > 0) {
            mDirParams.mAvailableUserAttrNames = new String[set.size()];
            int i = 0;
            for (Iterator iter = set.iterator(); iter.hasNext(); ++i)
                mDirParams.mAvailableUserAttrNames[i] = (String)iter.next();
        }
        
        // group property names
        conf = nconf.getSubConfigurator("Group");
        if (conf != null) {
            Iterator props = URMConfiguratorUtil.getPropertyList(conf, msLogger);
            while (props != null && props.hasNext()) {
                URMConfigurator propconf = ((URMConfigurator)props.next());
                Properties prop = propconf.getProperties();
                if (prop == null)
                    continue;
                String propname = prop.getProperty(PROP_NAME);
                if (propname == null)
                    continue;
    //            mAvailableGroupNamePropKeys.add(propname);
                String propkey = prop.getProperty(PROP_KEY);
                if (propkey == null)
                    continue;
                URMLdapPropMapEntry prop_map_entry = null;
                String propformat =  prop.getProperty(PROP_FORMAT);
                if (propformat != null) {
                    if (propformat.equals("{user}"))
                        prop_map_entry = new URMLdapPropMapEntry(mDirParams.mUserTypePropPrefix, 
                                            mDirParams.mUserTypePropPostfix, propname, propkey);
                    else if (propformat.equals("{group}"))
                        prop_map_entry =  new URMLdapPropMapEntry(mDirParams.mGroupTypePropPrefix, 
                                            mDirParams.mGroupTypePropPostfix, propname, propkey);
                    else {
                        if (propformat.indexOf(LDAP_ATTR_CN_REPLACE_STR) >= 0)
                            mDirParams.mDefaultSetGroupAttrs.put(propname, propformat);
                        prop_map_entry = new URMLdapPropMapEntry(null, null, propname, propkey);
                    }
                }
                else 
                    prop_map_entry = new URMLdapPropMapEntry(null, null, propname, propkey);
                Set keys = (Set) mDirParams.mAvailableGroupPropKeysNATIVE_KEYS.get(propname);
                if (keys == null) keys = new HashSet();
                keys.add(prop_map_entry);
                mDirParams.mAvailableGroupPropKeysNATIVE_KEYS.put(propname, keys);
                //zsa//mAvailableGroupPropKeysABSTRACT_KEYS.put(propkey,propname);
                mDirParams.mAvailableGroupPropKeysABSTRACT_KEYS.put(propkey, prop_map_entry);
//                mAvailableGroupPropKeys.put(propkey, propname);
                String writeable = prop.getProperty(PROP_WRITEABLE);
                if (writeable != null && writeable.equalsIgnoreCase("true")) {
                    mDirParams.mWriteableGroupPropKeys.add(propkey);
                }
            }
        }
        set = mDirParams.mAvailableGroupPropKeysNATIVE_KEYS.keySet();
        if (set.size() > 0) {
            mDirParams.mAvailableGroupAttrNames = new String[set.size()];
            int i = 0;
            for (Iterator iter = set.iterator(); iter.hasNext(); ++i)
                mDirParams.mAvailableGroupAttrNames[i] = (String)iter.next();
        }
        
    }
    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#getAllGroups()
     */
    public Set getAllGroups()
        throws
            URMNotImplementedException,
            URMForbiddenException,
            URMInternalServerException {
        Set groups = (Set)mAdminCaches[ALL_GROUPS_CACHE_IDX].get(null);
        if (groups == null) {
            if (msLogger.isDebugEnabled())
                msLogger.debug("getAllGroups(): read from database.");
            groups = getAllEntry(false);
            if (groups != null)
                mAdminCaches[ALL_GROUPS_CACHE_IDX].add(null, groups);
            else
                return null;
        }
        else
            if (msLogger.isDebugEnabled())
                msLogger.debug("getAllGroups(): read from cache.");
        return groups;
    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#getAllGroups(java.lang.String)
     */
    public Set getAllGroups(String domain)
        throws
            URMNotImplementedException,
            URMForbiddenException,
            URMInternalServerException {
        return getAllGroups();
    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#getAllUsers()
     */
    public Set getAllUsers()
        throws
            URMNotImplementedException,
            URMForbiddenException,
            URMInternalServerException {
        Set users = (Set)mAdminCaches[ALL_USERS_CACHE_IDX].get(null);
        if (users == null) {
            if (msLogger.isDebugEnabled())
                msLogger.debug("getAllUsers(): read from database.");
            users = getAllEntry(true);
            if (users != null)
                mAdminCaches[ALL_USERS_CACHE_IDX].add(null, users);
            else
                return null;
        }
        else
            if (msLogger.isDebugEnabled())
                msLogger.debug("getAllUsers(): read from cache.");
        return users;
    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#getAllUsers(java.lang.String)
     */
    public Set getAllUsers(String domain)
        throws
            URMNotImplementedException,
            URMForbiddenException,
            URMInternalServerException {
        return getAllUsers();
    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#getGroupsOfUser(java.lang.String, java.lang.String)
     */
    public Set getGroupsOfUser(String user)
        throws
            URMNotImplementedException,
            URMForbiddenException,
            URMInternalServerException {
        Set groups = (Set)mAdminCaches[GROUPSOFUSER_CACHE_IDX].get(user);
        if (groups == null) {
            if (msLogger.isDebugEnabled())
                msLogger.debug("getGroupsOfUser("+user+"): read from database.");
            groups = getGroupsWhereMemberOf(user, true);
            if (groups != null)
                mAdminCaches[GROUPSOFUSER_CACHE_IDX].add(user, groups);
            else
                return null;
        }
        else
            if (msLogger.isDebugEnabled())
                msLogger.debug("getGroupsOfUser("+user+"): read from cache.");
        return groups;
    }

    public Set getGroupsOfUser(String user, String domain)
        throws
            URMNotImplementedException,
            URMForbiddenException,
            URMInternalServerException {
        return getGroupsOfUser(user);
    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#getGroupMembers(java.lang.String)
     */
    public List getGroupMembers(String group)
        throws
            URMNotImplementedException,
            URMForbiddenException,
            URMInternalServerException {
        List members = (List)mAdminCaches[GROUPMEMBERS_CACHE_IDX].get(group);
        if (members == null) {
            if (msLogger.isDebugEnabled())
                msLogger.debug("getGroupMembers("+group+"): read from database.");
            members = getGroupMembers(group, false);
            if (members != null)
                mAdminCaches[GROUPMEMBERS_CACHE_IDX].add(group, members);
            else
                return null;
        }
        else
            if (msLogger.isDebugEnabled())
                msLogger.debug("getGroupMembers("+group+"): read from cache.");
        return members;
    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#getGroupMembers(java.lang.String, java.lang.String)
     */
    public List getGroupMembers(String group, String domain)
        throws
            URMNotImplementedException,
            URMForbiddenException,
            URMInternalServerException {
        return getGroupMembers(group);
    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#getGroupsMemberOf(java.lang.String)
     */
    public Set getGroupsMemberOf(String group)
        throws
            URMNotImplementedException,
            URMForbiddenException,
            URMInternalServerException {
        Set groups = (Set)mAdminCaches[GROUPSMEMBEROF_CACHE_IDX].get(group);
        if (groups == null) {
            if (msLogger.isDebugEnabled())
                msLogger.debug("getGroupsMemberOf("+group+"): read from database.");
            groups = getGroupsWhereMemberOf(group, false);
            if (groups != null)
                mAdminCaches[GROUPSMEMBEROF_CACHE_IDX].add(group, groups);
            else
                return null;
        }
        else
            if (msLogger.isDebugEnabled())
                msLogger.debug("getGroupsMemberOf("+group+"): read from cache.");
        return groups;
    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#getGroupsMemberOf(java.lang.String, java.lang.String)
     */
    public Set getGroupsMemberOf(String group, String domain)
        throws
            URMNotImplementedException,
            URMForbiddenException,
            URMInternalServerException {
        return getGroupsMemberOf(group);
    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#createUser(java.lang.String, char[], java.lang.String)
     */
    public void createUser(String user, char[] password, String domain)
        throws URMNotImplementedException, URMForbiddenException, URMInsertException {
        // TODO Auto-generated method stub

    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#createGroup(java.lang.String, java.lang.String)
     */
    public void createGroup(String group, String domain)
        throws URMNotImplementedException, URMForbiddenException, URMInsertException {
        // TODO Auto-generated method stub

    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#deleteUser(java.lang.String, java.lang.String)
     */
    public void deleteUser(String user, String domain)
        throws URMNotImplementedException, URMForbiddenException, URMDeleteException {
        // TODO Auto-generated method stub

    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#deleteGroup(java.lang.String, java.lang.String)
     */
    public void deleteGroup(String group, String domain)
        throws URMNotImplementedException, URMForbiddenException, URMDeleteException {
        // TODO Auto-generated method stub

    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#getUserProperties(java.lang.String)
     */
    public Properties getUserProperties(String user)
        throws
            URMNotImplementedException,
            URMForbiddenException,
            URMInternalServerException {
        Properties userProps = (Properties)mAdminCaches[USERPROPERTIES_CACHE_IDX].get(user);
        if (userProps == null) {
            if (msLogger.isDebugEnabled())
                msLogger.debug("getUserProperties("+user+"): read from database.");
            userProps = new Properties();
            Properties nativeUserProps = getEntryAttributes(user, true);
            Iterator propIter = nativeUserProps.keySet().iterator();
            while (propIter.hasNext()) {
                String currentKey  = (String) propIter.next();
                Set currentNames = (Set) mDirParams.mAvailableUserPropKeysNATIVE_KEYS.get(currentKey);
                if (currentNames != null){
                    Iterator keyiter = currentNames.iterator();
                    while (keyiter.hasNext()){
                        URMLdapPropMapEntry current = (URMLdapPropMapEntry) keyiter.next();
                        userProps.put(current.mAbstractAttrname, 
                                      current.getExtractedValue(nativeUserProps.getProperty(currentKey)));
                    }
                }
            }
            mAdminCaches[USERPROPERTIES_CACHE_IDX].add(user, userProps);
        }
        else
            if (msLogger.isDebugEnabled())
                msLogger.debug("getUserProperties("+user+"): read from cache.");
        return userProps;
    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#getUserProperties(java.lang.String, java.lang.String)
     */
    public Properties getUserProperties(String user, String domain)
        throws
            URMNotImplementedException,
            URMForbiddenException,
            URMInternalServerException {
        return getUserProperties(user);
    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#setUserProperties(java.lang.String, java.util.Properties)
     */
    public void setUserProperties(String user, Properties props)
        throws URMNotImplementedException, URMForbiddenException, URMUpdateException {
        // TODO Auto-generated method stub

    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#getGroupProperties(java.lang.String)
     */
    public Properties getGroupProperties(String group)
        throws
            URMNotImplementedException,
            URMForbiddenException,
            URMInternalServerException {
        Properties groupProps = (Properties)mAdminCaches[GROUPPROPERTIES_CACHE_IDX].get(group);
        if (groupProps == null) {
            if (msLogger.isDebugEnabled())
                msLogger.debug("getGroupProperties("+group+"): read from database.");
            groupProps = new Properties();
            Properties nativeGroupProps = getEntryAttributes(group, false);
            Iterator propIter = nativeGroupProps.keySet().iterator();
            while (propIter.hasNext()) {
                String currentKey  = (String) propIter.next();
                Set currentNames = (Set) mDirParams.mAvailableGroupPropKeysNATIVE_KEYS.get(currentKey);
                if (currentNames != null){
                    Iterator keyiter = currentNames.iterator();
                    while (keyiter.hasNext()){
                        URMLdapPropMapEntry current = (URMLdapPropMapEntry) keyiter.next();
                        groupProps.put(current.mAbstractAttrname, 
                                      current.getExtractedValue(nativeGroupProps.getProperty(currentKey)));
                    }
                }
            }
            mAdminCaches[GROUPPROPERTIES_CACHE_IDX].add(group, groupProps);
        }
        else
            if (msLogger.isDebugEnabled())
                msLogger.debug("getGroupProperties("+group+"): read from cache.");
        return groupProps;
    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#getGroupProperties(java.lang.String, java.lang.String)
     */
    public Properties getGroupProperties(String group, String domain)
        throws
            URMNotImplementedException,
            URMForbiddenException,
            URMInternalServerException {
        return getGroupProperties(group);
    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#setGroupProperties(java.lang.String, java.util.Properties)
     */
    public void setGroupProperties(String group, Properties props)
        throws URMNotImplementedException, URMForbiddenException, URMUpdateException {
        // TODO Auto-generated method stub

    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#setGroupProperties(java.lang.String, java.lang.String, java.util.Properties)
     */
    public void setGroupProperties(
        String group,
        String domain,
        Properties props)
        throws URMNotImplementedException, URMForbiddenException, URMUpdateException {
        // TODO Auto-generated method stub

    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#setUserProperties(java.lang.String, java.lang.String, java.util.Properties)
     */
    public void setUserProperties(String user, String domain, Properties props)
        throws URMNotImplementedException, URMForbiddenException, URMUpdateException {
        // TODO Auto-generated method stub

    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#addUserToGroup(java.lang.String, java.lang.String)
     */
    public void addUserToGroup(String user, String group)
        throws URMNotImplementedException, URMForbiddenException, URMInsertException {
        // TODO Auto-generated method stub

    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#addUserToGroup(java.lang.String, java.lang.String, java.lang.String)
     */
    public void addUserToGroup(String user, String group, String domain)
        throws URMNotImplementedException, URMForbiddenException, URMInsertException {
        // TODO Auto-generated method stub

    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#removeUserFromGroup(java.lang.String, java.lang.String)
     */
    public void removeUserFromGroup(String user, String group)
        throws URMNotImplementedException, URMForbiddenException, URMDeleteException {
        // TODO Auto-generated method stub

    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#removeUserFromGroup(java.lang.String, java.lang.String, java.lang.String)
     */
    public void removeUserFromGroup(String user, String group, String domain)
        throws URMNotImplementedException, URMForbiddenException, URMDeleteException {
        // TODO Auto-generated method stub

    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#addGroupToGroup(java.lang.String, java.lang.String)
     */
    public void addGroupToGroup(String memberGroup, String toGroup)
        throws URMNotImplementedException, URMForbiddenException, URMInsertException {
        // TODO Auto-generated method stub

    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#addGroupToGroup(java.lang.String, java.lang.String, java.lang.String)
     */
    public void addGroupToGroup(
        String memberGroup,
        String toGroup,
        String domain)
        throws URMNotImplementedException, URMForbiddenException, URMInsertException {
        // TODO Auto-generated method stub

    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#removeGroupFromGroup(java.lang.String, java.lang.String)
     */
    public void removeGroupFromGroup(String memberGroup, String fromGroup)
        throws URMNotImplementedException, URMForbiddenException, URMDeleteException {
        // TODO Auto-generated method stub

    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#removeGroupFromGroup(java.lang.String, java.lang.String, java.lang.String)
     */
    public void removeGroupFromGroup(
        String memberGroup,
        String fromGroup,
        String domain)
        throws URMNotImplementedException, URMForbiddenException, URMDeleteException {
        // TODO Auto-generated method stub

    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#changeUserPassword(java.lang.String, char[], char[])
     */
    public void changeUserPassword(
        String user,
        char[] oldpassword,
        char[] newpassword)
        throws URMNotImplementedException, URMForbiddenException, URMUpdateException {
        // TODO Auto-generated method stub

    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#changeUserPassword(java.lang.String, java.lang.String, char[], char[])
     */
    public void changeUserPassword(
        String user,
        String domain,
        char[] oldpassword,
        char[] newpassword)
        throws URMNotImplementedException, URMForbiddenException, URMUpdateException {
        // TODO Auto-generated method stub

    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#isUser(java.lang.String)
     */
    public boolean isUser(String user)
        throws
            URMNotImplementedException,
            URMForbiddenException,
            URMInternalServerException {
        Set allusers = (Set)mAdminCaches[ALL_USERS_CACHE_IDX].get(null);
        if ((allusers != null) && (allusers.contains(user))) {
            if (msLogger.isDebugEnabled())
                msLogger.debug("isUser("+user+") returns TRUE (read from all_users cache).");
            return true;
        } else {
            if (mAdminCaches[ISNOTUSER_CACHE_IDX].get(user) != null) {
                if (msLogger.isDebugEnabled())
                    msLogger.debug("isUser("+user+") returns FALSE (read from is_not_users cache).");
                return false;
            }
            try {
                if (this.getUserProperties(user).size() == 0){
                    if (msLogger.isDebugEnabled())
                        msLogger.debug("isUser("+user+") returns FALSE (has no properties).");
                    mAdminCaches[ISNOTUSER_CACHE_IDX].add(user, user);
                    return false;
                } else {
                    if (msLogger.isDebugEnabled())
                        msLogger.debug("isUser("+user+") returns TRUE (has any properties).");
                    return true;
                }
            } catch (URMInternalServerException e) {
                if (msLogger.isDebugEnabled())
                    msLogger.debug("isUser("+user+") returns FALSE (getProperties(): "+e.getMessage()+").");
                mAdminCaches[ISNOTUSER_CACHE_IDX].add(user, user);
                return false;
            }
        }
    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#isUser(java.lang.String, java.lang.String)
     */
    public boolean isUser(String user, String domain)
        throws
            URMNotImplementedException,
            URMForbiddenException,
            URMInternalServerException {
        return isUser(user);
    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#isGroup(java.lang.String)
     */
    public boolean isGroup(String group)
        throws
            URMNotImplementedException,
            URMForbiddenException,
            URMInternalServerException {
        Set allgroups = (Set)mAdminCaches[ALL_GROUPS_CACHE_IDX].get(null);
        if ((allgroups != null) && (allgroups.contains(group))) {
            if (msLogger.isDebugEnabled())
                msLogger.debug("isGroup("+group+") returns TRUE (read from all_groups cache).");
            return true;
        } else {
            if (mAdminCaches[ISNOTGROUP_CACHE_IDX].get(group) != null) {
                if (msLogger.isDebugEnabled())
                    msLogger.debug("isGroup("+group+") returns FALSE (read from is_not_groups cache).");
                return false;
            }
            try {
                if (this.getGroupProperties(group).size() == 0){
                    if (msLogger.isDebugEnabled())
                        msLogger.debug("isGroup("+group+") returns FALSE (has no properties).");
                    mAdminCaches[ISNOTGROUP_CACHE_IDX].add(group, group);
                    return false;
                } else {
                    if (msLogger.isDebugEnabled())
                        msLogger.debug("isGroup("+group+") returns TRUE (has any properties).");
                    return true;
                }
            } catch (URMInternalServerException e) {
                if (msLogger.isDebugEnabled())
                    msLogger.debug("isGroup("+group+") returns FALSE (getProperties(): "+e.getMessage()+").");
                mAdminCaches[ISNOTGROUP_CACHE_IDX].add(group, group);
                return false;
            }
        }
    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#isGroup(java.lang.String, java.lang.String)
     */
    public boolean isGroup(String group, String domain)
        throws
            URMNotImplementedException,
            URMForbiddenException,
            URMInternalServerException {
        return isGroup(group);
    }

    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#getWriteablePropKeys(boolean)
     */
    public Set getWriteablePropKeys(boolean isUser) {
        if (isUser)
            return mDirParams.mWriteableUserPropKeys;
        else
            return mDirParams.mWriteableGroupPropKeys;
    }
    
    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#getAvailablePropKeys(boolean)
     */
    public Set getAvailablePropKeys(boolean isUser) {
        if (isUser)
            return mDirParams.mAvailableUserPropKeysABSTRACT_KEYS.keySet();
        else
            return mDirParams.mAvailableGroupPropKeysABSTRACT_KEYS.keySet();
    }
    
    /* (non-Javadoc)
     * @see org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi#clearCaches()
     */
    public void clearCaches() {
        for (int i = 0; i < END_CACHE_IDX; ++i)
            mAdminCaches[i].clear();
    }

    private void resetCaches() {
        URMListener listener = URMListener.getAclListenerSet();
        if (listener != null) {
            listener.resetEvent();
        }
    }
    
    protected abstract Set getGroupsWhereMemberOf(String name, boolean isUser) throws URMInternalServerException;

    protected abstract List getGroupMembers(String name, boolean isUser) throws URMInternalServerException;
    
    protected abstract Set getAllEntry(boolean isUser) throws URMInternalServerException;
    
    protected abstract Properties getEntryAttributes(String name, boolean isUser) throws URMInternalServerException;

    /**
     * private class to cache information 
     */
    private class HashtableCache {
        
        private final static String mfsKeyName = "this_would_be_the_key_in_the_cache_if_null_key_parameter_is_received"; 

        private Hashtable elementCache = new Hashtable();
        private List      elementKeys  = null; // a kind of fifo
        private long sizeLimet = 0;
        private long validTime = 0;
        
        private HashtableCache(long sizelimit, long validtime) {
            this.sizeLimet = sizelimit;
            this.validTime  = validtime;
            synchronized (elementCache) {
                if (elementKeys == null) {
                    elementKeys = (Collections.synchronizedList(new LinkedList()));
                }
            }
        }
        
        private class CacheElement {
            public Object obj  = null;
            public long   time = 0;
            
            public CacheElement(Object obj) {
                this.obj = obj;
                this.time = System.currentTimeMillis();
            }
        }
        
        protected void add(String key, Object obj) {
            if (sizeLimet <= 0)
                return;
                
            if (elementCache.size() >= sizeLimet) {
                // remove an element from the cache
                synchronized (elementCache) {
                    while (elementKeys.size() > 0 && elementCache.remove(elementKeys.remove(0)) == null);
                }
            }
            String lkey;
            if (key == null)
                lkey = mfsKeyName;
            else
                lkey = key;
            elementCache.put(lkey, new CacheElement(obj));
            elementKeys.add(lkey);
        }
        
        protected Object get(String key) {
            if (sizeLimet <= 0)
                return null;
                
            String lkey;
            if (key == null)
                lkey = mfsKeyName;
            else
                lkey = key;
            CacheElement obj = (CacheElement)elementCache.get(lkey);
            if (obj != null) {
                if (validTime <= (System.currentTimeMillis() - obj.time)/1000) {
                    synchronized (elementCache) {
                        elementCache.remove(lkey);
                    }
                    return null;
                }
                else
                    return obj.obj;
            }
            else
                return null;
        }
        
        protected Object remove(String key) {
            if (sizeLimet <= 0)
                return null;
                
            String lkey;
            if (key == null)
                lkey = mfsKeyName;
            else
                lkey = key;
            Object obj = null;
            synchronized (elementCache) {
                obj = elementCache.remove(lkey);
            }
            return obj;
        }
        
        protected void clear() {
            synchronized (elementCache) {
                elementCache.clear();
                elementKeys.clear();
            }
        }
    }

    private final static int ALL_USERS_CACHE_IDX         = 0;
    private final static int ALL_GROUPS_CACHE_IDX        = 1;
    private final static int GROUPSOFUSER_CACHE_IDX      = 2;
    private final static int GROUPMEMBERS_CACHE_IDX      = 3;
    private final static int GROUPSMEMBEROF_CACHE_IDX    = 4;
    private final static int USERPROPERTIES_CACHE_IDX    = 5;
    private final static int GROUPPROPERTIES_CACHE_IDX   = 6;
    private final static int ISNOTUSER_CACHE_IDX         = 7;
    private final static int ISNOTGROUP_CACHE_IDX        = 8;
    private final static int END_CACHE_IDX               = 9;
    
}
