/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/urm/org/apache/slide/urm/common/impl/tamino/URMTaminoConnectionHandler.java,v 1.6 2005/03/02 10:53:35 eckehard Exp $
 * $Revision: 1.6 $
 * $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.common.impl.tamino;

import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import com.softwareag.tamino.db.api.accessor.TAccessLocation;
import com.softwareag.tamino.db.api.accessor.TXMLObjectAccessor;
import com.softwareag.tamino.db.api.common.TException;
import com.softwareag.tamino.db.api.connection.TConnection;
import com.softwareag.tamino.db.api.connection.TConnectionCloseException;
import com.softwareag.tamino.db.api.connection.TConnectionFactory;
import com.softwareag.tamino.db.api.connection.TConnectionPoolDescriptor;
import com.softwareag.tamino.db.api.connection.TConnectionNotAvailableException;
import com.softwareag.tamino.db.api.connection.TConnectionPoolManager;
import com.softwareag.tamino.db.api.connection.TIsolationDegree;
import com.softwareag.tamino.db.api.connection.TLocalTransaction;
import com.softwareag.tamino.db.api.connection.TLockMode;
import com.softwareag.tamino.db.api.connection.TLockwaitMode;
import com.softwareag.tamino.db.api.connection.TServerNotAvailableException;
import com.softwareag.tamino.db.api.connection.TTransactionException;
import com.softwareag.tamino.db.api.connection.TTransactionModeChangeException;
import com.softwareag.tamino.db.api.objectModel.dom.TDOMObjectModel;
import org.apache.slide.urm.accesscontroler.impl.tamino.URMAccessControlerSpiTamino;
import org.apache.slide.urm.authenticator.URMDBTransactionException;
import org.apache.slide.urm.authenticator.rolemanager.impl.tamino.URMRoleManagerSpiTamino;
import org.apache.slide.urm.common.URMConfigurator;
import org.apache.slide.urm.common.URMConnectionException;
import org.apache.slide.urm.common.impl.URMConfiguratorUtil;
import org.apache.slide.urm.utils.accesscontrolercache.URMAccessControlerFifoCache;
import org.apache.slide.urm.utils.messagelogger.MessageLogger;
import org.apache.slide.urm.utils.validatorcache.URMListener;

/**
 * Organizes the central access to the MetaDataStore implemented in Tamino.
 *
 * @author eckehard.hermann@softwareag.com
 * @author dieter.kessler@softwareag.com
 * @author zsolt.sasvarie@softwareag.com
 */
public class URMTaminoConnectionHandler {

    private static final String ATTR_NAME = "name";
    private static final String ATTR_VALUE = "value";
    private static final int UPDATE = 1;
    private static final int QUERY = 2;
    private static final int START_TA = 3;
    private static final int COMMIT_TA = 4;
    private static final int ROLLBACK_TA = 5;
    private static final int CONNPOOL_INIT_SIZE = 1;
    private static URMTaminoConnectionHandler mThisInstance = null;
    private static org.apache.log4j.Logger msLogger =
        org.apache.log4j.Logger.getLogger(URMRoleManagerSpiTamino.class.getName());

    private String mDatabaseUri = null;
    private TConnectionPoolManager mConnectionPoolManager = null;
    private String mCollectionName = 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 Map mConnections = Collections.synchronizedMap(new HashMap(5));

