/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/urm/org/apache/slide/urm/authenticator/userdb/impl/tamino/URMMetadataPropsSpiTamino.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.tamino;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import java.util.Vector;

import org.w3c.dom.Element;
import org.w3c.dom.Node;

import com.softwareag.tamino.db.api.accessor.TDeleteException;
import com.softwareag.tamino.db.api.accessor.TInsertException;
import com.softwareag.tamino.db.api.accessor.TQuery;
import com.softwareag.tamino.db.api.accessor.TXMLObjectAccessor;
import com.softwareag.tamino.db.api.accessor.TXQuery;
import com.softwareag.tamino.db.api.accessor.TXQueryException;
import com.softwareag.tamino.db.api.common.TException;
import com.softwareag.tamino.db.api.connection.TConnection;
import com.softwareag.tamino.db.api.connection.TConnectionFactory;
import com.softwareag.tamino.db.api.connection.TConnectionPoolManager;
import com.softwareag.tamino.db.api.connection.TIsolationDegree;
import com.softwareag.tamino.db.api.connection.TServerNotAvailableException;
import com.softwareag.tamino.db.api.objectModel.TIteratorException;
import com.softwareag.tamino.db.api.objectModel.TNoSuchXMLObjectException;
import com.softwareag.tamino.db.api.objectModel.TXMLObject;
import com.softwareag.tamino.db.api.objectModel.TXMLObjectIterator;
import com.softwareag.tamino.db.api.response.TResponse;
import org.apache.slide.urm.URMForbiddenException;
import org.apache.slide.urm.URMNotImplementedException;
import org.apache.slide.urm.authenticator.URMDBTransactionException;
import org.apache.slide.urm.authenticator.userdb.URMMetadataPropsSpi;
import org.apache.slide.urm.common.URMCloseConnectionException;
import org.apache.slide.urm.common.URMConfigurator;
import org.apache.slide.urm.common.URMConnectionException;
import org.apache.slide.urm.common.URMDeleteException;
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.common.impl.tamino.URMTaminoConnection;
import org.apache.slide.urm.common.impl.tamino.URMTaminoConnectionHandler;
import org.apache.slide.urm.utils.messagelogger.MessageLogger;
import org.apache.slide.urm.utils.URMFifoCache;
import com.softwareag.tamino.db.api.common.TAccessFailureException;

/**
 * Service Provider Interface implementation for the MetaData properties via Tamino.
 *
 * @author eckehard.hermann@softwareag.com
 * @author dieter.kessler@softwareag.com
 * @author zsolt.sasvarie@softwareag.com
 */
public class URMMetadataPropsSpiTamino implements URMMetadataPropsSpi {

    private static org.apache.log4j.Logger msLogger =
        org.apache.log4j.Logger.getLogger(URMMetadataPropsSpiTamino.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_WRITEABLE = "writeable";
    private static final int UPDATE = 1;
    private static final int QUERY = 2;
    private static final int CONNPOOL_INIT_SIZE = 1;
    private static URMFifoCache userPropertyCache = null;
    private static URMFifoCache groupPropertyCache = null;
    
    private String mDatabaseUri = null;
    private String mCollectionName = null;
    private TConnection mConnection = null;
//    private TXMLObjectAccessor mObjectAccessor = null;
    private TConnectionPoolManager mConnectionPoolManager = null;
    private URMTaminoConnectionHandler mConn = null;
    private String mPoolName = null;
    private String mUserName = null;
    private String mUserPwd = null;
    private String mUserDomain = null;
    private Integer mConnPoolMax = null;
    private Integer mConnPoolTimeout = null;
    private boolean mInTransaction = false;
    private Set mWriteableUserPropKeys = new HashSet();
    private Set mAvailableUserPropKeys = new HashSet();
    private Set mWriteableGroupPropKeys = new HashSet();
    private Set mAvailableGroupPropKeys = new HashSet();
    private Object mOwner = null;
    private URMConfigurator mConfigurator = null;
    
    private boolean mIsAlreadyClosed = false;
    
    public URMMetadataPropsSpiTamino(URMConfigurator metadataPropsConf, Object owner) throws URMConnectionException {
        mConfigurator = metadataPropsConf;
        mOwner = owner;
        
        String userName = null;
        String userPwd = null;
        String userDomain = null;
        Integer connPoolMax = null;
        Integer connPoolTimeout = null;

        if (msLogger.isInfoEnabled())
            MessageLogger.logMessage(msLogger, "URMCOI0019", getClass().getName());
        URMConfigurator attrConf = metadataPropsConf.getSubConfigurator("/Authenticator/Administrator");
        if (attrConf != null) {
            Iterator attrs = URMConfiguratorUtil.getAttributeList(attrConf, msLogger);
            while (attrs != null && attrs.hasNext()) {
                URMConfigurator attrconf = ((URMConfigurator)attrs.next());
                Properties attr = attrconf.getProperties();
                if (attr == null)
                    continue;
                String attrname = attr.getProperty(ATTR_NAME);
                if (attrname == null)
                    continue;
                if (msLogger.isInfoEnabled())
                    MessageLogger.logMessage(msLogger, "URMCOI0024", attrname, attr.getProperty(ATTR_VALUE));
                if (attrname.equals("databaseUri")) {
                    mDatabaseUri = attr.getProperty(ATTR_VALUE);
                }
                else if (attrname.equals("collectionName")) {
                    mCollectionName = attr.getProperty(ATTR_VALUE);
                }
                else if (attrname.equals("databaseAccount"))
                    userName = attr.getProperty(ATTR_VALUE);
                else if (attrname.equals("databasePassword"))
                    userPwd = attr.getProperty(ATTR_VALUE);
                else if (attrname.equals("databaseDomain"))
                    userDomain = attr.getProperty(ATTR_VALUE);
                else if (attrname.equals("connPoolMaxConns"))
                    connPoolMax = new Integer((String)attr.get(ATTR_VALUE));
                else if (attrname.equals("connPoolTimeout"))
                    connPoolTimeout = new Integer((String)attr.get(ATTR_VALUE));
            }
        }

        mUserName = (userName == null ? "" : userName);
        mUserPwd = (userPwd == null ? "" : userPwd);
        mUserDomain = (userDomain == null ? "" : userDomain);
        mConnPoolMax = (connPoolMax == null ? new Integer("60") : connPoolMax);
        mConnPoolTimeout = (connPoolTimeout == null ? new Integer("600") : connPoolTimeout);
        
        if (msLogger.isDebugEnabled())
            MessageLogger.logMessage(msLogger, "URMTRQ0001", mDatabaseUri, mCollectionName);
/*
        mConnection = getConnection(userDomain, userName, userPwd);
        try {
            mConnection.useAutoCommitMode();
        } catch (TTransactionModeChangeException e) {
            throw new URMConnectionException(msLogger, "F", e);
        }
        objectAccessor = mConnection.newXMLObjectAccessor(
                            TAccessLocation.newInstance(mCollectionName),
                            TDOMObjectModel.getInstance() );
        if (objectAccessor == null)
            throw new URMConnectionException(MessageLogger.getAndLogMessage(
                    msLogger, "URMTRC0002", mDatabaseUri, mCollectionName));
        if (msLogger.isInfoEnabled())
            MessageLogger.logMessage(msLogger, "URMCOI0018", getClass().getName());
*/
        
        URMConfigurator userdbConf = metadataPropsConf.getSubConfigurator("/Authenticator/Administrator/UserDatabase");
        if (userdbConf != null) {
            Iterator attrs = URMConfiguratorUtil.getAttributeList(userdbConf, msLogger);
            while (attrs != null && attrs.hasNext()) {
                String str_allCacheSize = null;
                URMConfigurator attrconf = ((URMConfigurator)attrs.next());
                Properties attr = attrconf.getProperties();
                if (attr == null)
                    continue;
                String attrname = attr.getProperty(ATTR_NAME);
                if (attrname == null)
                    continue;
                if (msLogger.isInfoEnabled())
                    MessageLogger.logMessage(msLogger, "URMCOI0024", attrname, attr.getProperty(ATTR_VALUE));
                if (attrname.equals("allCacheSize")) {
                    str_allCacheSize = attr.getProperty(ATTR_VALUE);
                    int size = (new Integer(str_allCacheSize)).intValue();
                    if (size > 0) {
                        if (userPropertyCache == null) userPropertyCache = new URMFifoCache(size);
                        if (groupPropertyCache == null) groupPropertyCache = new URMFifoCache(size);
                    }
                }
                
            }
        }
        
        // user property names
        URMConfigurator conf = metadataPropsConf.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;
                String propname = prop.getProperty(PROP_NAME);
                if (propname == null)
                    continue;
                mAvailableUserPropKeys.add(propname);
                
                String writeable = prop.getProperty(PROP_WRITEABLE);
                if (writeable.equalsIgnoreCase("true")) {
                    mWriteableUserPropKeys.add(propname);
                }
            }
        }

        // group property names
        conf = metadataPropsConf.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;
                mAvailableGroupPropKeys.add(propname);
                