  /*
   * Constructor
   * Read connection parameters from the config file.
   *
   * @param adminConf
   */
  private URMTaminoConnectionHandler(URMConfigurator adminConf)
  {
      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 = adminConf.getSubConfigurator("/Authenticator/Administrator");
      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 = (String)attr.get(ATTR_NAME);
          if (attrname == null)
              continue;
          if (msLogger.isInfoEnabled())
              MessageLogger.logMessage(msLogger, "URMCOI0024", attrname, (String)attr.get(ATTR_VALUE));
          if (attrname.equals("databaseUri")) {
              mDatabaseUri = (String)attr.get(ATTR_VALUE);
          }
          else if (attrname.equals("collectionName")) {
              mCollectionName = (String)attr.get(ATTR_VALUE);
          }
          else if (attrname.equals("databaseAccount"))
              userName = (String)attr.get(ATTR_VALUE);
          else if (attrname.equals("databasePassword"))
              userPwd = (String)attr.get(ATTR_VALUE);
          else if (attrname.equals("databaseDomain"))
              userDomain = (String)attr.get(ATTR_VALUE);
          else if (attrname.equals("connPoolMaxConns"))
              connPoolMax = new Integer((String)attr.get(ATTR_VALUE));
          else if (attrname.equals("connPoolTimeout"))
              connPoolMax = 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);
        
  }

  /*
   * This is an implementation of a singleton.
   * Always return the same class instance.
   */
  static public synchronized URMTaminoConnectionHandler getInstance(URMConfigurator roleAdminConf)
  {
      if (mThisInstance == null)
        mThisInstance = new URMTaminoConnectionHandler(roleAdminConf);
        
      return mThisInstance;
  }
  
  /*
   * Return the access capability for the Caches:
   * true: no user TA is pending, or user TA with only queries so far
   * false: user TA has started and updates have been made
   */
  public synchronized boolean accessCache(Object princThis){
      
      String currentThreadName = java.lang.Thread.currentThread().getName();

      String key = new String(princThis.toString() + currentThreadName);

      if (mConnections.containsKey(key)) {
          // entry exist. Return now the status of the TaminoConnection object
          URMTaminoConnection parms = (URMTaminoConnection)mConnections.get(key);
          return parms.accessCache();
      } else {
          // no entry found, access to cache is granted
          return true;
      }
  }
  
  /*
   * Return the actionDeltaCache object of the current transaction
   */
  public Map getActionDeltaCache(Object princThis) {

      String currentThreadName = java.lang.Thread.currentThread().getName();

      String key = new String(princThis.toString() + currentThreadName);

      if (mConnections.containsKey(key)) {
          // entry exist. Return now the status of the TaminoConnection object
          URMTaminoConnection parms = (URMTaminoConnection)mConnections.get(key);
          return parms.getActionDeltaCache();
      } else {
          // no entry found, access to cache is granted
          return null;
      }
  }
   
   /*
    * Return the aclDeltaCache object of the current transaction
    */
  public Map getAclDeltaCache(Object princThis) {

      String currentThreadName = java.lang.Thread.currentThread().getName();

      String key = new String(princThis.toString() + currentThreadName);

      if (mConnections.containsKey(key)) {
          // entry exist. Return now the status of the TaminoConnection object
          URMTaminoConnection parms = (URMTaminoConnection)mConnections.get(key);
          return parms.getAclDeltaCache();
      } else {
          // no entry found, access to cache is granted
          return null;
      }
  }
   
  /*
   * Return the TXMLObjectAccessor to the SPI-Methods
   */
  public synchronized URMTaminoConnection getObjectAccessor(int mode, Object princThis)
      throws URMConnectionException, URMDBTransactionException
  {
      TConnection conn = null;
      TXMLObjectAccessor accessor = null;
      TLocalTransaction locTrans = null;
      
      // The Level is either the current stack level or 0 for an explicit StartTA
      Integer currentThreadLevel = new Integer((mode == START_TA ? 0 : java.lang.Thread.currentThread().countStackFrames()));

      String currentThreadName = java.lang.Thread.currentThread().getName();

      String key = new String(princThis.toString() + currentThreadName);

      if (mConnections.containsKey(key)) {
          URMTaminoConnection parms = (URMTaminoConnection)mConnections.get(key);
          int threadLevel = parms.getThreadLevel();
          if (threadLevel == 0) {
              if (mode == START_TA) {
                  // Error! An open user transaction is still pending and we have a new beginTransaction here.
                  locTrans = parms.getTransaction();
                  // rollback
                  this.endTA(false, locTrans);
                  throw new URMDBTransactionException(MessageLogger.getAndLogMessage(msLogger, "URMTRQ0003"));
              }
              if (mode == UPDATE) {
//                  TConnection connLock = parms.getConnection();
//                connLock.setLockMode(TLockMode.PROTECTED);
                  TXMLObjectAccessor oAcc = parms.getAccessor();
                  oAcc.setLockMode(TLockMode.PROTECTED);
              }
          }
          if (mode == UPDATE){
              parms.updateMethod();
          }
          if ( threadLevel <= currentThreadLevel.intValue()) {
              // This is not the top entry.
              // Do not change the lock mode.
              { /* Debug
                  TConnection tc = parms.getConnection();
                  if (tc.isClosed()) {
                      if (msLogger.isDebugEnabled())
                          MessageLogger.logMessage(msLogger,
                           "URMCOE0004", "(1) " + threadLevel + " " + currentThreadLevel.intValue());
                  }
              */ }
              return parms;
          } else {
              msLogger.debug("ERROR, using TConnection objects from level '" + threadLevel +
                                     "' , current level = '" + currentThreadLevel);
              // Just replace thread level
              { /* Debug
                  TConnection tc = parms.getConnection();
                  if (tc.isClosed()) {
                      if (msLogger.isDebugEnabled())
                          MessageLogger.logMessage(msLogger,
                           "URMCOE0004", "(1) " + threadLevel + " " + currentThreadLevel.intValue());
                  }
              */ }
              parms.setThreadLevel(currentThreadLevel.intValue());
              return parms;
          }
      } else {
          // add new entry
          URMTaminoConnection parms = new URMTaminoConnection();
//          Object retVals[] = getNonPooledConnection(key, mode, princThis);
          Object retVals[] = getPooledConnection("URM", mode, princThis);
          conn = (TConnection)retVals[0];
          accessor = (TXMLObjectAccessor)retVals[1];
          locTrans = (TLocalTransaction)retVals[2];
          parms.add(currentThreadLevel.intValue(),
                    mode,
                    conn,
                    accessor,
                    locTrans);
          { /* Debug
              if (conn.isClosed()) {
                  if (msLogger.isDebugEnabled())
                      MessageLogger.logMessage(msLogger,
                       "URMCOE0004", "(2) " + currentThreadLevel.intValue());
              }
          */ }
          mConnections.put(key, parms);
          if (0 == currentThreadLevel.intValue()) {
              // create DeltaCaches, we're starting a transaction
              parms.createDeltaCaches();
          }
          return parms;
      }
  }
  
  /*
   * Release the connection from the HashMap and check if Commit() or Rollback() needs to be called.
   */
  public synchronized boolean releaseConnection(boolean commit, Object princThis)
    throws URMConnectionException
  {
      TConnection conn = null;
      TXMLObjectAccessor accessor = null;
      TLocalTransaction locTran = null;
      
      Integer currentThreadLevel = new Integer(java.lang.Thread.currentThread().countStackFrames());
      String currentThreadName = java.lang.Thread.currentThread().getName();

      String key = new String(princThis.toString() + currentThreadName);

      if (mConnections.containsKey(key)) {
          
          URMTaminoConnection parms = (URMTaminoConnection)mConnections.get(key);
          // User defined transaction pending? Do nothing
          if (0 == parms.getThreadLevel())
              return true;

          if (currentThreadLevel.intValue() <= parms.getThreadLevel()) {
              // We're back at the top level (or even above???).
              conn = parms.getConnection();
              locTran = parms.getTransaction();

              // Was it an update transaction in the database?
              if (parms.getMode() == UPDATE) {
                  try {
                    if (commit)
                        this.endTA(true, locTran);
                    else
                        this.endTA(false, locTran);
                  } catch (URMDBTransactionException e) {
                      throw new URMConnectionException(msLogger, "URMCOF0048", e);
                  }
              }
              
              try {
                  { /* Debug
                      if (conn.isClosed()) {
                          if (msLogger.isDebugEnabled())
                              MessageLogger.logMessage(msLogger,
                               "URMCOE0004", "(3) " + currentThreadLevel.intValue() +
                               " " + parms.getThreadLevel());
                      }
                  */ }
                  conn.close();
                  mConnections.remove(key);
              } catch (TConnectionCloseException ex) {
                  TException de = ex.getDeepestTException();
                  if (de == null)
                      de = ex;
                  throw new URMConnectionException(msLogger, "URMCOF0048", de);
              }
              return true;
          } else {
              // Calling release connection in a lower level
              // continues without doing anything
              return true;
          }
          
      }
      
      return true;
  }

  /*
   * Release the connection from the HashMap and check if Commit() or Rollback() needs to be called.
   */
  public synchronized boolean releaseTAConnection(boolean commit, Object princThis)
      throws URMConnectionException
  {
      TConnection conn = null;
      TXMLObjectAccessor accessor = null;
      TLocalTransaction locTran = null;
      
      Integer currentThreadLevel = new Integer(java.lang.Thread.currentThread().countStackFrames());
      String currentThreadName = java.lang.Thread.currentThread().getName();

      String key = new String(princThis.toString() + currentThreadName);

      if (mConnections.containsKey(key)) {
          
          URMTaminoConnection parms = (URMTaminoConnection)mConnections.get(key);
          if (0 != parms.getThreadLevel()) {
              // Error! We have an endTransaction without a beginTransaction
              throw new URMConnectionException(MessageLogger.getAndLogMessage(msLogger, "URMCOF0048"));
          } else {
              // We're back at the top level (or even above???).
              conn = parms.getConnection();
              locTran = parms.getTransaction();

              try {
                if (commit)
                    this.endTA(true, locTran);
                else
                    this.endTA(false, locTran);
              } catch (URMDBTransactionException e) {
                  throw new URMConnectionException(msLogger, "URMCOF0048", e);
              }

              // Now we need to merge the DeltaCaches into the real ones...
              Map actionDeltaCache = parms.getActionDeltaCache();
              Map aclDeltaCache = parms.getAclDeltaCache();
              URMAccessControlerFifoCache actionCache = URMAccessControlerSpiTamino.getActionCache();
              URMAccessControlerFifoCache aclCache = URMAccessControlerSpiTamino.getAclCache();
              URMListener aclListenerSet = URMListener.getAclListenerSet();

              if (actionCache != null) {
                  if (actionDeltaCache != null) {
                      actionCache.removeSet(actionDeltaCache.keySet());
                      actionDeltaCache = null;
                      aclListenerSet.resetEvent();
                  }
              }
              
              if (aclCache != null) {
                  if (aclDeltaCache != null) {
                      Set keys = aclDeltaCache.keySet();
                      aclCache.removeSet(keys);
                      Iterator iter = keys.iterator();
                      while (iter.hasNext()) {
                          aclListenerSet.removeEvent((String) iter.next());
                      }
                      aclDeltaCache = null;
                  }
              }
                    
              try {
                  { /* Debug
                      if (conn.isClosed()) {
                          if (msLogger.isDebugEnabled())
                              MessageLogger.logMessage(msLogger,
                               "URMCOE0004", "(4) " + currentThreadLevel.intValue() +
                               " " + parms.getThreadLevel());
                      }
                  */ }
                  conn.close();
                  mConnections.remove(key);
              } catch (TConnectionCloseException ex) {
                  TException de = ex.getDeepestTException();
                  if (de == null)
                      de = ex;
                  throw new URMConnectionException(msLogger, "URMCOF0048", de);
              }
              return true;
          }
      }
      return true;
  }

  /*
   * Get the singelton of the ConnectionPoolManager,
   * get or create a Connection,
   * start a transaction if LockMode says PROTECTED,
   * and create a TXMLObjectAccessor.
   *
   * @return Object array with
   *       retVals[0] = TConnection;
   *       retVals[1] = TXMLObjectAccessor;
   *       retVals[2] = TLocalTransaction;
   */
  private synchronized Object[] getPooledConnection(String poolName,
                                                    int mode,
                                                    Object princThis)
      throws URMConnectionException
  {
      Object retVals[] = new Object[3];
      TLocalTransaction localTransaction = null;
      TXMLObjectAccessor objectAccessor = null;
      TConnection conn = null;

      if (mConnectionPoolManager == null) {
          // Get the Pool Manager
          try {
              mConnectionPoolManager = TConnectionPoolManager.getInstance();
          }
          catch (TConnectionNotAvailableException e) {
              throw new URMConnectionException(e);
          }
          // Build a descriptor for my pool
          TConnectionPoolDescriptor descriptor = new TConnectionPoolDescriptor();
          descriptor.setDatabaseURI( mDatabaseUri );
          descriptor.setUser( mUserName );
          descriptor.setDomain(mUserDomain);
          descriptor.setPassword( mUserPwd );
          descriptor.setInitConnections( CONNPOOL_INIT_SIZE );
          descriptor.setMaxConnections( mConnPoolMax.intValue() );
          descriptor.setTimeOut( 0 );
          descriptor.setLockwaitMode(TLockwaitMode.YES);
          descriptor.setIsolationDegree( TIsolationDegree.SERIALIZABLE );
//          descriptor.setIsolationDegree( TIsolationDegree.COMMITTED_COMMAND );
          descriptor.setNonActivityTimeout( mConnPoolTimeout.intValue() );
          // create the pool
          try {
              mConnectionPoolManager.addConnectionPool( poolName,descriptor );
          }
          catch (TServerNotAvailableException e) {
              throw new URMConnectionException(e);
          }
      }
       
       try {

           // Establish a Tamino connection from the pool
           conn = mConnectionPoolManager.getConnection( poolName );
    
           if (mode == UPDATE || mode == START_TA) {
//               conn.setIsolationDegree( TIsolationDegree.SERIALIZABLE );
              
               // put it into local transaction mode
               localTransaction = startTA( conn );
           }
       } catch (TConnectionNotAvailableException ex) {
           TException de = ex.getDeepestTException();
           if (de == null)
               de = ex;
           if (msLogger.isDebugEnabled())
               MessageLogger.logMessage(msLogger, "URMTRE0013", de.getMessage());

           throw new URMConnectionException(msLogger, "F", de);
       } catch (TTransactionModeChangeException ex) {
           TException de = ex.getDeepestTException();
           if (de == null)
               de = ex;
           if (msLogger.isDebugEnabled())
               MessageLogger.logMessage(msLogger, "URMTRE0013", de.getMessage());

           throw new URMConnectionException(msLogger, "F", de);
       } catch (URMDBTransactionException e) {
           if (msLogger.isDebugEnabled())
               MessageLogger.logMessage(msLogger, "URMTRE0013", e.getMessage());

           throw new URMConnectionException(msLogger, "F", e);
       }
           
      objectAccessor = conn.newXMLObjectAccessor(
                          TAccessLocation.newInstance(mCollectionName),
                          TDOMObjectModel.getInstance() );
               
      if (objectAccessor == null)
          throw new URMConnectionException(MessageLogger.getAndLogMessage(
                  msLogger, "URMTRC0002", mDatabaseUri, mCollectionName));
                  
      if (mode == UPDATE || mode == START_TA) {
          objectAccessor.setLockMode(TLockMode.PROTECTED);
      } else {
          objectAccessor.setLockMode(TLockMode.SHARED);
      }

      retVals[0] = conn;
      retVals[1] = objectAccessor;
      retVals[2] = localTransaction;
      
      return retVals;
  }

  /*
   * Get the singelton of the ConnectionPoolManager,
   * get or create a Connection,
   * start a transaction if LockMode says PROTECTED,
   * and create a TXMLObjectAccessor.
   *
   * @return Object array with
   *       retVals[0] = TConnection;
   *       retVals[1] = TXMLObjectAccessor;
   *       retVals[2] = TLocalTransaction;
   */
  private synchronized Object[] getNonPooledConnection(String poolName,
                                                    int mode,
                                                    Object princThis)
      throws URMConnectionException
  {
      Object retVals[] = new Object[3];
      TLocalTransaction localTransaction = null;
      TXMLObjectAccessor objectAccessor = null;
      TConnection conn = null;
      
      try {
            TConnectionFactory fact = TConnectionFactory.getInstance();
            if (mUserDomain == null) {
                conn = fact.newConnection(mDatabaseUri, mUserName, mUserPwd);
            } else {
                conn = fact.newConnection(mDatabaseUri,  mUserDomain, mUserName, mUserPwd);
            }
      } catch (TServerNotAvailableException  ex) {
              TException de = ex.getDeepestTException();
              if (de == null)
                  de = ex;
              if (msLogger.isDebugEnabled())
                  MessageLogger.logMessage(msLogger, "URMTRE0012", de.getMessage());

              throw new URMConnectionException(MessageLogger.getAndLogMessage(msLogger, "F", de));
      }
      if (msLogger.isDebugEnabled())
          MessageLogger.logMessage(msLogger, "URMTRI0004", mDatabaseUri, conn,
                                  mUserName == null ? "" : mUserName, mUserDomain == null ? "" : mUserDomain);
                                
      /*
       * Set LockMode, LockwaitMode and IsolationLevel here!
       */
      try {
          if (mode == UPDATE || mode == START_TA) {
//              conn.setIsolationDegree(TIsolationDegree.SERIALIZABLE);
              conn.setLockwaitMode(TLockwaitMode.YES);
              conn.setLockMode(mode == UPDATE ? TLockMode.PROTECTED : TLockMode.SHARED);
              try {
                  localTransaction = startTA(conn);
              } catch (URMDBTransactionException e) {
                throw new URMConnectionException(msLogger, "F", e);
              }
          } else if (mode == COMMIT_TA || mode == ROLLBACK_TA) {
              try {
//                conn.setIsolationDegree(TIsolationDegree.SERIALIZABLE);
                  endTA((mode == COMMIT_TA ? true : false), localTransaction);
              } catch (URMDBTransactionException e) {
                  throw new URMConnectionException(msLogger, "F", e);
              }
          } else {
                // mode = QUERY
              conn.setLockwaitMode(TLockwaitMode.YES);
              conn.setLockMode(TLockMode.SHARED);
          }
      
          objectAccessor = conn.newXMLObjectAccessor(
                              TAccessLocation.newInstance(mCollectionName),
                              TDOMObjectModel.getInstance() );
               
          if (mode == UPDATE || mode == START_TA) {
              objectAccessor.setLockMode(TLockMode.PROTECTED);
          } else {
              objectAccessor.setLockMode(TLockMode.SHARED);
          }

      } catch (TTransactionModeChangeException ex) {
          TException de = ex.getDeepestTException();
          if (de == null)
              de = ex;
          if (msLogger.isDebugEnabled())
              MessageLogger.logMessage(msLogger, "URMTRE0013", de.getMessage());

          throw new URMConnectionException(msLogger, "F", de);
      }
      if (objectAccessor == null)
          throw new URMConnectionException(MessageLogger.getAndLogMessage(
                  msLogger, "URMTRC0002", mDatabaseUri, mCollectionName));
                  
      retVals[0] = conn;
      retVals[1] = objectAccessor;
      retVals[2] = localTransaction;
      
      return retVals;
  }

  /*
   * DB start Transaction
   */
  private synchronized TLocalTransaction startTA(TConnection conn)
    throws TTransactionModeChangeException, URMDBTransactionException {
        TLocalTransaction locTrans = null;

//        conn.setNonActivityTimeout(10000);  // TODO: work-around for bug in Tamino API 4.2.0.47
        locTrans = conn.useLocalTransactionMode();

      return locTrans;
  }

  /*
   * DB Commit Transaction
   */
   private synchronized void endTA(boolean mode, TLocalTransaction locTrans)
    throws URMDBTransactionException {
        try {
            if (mode) {
              locTrans.commit();
            } else {
              locTrans.rollback();
            }
        } catch (TTransactionException ex) {
            TException de = ex.getDeepestTException();
            if (de == null)
                de = ex;
            throw new URMDBTransactionException(msLogger, "F", de);
        }

    }
}