                String writeable = prop.getProperty(PROP_WRITEABLE);
                if (writeable.equalsIgnoreCase("true")) {
                    mWriteableGroupPropKeys.add(propname);
                }
            }
        }
    }

    public URMMetadataPropsSpi duplicateObject(Object owner) throws URMConnectionException {
        return new URMMetadataPropsSpiTamino(mConfigurator, owner);
    }
    
    
    public Set getWriteableUserPropKeys() {
        return mWriteableUserPropKeys;
    }
    
    public Set getWriteableGroupPropKeys() {
       return mWriteableGroupPropKeys;
    }
    
    public Set getAvailableUserPropKeys() {
        return mAvailableUserPropKeys;
    }
    
    public Set getAvailableGroupPropKeys() {
        return mAvailableGroupPropKeys;
    }
  
    public Properties getProperties(String name, String domain, boolean isUser)
            throws URMForbiddenException, URMInternalServerException {

    try {
        Properties props = null;
        String cacheId = null;
        if (domain != null) {
            cacheId = name + "\\" + domain;
        } else {
            cacheId = name;
        }
        
        // check if properties have been cached
        if (accessCacheAllowed()) {
            if (isUser) {
                if(userPropertyCache != null)
                    props = (Properties)userPropertyCache.get(cacheId);
            }
            else
                if (groupPropertyCache != null)
                    props = (Properties)groupPropertyCache.get(cacheId);
            if (props != null) return props;
        }
        TXMLObjectAccessor objectAccessor = null;
        URMTaminoConnection tConn = null;
        if ((tConn = prologue(QUERY)) == null)
            throw new URMInternalServerException(MessageLogger.getAndLogMessage(msLogger, "URMCOE0004"));
        objectAccessor = tConn.getAccessor();

        props = new Properties();
        
        synchronized (objectAccessor) {
            String choicename = isUser ? "$b/UserName" : "$b/GroupName";
            TXQuery query = TXQuery.newInstance("for    $b in input()/UserProperties " +
                        "where " + choicename + " = '" + name + "' and " +
                        (domain != null ? choicename + "[@Domain = '"+domain+"'] "
                            : choicename + "[not(@Domain)] ") +
                        "return $b/Properties/Property");
            try {
                TResponse response = objectAccessor.xquery(query);
                TXMLObjectIterator iter = response.getXMLObjectIterator();
                Vector roles = new Vector();
                while (iter != null && iter.hasNext()) {
                    TXMLObject tobj = iter.next();
                    Object obj_el = tobj != null ? tobj.getElement() : null;
                    if (obj_el != null && obj_el instanceof Element) {
                        String pkey = null, pval = null;
                        Node currNode = ((Element)obj_el).getFirstChild();
                        for (;currNode != null; currNode = currNode.getNextSibling()) {
                            Node tnode = currNode.getFirstChild();
                            while (tnode != null && tnode.getNodeType() != Node.TEXT_NODE)
                                tnode = tnode.getNextSibling();
                            if (tnode == null)
                                continue;
                            String tval = tnode.getNodeValue();
                            if ("Value".equals(currNode.getNodeName())) {
                                pval = tval;
                                if (pkey != null) {
                                    if (isUser) {
                                        if (mAvailableUserPropKeys.contains(pkey)) props.put(pkey, pval);
                                    } else {
                                        if (mAvailableGroupPropKeys.contains(pkey))props.put(pkey, pval);
                                    }
                                    break;
                                }
                            } else
                            if ("Key".equals(currNode.getNodeName())) {
                                pkey = tval;
                                if (pval != null) {
                                    if (isUser) {
                                        if (mAvailableUserPropKeys.contains(pkey)) props.put(pkey, pval);
                                    } else {
                                        if (mAvailableGroupPropKeys.contains(pkey))props.put(pkey, pval);
                                    }
                                    break;
                                }
                            }
                        }
                    }
                }
                if (tConn.accessCache())
                    if (isUser) {
                        if(userPropertyCache != null) userPropertyCache.add(cacheId,props);
                    }
                    else
                        if (groupPropertyCache != null) groupPropertyCache.add(cacheId,props);
                return props;
            } catch (TXQueryException e) {
                throw new URMInternalServerException(msLogger, "E", e);
            } catch (TNoSuchXMLObjectException e) {
                throw new URMInternalServerException(msLogger, "E", e);
            } catch (TIteratorException e) {
                throw new URMInternalServerException(msLogger, "E", e);
            }
        }
    } catch (Exception e) {
        epilogue(false);
        if (e instanceof URMForbiddenException)
            throw (URMForbiddenException)e;
        else if (e instanceof URMInternalServerException)
            throw (URMInternalServerException)e;
        throw new URMInternalServerException (e); //(RuntimeException)e;
    } finally {
        epilogue(true);
    }
    }

    public synchronized void setProperties(String name, String domain, Properties inProps, boolean isUser)
            throws URMNotImplementedException, URMForbiddenException, URMUpdateException, URMDeleteException, URMInternalServerException {
    try {
        
        TXMLObjectAccessor objectAccessor = null;
        URMTaminoConnection tConn = null;
        if ((tConn = prologue(UPDATE)) == null)
            throw new URMUpdateException(MessageLogger.getAndLogMessage(msLogger, "URMCOE0004"));
        objectAccessor = tConn.getAccessor();
                        
        Properties oldprops = getProperties(name, domain, isUser);
        HashSet deletekeys = new HashSet();
        Properties insertprops = new Properties();
        Properties replaceprops = new Properties();
        insertprops.putAll(inProps);
        
        Iterator keys = oldprops.keySet().iterator();
        String key = null, value = null;
        while (keys.hasNext()) {
            key = (String)keys.next();
            value = insertprops.getProperty(key);
            if (value == null)
                continue;
            insertprops.remove(key);
            if (value.length() == 0)
                deletekeys.add(key);
            else
                replaceprops.setProperty(key, value);
        }
        // collect delete keys
        keys = deletekeys.iterator();
        String delkeysstr = keys.hasNext() ? ("Key = '" + (String)keys.next() + "'") : null;
        while (keys.hasNext()) {
            String nextKey = (String)keys.next();
//            if (isUser && (!mWriteableUserPropKeys.contains(nextKey))) throw new URMUpdateException(MessageLogger.getAndLogMessage(msLogger, "URMMPE0001", nextKey));
//            if (!isUser && (!mWriteableGroupPropKeys.contains(nextKey))) throw new URMUpdateException(MessageLogger.getAndLogMessage(msLogger, "URMMPE0001", nextKey));
            delkeysstr += (" or Key = '" + nextKey + "'");
        }
        // collect replace keys
        keys = replaceprops.keySet().iterator();
        HashSet replacestrs = new HashSet();
        while (keys.hasNext()) {
            key = (String)keys.next();
//            if (isUser && (!mWriteableUserPropKeys.contains(key))) throw new URMUpdateException(MessageLogger.getAndLogMessage(msLogger, "URMMPE0001", key));
//            if (!isUser && (!mWriteableGroupPropKeys.contains(key))) throw new URMUpdateException(MessageLogger.getAndLogMessage(msLogger, "URMMPE0001", key));
            //replacestrs.add("$b/Key = '" + key + "' do replace $b/Value with <Value>"+replaceprops.getProperty(key)+"</Value>");
            replacestrs.add("[Key = '" + key + "']/Value with <Value>"+replaceprops.getProperty(key)+"</Value>");
        }
        // collect insert values
        //String updateinsertpropsstr = "";
        HashSet insertstrs = new HashSet();
        for (keys = insertprops.keySet().iterator(); keys.hasNext(); ) {
            key = (String)keys.next();
//            if (isUser && (!mWriteableUserPropKeys.contains(key))) throw new URMUpdateException(MessageLogger.getAndLogMessage(msLogger, "URMMPE0001", key));
//            if (!isUser && (!mWriteableGroupPropKeys.contains(key))) throw new URMUpdateException(MessageLogger.getAndLogMessage(msLogger, "URMMPE0001", key));
            if ((value = insertprops.getProperty(key)) != null) {
                insertstrs.add( "<Property><Key>" + key + "</Key><Value>" + value +
                                    "</Value></Property>" );
                /*updateinsertpropsstr += ( "insert " + insertpropsstr +
                                          " into $a/Properties " );*/
            }
        }
        String choicename = isUser ? "UserName" : "GroupName";
        synchronized (objectAccessor) {
            TXQuery query = null;
            TResponse response = null;
            if (replacestrs.size() > 0) {
                for (Iterator iter = replacestrs.iterator(); iter.hasNext();) {
                    /*query = TXQuery.newInstance(
                        "update for $a in input()/UserProperties let $b := $a/Properties/Property "+
                        "where $a/"+choicename+" = '"+name+"' and " +
                        (domain != null ? "$a/"+choicename+"/@Domain = '"+domain+"' "
                        : "not($a/"+choicename+"/@Domain) and ") + (String)iter.next());*/
                    query = TXQuery.newInstance(
                        "update replace input()/UserProperties["+choicename+" = '"+name+"' and " +
                        (domain != null ? choicename+"/@Domain = '"+domain+"'"
                        : "not("+choicename+"/@Domain)") + "]/Properties/Property" + (String)iter.next());
                    try {
                        response = objectAccessor.xquery(query);
                    } catch (TXQueryException e) {
                        throw new URMUpdateException(msLogger, "E", e);
                    }
                }
            }
            if (delkeysstr != null) {
                query = TXQuery.newInstance("update delete " +
                    "input()/UserProperties["+choicename+" = '" + name + "' and " +
                    (domain != null ? choicename+"/@Domain = '"+domain+"' "
                        : "not("+choicename+"/@Domain)") +
                     "]/Properties/Property["+delkeysstr+"]");
                try {
                    response = objectAccessor.xquery(query);
                } catch (TXQueryException e) {
                    throw new URMDeleteException(msLogger, "E", e);
                }
            }
            if (insertstrs.size() > 0) {
                /*TXQuery query = TXQuery.newInstance(
                    "update for $a in input()/UserProperties where $a/"+choicename+" = '"+name
                    +"' and " + (domain != null ? "$a/"+choicename+"/@Domain = '"+domain+"' "
                    : "not($a/"+choicename+"/@Domain)") + " do ( "+updateinsertpropsstr+")");*/
                TXMLObjectIterator oiter = null;
                for (Iterator iter = insertstrs.iterator(); iter.hasNext();) {
                    query = TXQuery.newInstance("update insert " + (String)iter.next() +
                        " into input()/UserProperties["+choicename+" = '" + name + "' and " +
                        (domain != null ? choicename + "/@Domain = '"+domain+"' "
                                : "not("+choicename+"/@Domain)") + "]/Properties");
                    try {
                        response = objectAccessor.xquery(query);
                    } catch (TXQueryException e) {
                        throw new URMUpdateException(msLogger, "E", e);
                    }
                    if (oiter == null) {
                        oiter = response.getXMLObjectIterator();
                        if (!oiter.hasNext()) {
                            oiter = null;
                            break;
                        }
                    }
                }
                if (oiter == null) {
                    String insertpropsstr = "";
                    for (Iterator iter = insertstrs.iterator(); iter.hasNext();)
                        insertpropsstr += (String)iter.next();
                    TXMLObject xmlobj = TXMLObject.newInstance(
                        "<UserProperties><" + choicename +
                        (domain != null ? " Domain=\""+domain+"\"" : "")+">" + name +
                        "</"+choicename+">" +
                        "<Properties>" + insertpropsstr + "</Properties> " +
                        "</UserProperties>");
                    try {
                        response = objectAccessor.insert(xmlobj);
                    } catch (TInsertException e) {
                        throw new URMUpdateException(msLogger, "E", e);
                    }
                }
            }
        }
        if (tConn.accessCache()) {
            String cacheId = null;
            if (domain != null) cacheId = name + "\\" + domain;
            else                cacheId = name;
            if (isUser) {
                if(userPropertyCache != null) userPropertyCache.remove(cacheId);
            }
            else
                if (groupPropertyCache != null) groupPropertyCache.remove(cacheId);
        }
        oldprops = getProperties(name, domain, isUser);
        if (oldprops.size() == 0) {
            synchronized (objectAccessor) {
                TQuery query = TQuery.newInstance("UserProperties["+choicename+
                                    " = '" + name + "' and " +
                                    (domain != null ? choicename + "/@Domain = '"+domain+"' "
                                        : "not(" + choicename + "/@Domain)")+"]");
                try {
                    TResponse response = objectAccessor.delete(query);
                } catch (TDeleteException e) {
                    throw new URMDeleteException(msLogger, "E", e);
                }
            }
        }
    } catch (Exception e) {
        epilogue(false);
        if (e instanceof URMNotImplementedException)
            throw (URMNotImplementedException)e;
        else if (e instanceof URMForbiddenException)
            throw (URMForbiddenException)e;
        else if (e instanceof URMUpdateException)
            throw (URMUpdateException)e;
        else if (e instanceof URMDeleteException)
            throw (URMDeleteException)e;
        else if (e instanceof URMInternalServerException)
            throw (URMInternalServerException)e;
        throw new URMInternalServerException (e); //(RuntimeException)e;
    } finally {
        epilogue(true);
    }
    }
    
    public void close() throws URMCloseConnectionException {
        // use Tamino connection pool.
        // 'close' not required
        return;
/*
        try {
            if (!mIsAlreadyClosed) {
                mIsAlreadyClosed = true;
                mConnection.close();
            }
        } catch (TConnectionCloseException e) {
            throw new URMCloseConnectionException(msLogger, "E", e);
        }
*/
    }

    private TConnection getConnection(String domain, String userId, String password)
            throws URMConnectionException {
        TConnection conn = null;
        try {
            TConnectionFactory cf = TConnectionFactory.getInstance();
            if (msLogger.isDebugEnabled())
                MessageLogger.logMessage(msLogger, "URMTRQ0003", mDatabaseUri, mCollectionName,
                                    userId == null ? "" : userId, domain == null ? "" : domain);
            if (domain == null)
                conn = cf.newConnection(mDatabaseUri, userId, password);
            else
                conn = cf.newConnection(mDatabaseUri, domain, userId, password);
        } catch (TServerNotAvailableException  ex) {
            TException de = ex.getDeepestTException();
            if (de == null)
                de = ex;
            throw new URMConnectionException(MessageLogger.getAndLogMessage(msLogger, "F", de));
        }
        if (msLogger.isDebugEnabled())
            MessageLogger.logMessage(msLogger, "URMTRI0004", mDatabaseUri, mCollectionName,
                                    userId == null ? "" : userId, domain == null ? "" : domain);
        conn.setIsolationDegree(TIsolationDegree.STABLE_DOCUMENT);
        return conn;
    }

    public void syncronizeUsersGroupsRoles(String domain, Set existingUsers, Set existingGroups)
            throws URMDeleteException, URMNotImplementedException, URMForbiddenException, URMInternalServerException {
        // for users
        Set set = getNeedlessUsers(domain, existingUsers);
        removeUsersProperiesEntries(set, domain);
        set = getNeedlessGroups(domain, existingGroups);
        removeGroupsProperiesEntries(set, domain);
    }
            
    public Set getAvailableDomains() throws URMInternalServerException {
        // if tamino will have the function distinct-values(...) it will be easier and faster

    try {
        TXMLObjectAccessor objectAccessor = null;
        URMTaminoConnection tConn = null;
        if ((tConn = prologue(QUERY)) == null)
            throw new URMInternalServerException(MessageLogger.getAndLogMessage(msLogger, "URMCOE0004"));
        objectAccessor = tConn.getAccessor();

        HashSet dmns = new HashSet();
        try {
            TXQuery query = TXQuery.newInstance("count(input()/UserProperties[not(//@Domain)])");
            TResponse response = objectAccessor.xquery(query);
            TXMLObjectIterator iter = response.getXMLObjectIterator();
            if (iter != null && iter.hasNext()) {
                TXMLObject tobj = iter.next();
                Object obj_el = tobj != null ? tobj.getElement() : null;
                if (obj_el != null && obj_el instanceof Element) {
                    Node tnode = ((Node)obj_el).getFirstChild();
                    if (tnode != null && tnode.getNodeType() == Node.TEXT_NODE) {
                        String tval = tnode.getNodeValue();
                        if (tval != null) {
                            int num = 0;
                            try {
                                num = Integer.parseInt(tval);
                            } catch (NumberFormatException e1) {}
                            if (num > 0)
                                dmns.add(null);
                        }
                    }
                }
            }

            query = TXQuery.newInstance("for $b in input()/UserProperties" +
                                        " where $b//@Domain return $b//@Domain");
            response = objectAccessor.xquery(query);
            iter = response.getXMLObjectIterator();
            while (iter != null && iter.hasNext()) {
                TXMLObject tobj = iter.next();
                Object obj_el = tobj != null ? tobj.getElement() : null;
                if (obj_el != null && obj_el instanceof Element) {
                    Node tnode = ((Node)obj_el).getFirstChild();
                    if (tnode != null && tnode.getNodeType() == Node.TEXT_NODE) {
                        String tval = tnode.getNodeValue();
                        if (tval != null && !dmns.contains(tval))
                            dmns.add(tval);
                    }
                }
            }
        } catch (TXQueryException e) {
            throw new URMInternalServerException(msLogger, "E", e);
        } catch (TNoSuchXMLObjectException e) {
            throw new URMInternalServerException(msLogger, "E", e);
        } catch (TIteratorException e) {
            throw new URMInternalServerException(msLogger, "E", e);
        }
        
        return dmns;
    } catch (Exception e) {
        epilogue(false);
        if (e instanceof URMNotImplementedException)
            throw (URMInternalServerException)e;
        throw new URMInternalServerException (e); //(RuntimeException)e;
    } finally {
        epilogue(true);
    }
    }

    private Set getNeedlessUsers(String domain, Set existingUsers)
            throws URMNotImplementedException, URMForbiddenException, URMInternalServerException{

    try {
        TXMLObjectAccessor objectAccessor = null;
        URMTaminoConnection tConn = null;
        if ((tConn = prologue(QUERY)) == null)
            throw new URMInternalServerException(MessageLogger.getAndLogMessage(msLogger, "URMCOE0004"));
        objectAccessor = tConn.getAccessor();

        HashSet metausrs = new HashSet();
        TXQuery query = TXQuery.newInstance("for  $b in input()/UserProperties " +
                    "where " + (domain != null ? "$b/UserName/@Domain = '"+domain+"'"
                        : "not($b/UserName/@Domain)") + " return $b/UserName");
        try {
            TResponse response = objectAccessor.xquery(query);
            TXMLObjectIterator iter = response.getXMLObjectIterator();
            while (iter != null && iter.hasNext()) {
                TXMLObject tobj = iter.next();
                Object obj_el = tobj != null ? tobj.getElement() : null;
                if (obj_el != null && obj_el instanceof Element) {
                    Node tnode = ((Node)obj_el).getFirstChild();
                    while (tnode != null && tnode.getNodeType() != Node.TEXT_NODE)
                        tnode = tnode.getNextSibling();
                    if (tnode == null)
                        continue;
                    String tval = tnode.getNodeValue();
                    if (tval != null)
                        metausrs.add(tval);
                }
            }
        } catch (TXQueryException e) {
            throw new URMInternalServerException(msLogger, "E", e);
        } catch (TNoSuchXMLObjectException e) {
            throw new URMInternalServerException(msLogger, "E", e);
        } catch (TIteratorException e) {
            throw new URMInternalServerException(msLogger, "E", e);
        }
        
        for (Iterator iter = existingUsers.iterator();iter.hasNext();)
            metausrs.remove(iter.next());
        
        return metausrs;

    } catch (Exception e) {
        epilogue(false);
        if (e instanceof URMNotImplementedException)
            throw (URMNotImplementedException)e;
        else if (e instanceof URMForbiddenException)
            throw (URMForbiddenException)e;
        else if (e instanceof URMInternalServerException)
            throw (URMInternalServerException)e;
        throw new URMInternalServerException (e); //(RuntimeException)e;
    } finally {
        epilogue(true);
    }
    }

    private Set getNeedlessGroups(String domain, Set existingGroups)
            throws URMNotImplementedException, URMForbiddenException, URMInternalServerException{

    try {
        TXMLObjectAccessor objectAccessor = null;
        URMTaminoConnection tConn = null;
        if ((tConn = prologue(QUERY)) == null)
            throw new URMInternalServerException(MessageLogger.getAndLogMessage(msLogger, "URMCOE0004"));
        objectAccessor = tConn.getAccessor();

        HashSet metagrps = new HashSet();
        TXQuery query = TXQuery.newInstance("for  $b in input()/UserProperties " +
                    "where " + (domain != null ? "$b/GroupName/@Domain = '"+domain+"'"
                        : "not($b/GroupName/@Domain)") + " return $b/GroupName");
        try {
            TResponse response = objectAccessor.xquery(query);
            TXMLObjectIterator iter = response.getXMLObjectIterator();
            while (iter != null && iter.hasNext()) {
                TXMLObject tobj = iter.next();
                Object obj_el = tobj != null ? tobj.getElement() : null;
                if (obj_el != null && obj_el instanceof Element) {
                    Node tnode = ((Node)obj_el).getFirstChild();
                    while (tnode != null && tnode.getNodeType() != Node.TEXT_NODE)
                        tnode = tnode.getNextSibling();
                    if (tnode == null)
                        continue;
                    String tval = tnode.getNodeValue();
                    if (tval != null)
                        metagrps.add(tval);
                }
            }
        } catch (TXQueryException e) {
            throw new URMInternalServerException(msLogger, "E", e);
        } catch (TNoSuchXMLObjectException e) {
            throw new URMInternalServerException(msLogger, "E", e);
        } catch (TIteratorException e) {
            throw new URMInternalServerException(msLogger, "E", e);
        }
        
        for (Iterator iter = existingGroups.iterator();iter.hasNext();)
            metagrps.remove(iter.next());
        
        return metagrps;
    } catch (Exception e) {
        epilogue(false);
        if (e instanceof URMNotImplementedException)
            throw (URMNotImplementedException)e;
        else if (e instanceof URMForbiddenException)
            throw (URMForbiddenException)e;
        else if (e instanceof URMInternalServerException)
            throw (URMInternalServerException)e;
        throw new URMInternalServerException (e); //(RuntimeException)e;
    } finally {
        epilogue(true);
    }
    }
    
    private void removeUsersProperiesEntries(Set users, String domain) throws URMDeleteException, URMInternalServerException {

    try {
        String cacheId = null;
        TXMLObjectAccessor objectAccessor = null;
        URMTaminoConnection tConn = null;
        if ((tConn = prologue(QUERY)) == null)
            throw new URMDeleteException(MessageLogger.getAndLogMessage(msLogger, "URMCOE0004"));
        objectAccessor = tConn.getAccessor();

        if (users != null && users.size() > 0) {
            synchronized (objectAccessor) {
                for (Iterator iter = users.iterator(); iter.hasNext();) {
                    String name = (String)iter.next();
                    TQuery query = TQuery.newInstance(
                            "UserProperties[UserName = '" + name + "' and " +
                            (domain != null ? "UserName/@Domain = '"+domain+"' "
                                            : "not(UserName/@Domain)")+"]");
                    try {
                        TResponse response = objectAccessor.delete(query);
                        if (domain != null) {
                            cacheId = name + "\\" + domain;
                        } else {
                            cacheId = name;
                        }
                        if ((userPropertyCache != null) && tConn.accessCache()) userPropertyCache.remove(cacheId);
                        
                    } catch (TDeleteException e) {
                        throw new URMDeleteException(msLogger, "E", e);
                    }
                }
            }
        }
    } catch (Exception e) {
        epilogue(false);
        if (e instanceof URMDeleteException)
            throw (URMDeleteException)e;
        throw new URMInternalServerException (e); //(RuntimeException)e;
    } finally {
        epilogue(true);
    }
    }

    public void deleteProperiesOfUser(String user, String domain) throws URMDeleteException, URMInternalServerException {
    try {
        String cacheId = null;
        TXMLObjectAccessor objectAccessor = null;
        URMTaminoConnection tConn = null;
        if ((tConn = prologue(QUERY)) == null)
            throw new URMDeleteException(MessageLogger.getAndLogMessage(msLogger, "URMCOE0004"));
        objectAccessor = tConn.getAccessor();

        if (user != null) {
            synchronized (objectAccessor) {
                   
                TQuery query = TQuery.newInstance(
                        "UserProperties[UserName = '" + user + "' and " +
                            (domain != null ? "UserName/@Domain = '"+domain+"' "
                                            : "not(UserName/@Domain)")+"]");
                try {
                    TResponse response = objectAccessor.delete(query);
                    if (domain != null) {
                        cacheId = user + "\\" + domain;
                    } else {
                        cacheId = user;
                    }
                    if ((userPropertyCache != null) && tConn.accessCache()) userPropertyCache.remove(cacheId);
 
                } catch (TDeleteException e) {
                    // do not throw "No matching document found" exception
                    Exception deepest_throwable = e.getDeepestTException();
                    String code = null;
                    if (deepest_throwable != null && deepest_throwable instanceof TAccessFailureException) {
                        code = ((TAccessFailureException)deepest_throwable).getCode();
                    }
                    if (code == null || !code.equals("INOXIE8300"))
                        throw new URMDeleteException(msLogger, "E", e);
                }
            }
        }
    } catch (Exception e) {
        epilogue(false);
        if (e instanceof URMDeleteException)
            throw (URMDeleteException)e;
        throw new URMInternalServerException (e); //(RuntimeException)e;
    } finally {
        epilogue(true);
    }
    }
    
    private void removeGroupsProperiesEntries(Set groups, String domain) throws URMDeleteException, URMInternalServerException {
    try {
            String cacheId;
        TXMLObjectAccessor objectAccessor = null;
        URMTaminoConnection tConn = null;
        if ((tConn = prologue(UPDATE)) == null)
            throw new URMDeleteException(MessageLogger.getAndLogMessage(msLogger, "URMCOE0004"));
        objectAccessor = tConn.getAccessor();

        if (groups != null && groups.size() > 0) {
            synchronized (objectAccessor) {
                for (Iterator iter = groups.iterator(); iter.hasNext();) {
                    String name = (String)iter.next();
                    TQuery query = TQuery.newInstance(
                            "UserProperties[GroupName = '" + name + "' and " +
                            (domain != null ? "GroupName/@Domain = '"+domain+"' "
                                            : "not(GroupName/@Domain)")+"]");
                    try {
                        TResponse response = objectAccessor.delete(query);
                        if (domain != null) {
                            cacheId = name + "\\" + domain;
                        } else {
                            cacheId = name;
                        }
                        if ((groupPropertyCache != null) && tConn.accessCache()) groupPropertyCache.remove(cacheId);
                    } catch (TDeleteException e) {
                        throw new URMDeleteException(msLogger, "E", e);
                    }
                }
            }
        }
    } catch (Exception e) {
        epilogue(false);
        if (e instanceof URMDeleteException)
            throw (URMDeleteException)e;
        throw new URMInternalServerException (e); //(RuntimeException)e;
    } finally {
        epilogue(true);
    }
    }
    
    public void deleteProperiesOfGroup(String group, String domain) throws URMDeleteException, URMInternalServerException {
    try {
            String cacheId;
        TXMLObjectAccessor objectAccessor = null;
        URMTaminoConnection tConn = null;
        if ((tConn = prologue(UPDATE)) == null)
            throw new URMDeleteException(MessageLogger.getAndLogMessage(msLogger, "URMCOE0004"));
        objectAccessor = tConn.getAccessor();

        if (group != null) {
            synchronized (objectAccessor) {
                TQuery query = TQuery.newInstance(
                        "UserProperties[GroupName = '" + group + "' and " +
                        (domain != null ? "GroupName/@Domain = '"+domain+"' "
                                        : "not(GroupName/@Domain)")+"]");
                try {
                    TResponse response = objectAccessor.delete(query);
                    if (domain != null) {
                        cacheId = group + "\\" + domain;
                    } else {
                        cacheId = group;
                    }
                    if ((groupPropertyCache != null) && tConn.accessCache()) groupPropertyCache.remove(cacheId);
                } catch (TDeleteException e) {
                    // do not throw "No matching document found" exception
                    Exception deepest_throwable = e.getDeepestTException();
                    String code = null;
                    if (deepest_throwable != null && deepest_throwable instanceof TAccessFailureException) {
                        code = ((TAccessFailureException)deepest_throwable).getCode();
                    }
                    if (code == null || !code.equals("INOXIE8300"))
                        throw new URMDeleteException(msLogger, "E", e);
                }
                
            }
        }
    } catch (Exception e) {
        epilogue(false);
        if (e instanceof URMDeleteException)
            throw (URMDeleteException)e;
        throw new URMInternalServerException (e); //(RuntimeException)e;
    } finally {
        epilogue(true);
    }
    }
    
    /*
     * Always perform this at the beginning of each method
     */
    private URMTaminoConnection prologue(int mode)
        throws URMInternalServerException {
        TXMLObjectAccessor objectAccessor = null;
        URMTaminoConnection tConn = null;
        
        if (mIsAlreadyClosed) {
            return null;
        }
        
        if (mConn == null)
            try {
                mConn = URMTaminoConnectionHandler.getInstance(mConfigurator);
            } catch (Exception e) {
                throw new URMInternalServerException(msLogger, "URMCOF0047", e);
            }

        try {
            tConn = mConn.getObjectAccessor(mode, mOwner);
        } catch (URMDBTransactionException e) {
            throw new URMInternalServerException(msLogger, "URMCOF0047", e);
        } catch (URMConnectionException e) {
        if (msLogger.isInfoEnabled())
            throw new URMInternalServerException(msLogger, "URMCOF0047", e);
    }
        return tConn;
    }
    
    /*
     * Always perform this at the end of each method
     */
    private boolean epilogue(boolean commit) throws URMInternalServerException {
        try {
            return mConn.releaseConnection(commit, mOwner);
        } catch (URMConnectionException e) {
            throw new URMInternalServerException(msLogger, "URMCOF0048", e);
        }
    }
    
        /*
     * Check via the TaminoConnection object, if we can access the cache
     */
    private boolean accessCacheAllowed()
        throws URMInternalServerException {
        if (mConn == null)
            try {
                mConn = URMTaminoConnectionHandler.getInstance(mConfigurator);
            } catch (Exception e) {
                throw new URMInternalServerException(msLogger, "URMCOF0047", e);
            }

        return mConn.accessCache(mOwner);
    }

    public void beginTransaction() throws URMDBTransactionException, URMInternalServerException {
        // nothing to do here
    }

    public void endTransaction(boolean manner) throws URMDBTransactionException, URMInternalServerException {

        // need to clear all caches
        if (manner == true) {
            if (userPropertyCache != null) userPropertyCache.clear();
            if (groupPropertyCache != null) groupPropertyCache.clear();
        }
    }

}
