/*
 * $Header: /home/cvspublic/jakarta-slide/src/share/org/apache/slide/store/AbstractStore.java,v 1.52 2005/03/29 08:02:23 luetzkendorf Exp $
 * $Revision: 1.52 $
 * $Date: 2005/03/29 08:02:23 $
 *
 * ====================================================================
 *
 * Copyright 1999-2002 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.store;

import java.util.Enumeration;
import java.util.Hashtable;

import javax.transaction.Transaction;
import javax.transaction.xa.XAException;
import javax.transaction.xa.Xid;

import org.apache.slide.authenticate.CredentialsToken;
import org.apache.slide.common.AbstractSimpleService;
import org.apache.slide.common.Namespace;
import org.apache.slide.common.NamespaceAccessToken;
import org.apache.slide.common.Scope;
import org.apache.slide.common.Service;
import org.apache.slide.common.ServiceAccessException;
import org.apache.slide.common.ServiceConnectionFailedException;
import org.apache.slide.common.ServiceDisconnectionFailedException;
import org.apache.slide.common.ServiceInitializationFailedException;
import org.apache.slide.common.ServiceParameterErrorException;
import org.apache.slide.common.ServiceParameterMissingException;
import org.apache.slide.common.ServiceResetFailedException;
import org.apache.slide.common.SlideToken;
import org.apache.slide.common.Uri;
import org.apache.slide.content.NodeRevisionContent;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.content.NodeRevisionDescriptors;
import org.apache.slide.content.NodeRevisionNumber;
import org.apache.slide.content.RevisionAlreadyExistException;
import org.apache.slide.content.RevisionDescriptorNotFoundException;
import org.apache.slide.content.RevisionNotFoundException;
import org.apache.slide.lock.LockTokenNotFoundException;
import org.apache.slide.lock.NodeLock;
import org.apache.slide.search.IndexException;
import org.apache.slide.security.NodePermission;
import org.apache.slide.structure.ObjectAlreadyExistsException;
import org.apache.slide.structure.ObjectNode;
import org.apache.slide.structure.ObjectNotFoundException;
import org.apache.slide.util.Messages;
import org.apache.slide.util.logger.Logger;

/**
 * Abstract implementation of a store. Handles all caching operations.
 *
 * @version $Revision: 1.52 $
 */
public abstract class AbstractStore extends AbstractSimpleService
    implements Store {
    
    
    // ----------------------------------------------------------- Constructors
    
    
    // ----------------------------------------------------- Instance Variables
    
    
    /**
     * Node store.
     */
    protected NodeStore nodeStore;
    
    
    /**
     * Security store.
     */
    protected SecurityStore securityStore;
    
    
    /**
     * Lock store.
     */
    protected LockStore lockStore;
    
    
    /**
     * Revision descriptors store.
     */
    protected RevisionDescriptorsStore revisionDescriptorsStore;
    
    
    /**
     * Revision descriptor store.
     */
    protected RevisionDescriptorStore revisionDescriptorStore;
    
    
    /**
     * Content store.
     */
    protected ContentStore contentStore;
    
    /**
     * DescriptorIndex store
     */
    protected IndexStore propertiesIndexer;
    
    /**
     * ContentIndex store
     */
    protected IndexStore contentIndexer;
    
    /**
     * Sequence store
     */
    protected SequenceStore sequenceStore = null;
    
    /**
     * Macro store
     */
    protected MacroStore macroStore = null;
    
    /**
     * Active resource manager list.
     */
    protected Service resourceManagers[] = new Service[0];
    
    
    
    // the name of this store as specified in domain.xml
    private String name;
    
    
    /**
     * Set the name of the store as specified in domain.xml.
     */
    public void setName(String name) {
        this.name = name;
    }
    
    
    
    /**
     * Return the name of the store as specified in domain.xml.
     */
    public String getName() {
        return this.name;
    }
    
    
    
    /**
     * Set the scope of the store as specified in domain.xml.
     */
    public void setScope(Scope scope) {
        super.setScope(scope);
        for (int i = 0; i < resourceManagers.length; i++) {
            resourceManagers[i].setScope(scope);
        }
    }
    
    
    
    // ---------------------------------------------------- ServiceImpl Methods
    
    
    /**
     * Namespace setter.
     */
    public void setNamespace(Namespace namespace) {
        
        super.setNamespace(namespace);
        
        for (int i = 0; i < resourceManagers.length; i++) {
            resourceManagers[i].setNamespace(namespace);
        }
        
    }
    
    
    protected Hashtable parameters = null;
    
    /**
     * Remeber the store parameters to initialise the default child stores on request
     *
     * @param parameters Hashtable containing the parameters' name
     * and associated value
     * @exception ServiceParameterErrorException Incorrect service parameter
     * @exception ServiceParameterMissingException Service parameter missing
     */
    public void setParameters(Hashtable parameters)
        throws ServiceParameterErrorException,
        ServiceParameterMissingException {
        this.parameters = new Hashtable(parameters);
    }
    
    public Object getParameter (Object key) {
        return parameters.get (key);
    }
    
    /**
     * Connects to descriptors store.
     *
     * @exception DataException
     */
    public void connect(CredentialsToken crdtoken)
        throws ServiceConnectionFailedException {
        
        for (int i = 0; i < resourceManagers.length; i++) {
            resourceManagers[i].connect(crdtoken);
        }
        
    }
    
    
    
    /**
     * Connects to descriptors store.
     *
     * @exception DataException
     */
    public void connect()
        throws ServiceConnectionFailedException {
        
        for (int i = 0; i < resourceManagers.length; i++) {
            resourceManagers[i].connect();
        }
        
    }
    
    
    /**
     * Disconnects from descriptors store.
     *
     * @exception ServiceDisconnectionFailedException
     */
    public void disconnect()
        throws ServiceDisconnectionFailedException {
        
        for (int i = 0; i < resourceManagers.length; i++) {
            resourceManagers[i].disconnect();
        }
        
    }
    
    
    /**
     * Initializes descriptors store.
     *
     * @exception ServiceInitializationFailedException Throws an exception
     * if the descriptors store has already been initialized before
     */
    public void initialize(NamespaceAccessToken token)
        throws ServiceInitializationFailedException {
        
        super.initialize(token);
        
        for (int i = 0; i < resourceManagers.length; i++) {
            resourceManagers[i].initialize(token);
        }
        
    }
    
    
    /**
     * Deletes descriptors store. Should remove stored data if possible.
     *
     * @exception ServiceResetFailedException Reset failed
     */
    public void reset()
        throws ServiceResetFailedException {
        
        for (int i = 0; i < resourceManagers.length; i++) {
            resourceManagers[i].reset();
        }
        
    }
    
    
    /**
     * This function tells whether or not the descriptors store is connected.
     *
     * @return boolean true if we are connected
     * @exception ServiceAccessException Service access error
     */
    public boolean isConnected()
        throws ServiceAccessException {
        
        for (int i = 0; i < resourceManagers.length; i++) {
            if (!resourceManagers[i].isConnected())
                return false;
        }
        return true;
        
    }
    
    
    // ----------------------------------------------------- XAResource Methods
    
    
    /**
     * Commit the global transaction specified by xid.
     *
     * @param xid A global transaction identifier
     * @param onePhase If true, the resource manager should use a one-phase
     * commit protocol to commit the work done on behalf of xid.
     * @exception XAException An error has occurred. Possible XAExceptions
     * are XA_HEURHAZ, XA_HEURCOM, XA_HEURRB, XA_HEURMIX, XAER_RMERR,
     * XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or XAER_PROTO. If the resource
     * manager did not commit the transaction and the paramether onePhase is
     * set to true, the resource manager may throw one of the XA_RB*
     * exceptions. Upon return, the resource manager has rolled back the
     * branch's work and has released all held resources.
     */
    public void commit(Xid xid, boolean onePhase)
        throws XAException {
        super.commit(xid, onePhase);
    }
    
    
    /**
     * Ends the work performed on behalf of a transaction branch.
     *
     * @param xid A global transaction identifier that is the same as what
     * was used previously in the start method.
     * @param flags One of TMSUCCESS, TMFAIL, or TMSUSPEND
     * @exception XAException An error has occurred. Possible XAException
     * values are XAER_RMERR, XAER_RMFAILED, XAER_NOTA, XAER_INVAL,
     * XAER_PROTO, or XA_RB*.
     */
    public void end(Xid xid, int flags)
        throws XAException {
        super.end(xid, flags);
    }
    
    
    /**
     * Tell the resource manager to forget about a heuristically completed
     * transaction branch.
     *
     * @param xid A global transaction identifier
     * @exception XAException An error has occurred. Possible exception values
     * are XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or XAER_PROTO.
     */
    public void forget(Xid xid)
        throws XAException {
        super.forget(xid);
    }
    
    
    /**
     * Ask the resource manager to prepare for a transaction commit of the
     * transaction specified in xid.
     *
     * @param xid A global transaction identifier
     * @return A value indicating the resource manager's vote on the outcome
     * of the transaction. The possible values are: XA_RDONLY or XA_OK. If
     * the resource manager wants to roll back the transaction, it should do
     * so by raising an appropriate XAException in the prepare method.
     * @exception XAException An error has occurred. Possible exception
     * values are: XA_RB*, XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL,
     * or XAER_PROTO.
     */
    public int prepare(Xid xid)
        throws XAException {
        return super.prepare(xid);
    }
    
    
    /**
     * Inform the resource manager to roll back work done on behalf of a
     * transaction branch.
     *
     * @param xid A global transaction identifier
     * @exception XAException An error has occurred
     */
    public void rollback(Xid xid)
        throws XAException {
        super.rollback(xid);
    }
    
    
    /**
     * Start work on behalf of a transaction branch specified in xid.
     *
     * @param xid A global transaction identifier to be associated with the
     * resource
     * @param flags One of TMNOFLAGS, TMJOIN, or TMRESUME
     * @exception XAException An error has occurred. Possible exceptions are
     * XA_RB*, XAER_RMERR, XAER_RMFAIL, XAER_DUPID, XAER_OUTSIDE, XAER_NOTA,
     * XAER_INVAL, or XAER_PROTO.
     */
    public void start(Xid xid, int flags)
        throws XAException {
        super.start(xid, flags);
    }
    
    
    // ---------------------------------------------------------- Store Methods
    
    
    /**
     * Set the node store associated with this store.
     */
    public void setNodeStore(NodeStore nodeStore) {
        if (nodeStore == null) {
            throw new IllegalArgumentException("Nodestore must not be null");
        }
        this.nodeStore = nodeStore;
        addResourceManager(this.nodeStore);
    }
    
    
    /**
     * Set the security store associated with this store.
     */
    public void setSecurityStore(SecurityStore securityStore) {
        if (securityStore == null) {
            throw new IllegalArgumentException("Securitystore must not be null");
        }
        this.securityStore = securityStore;
        addResourceManager(this.securityStore);
    }
    
    
    /**
     * Set the lock store associated with this store.
     */
    public void setLockStore(LockStore lockStore) {
        if (lockStore == null) {
            throw new IllegalArgumentException("Lockstore must not be null");
        }
        this.lockStore = lockStore;
        addResourceManager(this.lockStore);
    }
    
    
    /**
     * Set the revision descriptors store associated with this store.
     */
    public void setRevisionDescriptorsStore
        (RevisionDescriptorsStore revisionDescriptorsStore) {
        if (revisionDescriptorsStore == null) {
            throw new IllegalArgumentException("Revisiondescriptorsstore must not be null");
        }
        this.revisionDescriptorsStore = revisionDescriptorsStore;
        addResourceManager(this.revisionDescriptorsStore);
    }
    
    
    /**
     * Set the revision descriptor store associated with this store.
     */
    public void setRevisionDescriptorStore
        (RevisionDescriptorStore revisionDescriptorStore) {
        if (revisionDescriptorStore == null) {
            throw new IllegalArgumentException("Revisiondescriptorstore must not be null");
        }
        this.revisionDescriptorStore = revisionDescriptorStore;
        addResourceManager(this.revisionDescriptorStore);
    }
    
    
    /**
     * Set the content store associated with this store.
     */
    public void setContentStore(ContentStore contentStore) {
        if (contentStore == null) {
            
        }
        this.contentStore = contentStore;
        addResourceManager(this.contentStore);
    }
    
    
    /**
     * Set the Indexer associated to the properties store
     *
     * @param    propertiesIndexer   an IndexStore
     *
     */
    public void setPropertiesIndexer (IndexStore propertiesIndexer) {
        if (propertiesIndexer == null) {
            throw new IllegalArgumentException("PropertiesIndexer must not be null");
        }
        this.propertiesIndexer = propertiesIndexer;
        addResourceManager(this.propertiesIndexer);
        
    }
    
    
    /**
     * Set the Indexer associated to the conten store
     *
     * @param    contentIndexer      an IndexStore
     *
     */
    public void setContentIndexer (IndexStore contentIndexer) {
        if (contentIndexer == null) {
            throw new IllegalArgumentException("ContentIndexer must not be null");
        }
        this.contentIndexer = contentIndexer;
        addResourceManager(this.contentIndexer);
        
    }
    
    /**
     * Method getContentIndexer
     *
     * @return   an IndexStore
     *
     */
    public IndexStore getContentIndexer () {
        return contentIndexer;
    }
    
    /**
     * Method getPropertiesIndexer
     *
     * @return   an IndexStore
     *
     */
    public IndexStore getPropertiesIndexer () {
        return propertiesIndexer;
    }
    
    /**
     * Sets the sequence store associated with this store.
     */
    public void setSequenceStore(SequenceStore store) {
        sequenceStore = store;
    }
        
    /**
     * Sets the sequence store associated with this store.
     */
    public void setMacroStore(MacroStore store) {
        macroStore = store;
    }
        
    //
    // sequence methods 
    //

    /**
     * @see org.apache.slide.store.SequenceStore#isSequenceSupported()
     */
    public boolean isSequenceSupported() {
        return (sequenceStore != null && sequenceStore.isSequenceSupported());
    }

    /**
     * @see org.apache.slide.store.SequenceStore#sequenceExists(java.lang.String)
     */
    public boolean sequenceExists(String sequenceName) throws ServiceAccessException {
        if (!isSequenceSupported()) {
            throw new ServiceAccessException(this, "Sequences not supported");
        }
        return sequenceStore.sequenceExists(sequenceName);
    }

    /**
     * @see org.apache.slide.store.SequenceStore#createSequence(java.lang.String)
     */
    public boolean createSequence(String sequenceName) throws ServiceAccessException {
        if (!isSequenceSupported()) {
            throw new ServiceAccessException(this, "Sequences not supported");
        }
        return sequenceStore.createSequence(sequenceName);
    }

    /**
     * @see org.apache.slide.store.SequenceStore#nextSequenceValue(java.lang.String)
     */
    public long nextSequenceValue(String sequenceName) throws ServiceAccessException {
        if (!isSequenceSupported()) {
            throw new ServiceAccessException(this, "Sequences not supported");
        }
        return sequenceStore.nextSequenceValue(sequenceName);
    }

    //
    // macro methods 
    //

    /**
     * @see org.apache.slide.store.MacroStore#isMacroDeleteSupported()
     */
    public boolean isMacroDeleteSupported() {
        return (macroStore != null && macroStore.isMacroDeleteSupported());
    }

    /**
     * @see org.apache.slide.store.MacroStore#macroDelete(org.apache.slide.common.Uri)
     */
    public void macroDelete(Uri targetUri) throws ServiceAccessException, ObjectNotFoundException {
        if (!isMacroDeleteSupported()) {
            throw new ServiceAccessException(this, "Macro delete not supported");
        }
        enlist(macroStore);
        try {
            macroStore.macroDelete(targetUri);
        } finally {
            delist(macroStore);
        }
    }

    /**
     * @see org.apache.slide.store.MacroStore#isMacroCopySupported()
     */
    public boolean isMacroCopySupported() {
        return (macroStore != null && macroStore.isMacroCopySupported());
    }

    /**
     * @see org.apache.slide.store.MacroStore#macroCopy(Uri, Uri, boolean, boolean)
     */
    public void macroCopy(Uri sourceUri, Uri targetUri, boolean overwrite, boolean recursive)
            throws ServiceAccessException, ObjectNotFoundException, ObjectAlreadyExistsException {
        if (!isMacroCopySupported()) {
            throw new ServiceAccessException(this, "Macro copy not supported");
        }
        enlist(macroStore);
        try {
            macroStore.macroCopy(sourceUri, targetUri, overwrite, recursive);
        } finally {
            delist(macroStore);
        }
    }

    /**
     * @see org.apache.slide.store.MacroStore#isMacroMoveSupported()
     */
    public boolean isMacroMoveSupported() {
        return (macroStore != null && macroStore.isMacroMoveSupported());
    }

    /**
     * @see org.apache.slide.store.MacroStore#macroMove(Uri, Uri, boolean)
     */
    public void macroMove(Uri sourceUri, Uri targetUri, boolean overwrite)
            throws ServiceAccessException, ObjectNotFoundException, ObjectAlreadyExistsException {
        if (!isMacroMoveSupported()) {
            throw new ServiceAccessException(this, "Macro move not supported");
        }
        enlist(macroStore);
        try {
            macroStore.macroMove(sourceUri, targetUri, overwrite);
        } finally {
            delist(macroStore);
        }
    }
    
    /**
     * Retrive an object from the Descriptors Store.
     *
     * @param uri Uri of the object we want to retrieve
     * @exception ServiceAccessException Error accessing the Descriptors Store
     * @exception ObjectNotFoundException The object to retrieve was not found
     */
    public ObjectNode retrieveObject(Uri uri)
        throws ServiceAccessException, ObjectNotFoundException {
        ObjectNode objectNode = null;
        if (isForceStoreEnlistment(uri)) {
            enlist(nodeStore);
            try {
                objectNode = nodeStore.retrieveObject(uri);
            }  catch (ServiceAccessException e) {
                delist(nodeStore, false);
                throw e;
            } catch (ObjectNotFoundException e) {
                // Note : Failed reads aren't considered fatal (ie, the
                // transaction won't be always rolledback when committed)
                delist(nodeStore);
                throw e;
            } catch (Error e) {
                delist(nodeStore, false);
                throw e;
            } catch (Throwable t) {
                delist(nodeStore, false);
                // Wrap everything else in a ServiceAccessException
                throw new ServiceAccessException(nodeStore, t);
            }
            delist(nodeStore);
        } else {
            try {
                objectNode = nodeStore.retrieveObject(uri);
            } catch (ServiceAccessException e) {
                throw e;
            } catch (ObjectNotFoundException e) {
                throw e;
            } catch (Error e) {
                throw e;
            } catch (Throwable t) {
                // Wrap everything else in a ServiceAccessException
                throw new ServiceAccessException(nodeStore, t);
            }
        }
        objectNode.validate(uri.toString());
        return objectNode;
    }
    
    
    /**
     * Store an object in the Descriptors Store.
     *
     * @param object Object to update
     * @exception ServiceAccessException Error accessing the Descriptors Store
     * @exception ObjectNotFoundException The object to update was not found
     */
    public void storeObject(Uri uri, ObjectNode object)
        throws ServiceAccessException, ObjectNotFoundException {
        ObjectNode tempObject = object.cloneObject();
        tempObject.validate(uri.toString());
        enlist(nodeStore);
        try {
            nodeStore.storeObject(uri, tempObject);
        } catch (ServiceAccessException e) {
            delist(nodeStore, false);
            throw e;
        } catch (ObjectNotFoundException e) {
            delist(nodeStore);
            throw e;
        } catch (Error e) {
            delist(nodeStore, false);
            throw e;
        } catch (Throwable t) {
            delist(nodeStore, false);
            // Wrap everything else in a ServiceAccessException
            throw new ServiceAccessException(contentStore, t);
        }
        delist(nodeStore);
    }
    
    
    /**
     * Create a new object in the Descriptors Store.
     *
     * @param object SlideObject
     * @param uri Uri of the object we want to create
     * @exception ServiceAccessException Error accessing the Descriptors Store
     * @exception ObjectAlreadyExistsException An object already exists
     * at this Uri
     */
    public void createObject(Uri uri, ObjectNode object)
        throws ServiceAccessException, ObjectAlreadyExistsException {
        ObjectNode tempObject = object.cloneObject();
        tempObject.validate(uri.toString());
        enlist(nodeStore);
        try {
            nodeStore.createObject(uri, tempObject);
            if (useBinding()) {
                String uuri = tempObject.getUuri();
                if (uuri == null) {
                    throw new IllegalStateException();
                }
                object.setUuri(uuri);
            } else {
                object.setUuri(tempObject.getUri());
            }
        } catch (ServiceAccessException e) {
            delist(nodeStore, false);
            throw e;
        } catch (ObjectAlreadyExistsException e) {
            delist(nodeStore);
            throw e;
        } catch (Error e) {
            delist(nodeStore, false);
            throw e;
        } catch (Throwable t) {
            delist(nodeStore, false);
            // Wrap everything else in a ServiceAccessException
            throw new ServiceAccessException(nodeStore, t);
        }
        delist(nodeStore);
    }
    
    
    /**
     * Remove an object from the Descriptors Store.
     *
     * @param object Object to remove
     * @exception ServiceAccessException Error accessing the Descriptors Store
     * @exception ObjectNotFoundException The object to remove was not found
     */
    public void removeObject(Uri uri, ObjectNode object)
        throws ServiceAccessException, ObjectNotFoundException {
        object.validate(uri.toString());
        enlist(nodeStore);
        try {
            nodeStore.removeObject(uri, object);
        } catch (ServiceAccessException e) {
            delist(nodeStore, false);
            throw e;
        } catch (ObjectNotFoundException e) {
            delist(nodeStore);
            throw e;
        } catch (Error e) {
            delist(nodeStore, false);
            throw e;
        } catch (Throwable t) {
            delist(nodeStore, false);
            // Wrap everything else in a ServiceAccessException
            throw new ServiceAccessException(nodeStore, t);
        }
        delist(nodeStore);
    }
    
    
    /**
     * Store an object permissions in the Descriptors Store.
     *
     * @param permission Permission we want to create
     * @exception ServiceAccessException Error accessing the Descriptors Store
     */
    public void grantPermission(Uri uri, NodePermission permission)
        throws ServiceAccessException {
        NodePermission tempPermission = permission.cloneObject();
        tempPermission.validate(uri.toString());
        enlist(securityStore);
        try {
            securityStore.grantPermission(uri, tempPermission);
        } catch (ServiceAccessException e) {
            delist(securityStore, false);
            throw e;
        } catch (Error e) {
            delist(securityStore, false);
            throw e;
        } catch (Throwable t) {
            delist(securityStore, false);
            // Wrap everything else in a ServiceAccessException
            throw new ServiceAccessException(securityStore, t);
        }
        delist(securityStore);
    }
    
    
    /**
     * Store an object permissions in the Descriptors Store.
     *
     * @param permission Permission we want to create
     * @exception ServiceAccessException Error accessing the Descriptors Store
     */
    public void revokePermission(Uri uri, NodePermission permission)
        throws ServiceAccessException {
        permission.validate(uri.toString());
        enlist(securityStore);
        try {
            securityStore.revokePermission(uri, permission);
        } catch (ServiceAccessException e) {
            delist(securityStore, false);
            throw e;
        } catch (Error e) {
            delist(securityStore, false);
            throw e;
        } catch (Throwable t) {
            delist(securityStore, false);
            // Wrap everything else in a ServiceAccessException
            throw new ServiceAccessException(securityStore, t);
        }
        delist(securityStore);
    }
    
    
    /**
     * Revoke all the permissions on the object.
     *
     * @param uri Uri of the object
     * @exception ServiceAccessException Error accessing the Descriptors Store
     */
    public void revokePermissions(Uri uri)
        throws ServiceAccessException {
        enlist(securityStore);
        try {
            securityStore.revokePermissions(uri);
        } catch (ServiceAccessException e) {
            delist(securityStore, false);
            throw e;
        } catch (Error e) {
            delist(securityStore, false);
            throw e;
        } catch (Throwable t) {
            delist(securityStore, false);
            // Wrap everything else in a ServiceAccessException
            throw new ServiceAccessException(securityStore, t);
        }
        delist(securityStore);
    }
    
    
    /**
     * Enumerate locks on an object.
     * 
     * @param uri Uri of the subject
     * @return Enumeration List of {@link org.apache.slide.lock.NodeLock locks}
     * which have been put on the subject
     * @exception ServiceAccessException Service access error
     */
    public Enumeration enumeratePermissions(Uri uri)
        throws ServiceAccessException {
        // TODO : The vectors elements MUST be cloned
        if (isForceStoreEnlistment(uri)) {
            enlist(securityStore);
            Enumeration permissions = null;
            try {
                permissions = securityStore.enumeratePermissions(uri);
            } catch (ServiceAccessException e) {
                delist(securityStore, false);
                throw e;
            } catch (Error e) {
                delist(securityStore, false);
                throw e;
            } catch (Throwable t) {
                delist(securityStore, false);
                // Wrap everything else in a ServiceAccessException
                throw new ServiceAccessException
                    (securityStore, t);
            }
            delist(securityStore);
            return permissions;
        } else {
            try {
                return securityStore.enumeratePermissions(uri);
            } catch (ServiceAccessException e) {
                throw e;
            } catch (Error e) {
                throw e;
            } catch (Throwable t) {
                // Wrap everything else in a ServiceAccessException
                throw new ServiceAccessException
                    (securityStore, t);
            }
        }
    }
    
    
    /**
     * Puts a lock on a subject.
     *
     * @param lock Lock token
     * @exception ServiceAccessException Service access error
     */
    public void putLock(Uri uri, NodeLock lock)
        throws ServiceAccessException {
        lock.validate(uri.toString());
        enlist(lockStore);
        try {
            lockStore.putLock(uri, lock);
        } catch (ServiceAccessException e) {
            delist(lockStore, false);
            throw e;
        } catch (Error e) {
            delist(lockStore, false);
            throw e;
        } catch (Throwable t) {
            delist(lockStore, false);
            // Wrap everything else in a ServiceAccessException
            throw new ServiceAccessException(lockStore, t);
        }
        delist(lockStore);
    }
    
    
    /**
     * Renews a lock.
     *
     * @param lock Token to renew
     * @exception ServiceAccessException Service access error
     * @exception LockTokenNotFoundException Lock token was not found
     */
    public void renewLock(Uri uri, NodeLock lock)
        throws ServiceAccessException, LockTokenNotFoundException {
        lock.validate(uri.toString());
        enlist(lockStore);
        try {
            lockStore.renewLock(uri, lock);
        } catch (ServiceAccessException e) {
            delist(lockStore, false);
            throw e;
        } catch (LockTokenNotFoundException e) {
            delist(lockStore);
            throw e;
        } catch (Error e) {
            delist(lockStore, false);
            throw e;
        } catch (Throwable t) {
            delist(lockStore, false);
            // Wrap everything else in a ServiceAccessException
            throw new ServiceAccessException(lockStore, t);
        }
        delist(lockStore);
    }
    
    
    /**
     * Removes (cancels) a lock.
     *
     * @param lock Token to remove
     * @exception ServiceAccessException Service access error
     * @exception LockTokenNotFoundException Lock token was not found
     */
    public void removeLock(Uri uri, NodeLock lock)
        throws ServiceAccessException, LockTokenNotFoundException {
        lock.validate(uri.toString());
        enlist(lockStore);
        try {
            lockStore.removeLock(uri, lock);
        } catch (ServiceAccessException e) {
            delist(lockStore, false);
            throw e;
        } catch (LockTokenNotFoundException e) {
            delist(lockStore);
            throw e;
        } catch (Error e) {
            delist(lockStore, false);
            throw e;
        } catch (Throwable t) {
            delist(lockStore, false);
            // Wrap everything else in a ServiceAccessException
            throw new ServiceAccessException(lockStore, t);
        }
        delist(lockStore);
    }
    
    
    /**
     * Kills a lock.
     *
     * @param lock Token to remove
     * @exception ServiceAccessException Service access error
     * @exception LockTokenNotFoundException Lock token was not found
     */
    public void killLock(Uri uri, NodeLock lock)
        throws ServiceAccessException, LockTokenNotFoundException {
        lock.validate(uri.toString());
        enlist(lockStore);
        try {
            lockStore.killLock(uri, lock);
        } catch (ServiceAccessException e) {
            delist(lockStore, false);
            throw e;
        } catch (LockTokenNotFoundException e) {
            delist(lockStore);
            throw e;
        } catch (Error e) {
            delist(lockStore, false);
            throw e;
        } catch (Throwable t) {
            delist(lockStore, false);
            // Wrap everything else in a ServiceAccessException
            throw new ServiceAccessException(lockStore, t);
        }
        delist(lockStore);
    }
    
    
    /**
     * Enumerate locks on an object.
     * 
     * @param uri Uri of the subject
     * @return Enumeration List of {@link org.apache.slide.lock.NodeLock locks}
     * which have been put on the subject
     * @exception ServiceAccessException Service access error
     */
    public Enumeration enumerateLocks(Uri uri)
        throws ServiceAccessException {
        if (isForceStoreEnlistment(uri)) {
            enlist(lockStore);
            Enumeration locks = null;
            try {
                locks = lockStore.enumerateLocks(uri);
            } catch (ServiceAccessException e) {
                delist(lockStore, false);
                throw e;
            } catch (Error e) {
                delist(lockStore, false);
                throw e;
            } catch (Throwable t) {
                delist(lockStore, false);
                // Wrap everything else in a ServiceAccessException
                throw new ServiceAccessException(lockStore, t);
            }
            delist(lockStore);
            return locks;
        } else {
            try {
                return lockStore.enumerateLocks(uri);
            } catch (ServiceAccessException e) {
                throw e;
            } catch (Error e) {
                throw e;
            } catch (Throwable t) {
                // Wrap everything else in a ServiceAccessException
                throw new ServiceAccessException(lockStore, t);
            }
        }
    }
    
    
    /**
     * Retrieve a revision descriptors.
     *
     * @param uri Uri
     * @exception ServiceAccessException Service access error
     * @exception RevisionDescriptorNotFoundException Revision descriptor
     * was not found
     */
    public NodeRevisionDescriptors retrieveRevisionDescriptors(Uri uri)
        throws ServiceAccessException, RevisionDescriptorNotFoundException {
        NodeRevisionDescriptors revisionDescriptors = null;
        if (isForceStoreEnlistment(uri)) {
            enlist(revisionDescriptorsStore);
            try {
                revisionDescriptors =
                    revisionDescriptorsStore.retrieveRevisionDescriptors(uri);
            } catch (ServiceAccessException e) {
                delist(revisionDescriptorsStore, false);
                throw e;
            } catch (RevisionDescriptorNotFoundException e) {
                // Normal read failures aren't considered fatal
                delist(revisionDescriptorsStore);
                throw e;
            } catch (Error e) {
                delist(revisionDescriptorsStore, false);
                throw e;
            } catch (Throwable t) {
                delist(revisionDescriptorsStore, false);
                // Wrap everything else in a ServiceAccessException
                throw new ServiceAccessException
                    (revisionDescriptorsStore, t);
            }
            delist(revisionDescriptorsStore);
        } else {
            try {
                revisionDescriptors =
                    revisionDescriptorsStore.retrieveRevisionDescriptors(uri);
            } catch (ServiceAccessException e) {
                throw e;
            } catch (RevisionDescriptorNotFoundException e) {
                throw e;
            } catch (Error e) {
                throw e;
            } catch (Throwable t) {
                // Wrap everything else in a ServiceAccessException
                throw new ServiceAccessException
                    (revisionDescriptorsStore, t);
            }
        }
        revisionDescriptors.validate(uri.toString());
        return revisionDescriptors;
    }
    
    
    /**
     * Create new revision descriptors.
     *
     * @param uri Uri
     * @param revisionDescriptors Node revision descriptors
     * @exception ServiceAccessException Service access error
     */
    public void createRevisionDescriptors
        (Uri uri, NodeRevisionDescriptors revisionDescriptors)
        throws ServiceAccessException {
        revisionDescriptors.validate(uri.toString());
        enlist(revisionDescriptorsStore);
        try {
            revisionDescriptorsStore.createRevisionDescriptors
                (uri, revisionDescriptors);
        } catch (ServiceAccessException e) {
            delist(revisionDescriptorsStore, false);
            throw e;
        } catch (Error e) {
            delist(revisionDescriptorsStore, false);
            throw e;
        } catch (Throwable t) {
            delist(revisionDescriptorsStore, false);
            // Wrap everything else in a ServiceAccessException
            throw new ServiceAccessException
                (revisionDescriptorsStore, t);
        }
        delist(revisionDescriptorsStore);
    }
    
    
    /**
     * Update revision descriptors.
     *
     * @param uri Uri
     * @param revisionDescriptors Node revision descriptors
     * @exception ServiceAccessException Service access error
     * @exception RevisionDescriptorNotFoundException Revision descriptor
     * was not found
     */
    public void storeRevisionDescriptors
        (Uri uri, NodeRevisionDescriptors revisionDescriptors)
        throws ServiceAccessException, RevisionDescriptorNotFoundException {
        revisionDescriptors.validate(uri.toString());
        enlist(revisionDescriptorsStore);
        try {
            revisionDescriptorsStore.storeRevisionDescriptors
                (uri, revisionDescriptors);
        } catch (ServiceAccessException e) {
            delist(revisionDescriptorsStore, false);
            throw e;
        } catch (RevisionDescriptorNotFoundException e) {
            delist(revisionDescriptorsStore);
            throw e;
        } catch (Error e) {
            delist(revisionDescriptorsStore, false);
            throw e;
         } catch (Throwable t) {
            delist(revisionDescriptorsStore, false);
            // Wrap everything else in a ServiceAccessException
            throw new ServiceAccessException
                (revisionDescriptorsStore, t);
        }
        delist(revisionDescriptorsStore);
    }
    
    
    /**
     * Remove revision descriptors.
     *
     * @param uri Uri
     * @exception ServiceAccessException Service access error
     */
    public void removeRevisionDescriptors(Uri uri)
        throws ServiceAccessException {
        enlist(revisionDescriptorsStore);
        try {
            revisionDescriptorsStore.removeRevisionDescriptors(uri);
        } catch (ServiceAccessException e) {
            delist(revisionDescriptorsStore, false);
            throw e;
        } catch (Error e) {
            delist(revisionDescriptorsStore, false);
            throw e;
        } catch (Throwable t) {
            delist(revisionDescriptorsStore, false);
            // Wrap everything else in a ServiceAccessException
            throw new ServiceAccessException
                (revisionDescriptorsStore, t);
        }
        delist(revisionDescriptorsStore);
    }
    
    
    /**
     * Retrieve revision descriptor.
     *
     * @param uri uri
     * @param revisionNumber Node revision number
     */
    public NodeRevisionDescriptor retrieveRevisionDescriptor
        (Uri uri, NodeRevisionNumber revisionNumber)
        throws ServiceAccessException, RevisionDescriptorNotFoundException {
        NodeRevisionDescriptor revisionDescriptor = null;
        if (isForceStoreEnlistment(uri)) {
            enlist(revisionDescriptorStore);
            try {
                revisionDescriptor =
                    revisionDescriptorStore.retrieveRevisionDescriptor
                    (uri, revisionNumber);
            } catch (ServiceAccessException e) {
                delist(revisionDescriptorStore, false);
                throw e;
            } catch (RevisionDescriptorNotFoundException e) {
                // Normal read failures aren't considered fatal
                delist(revisionDescriptorStore);
                throw e;
            } catch (Error e) {
                delist(revisionDescriptorsStore, false);
                throw e;
            } catch (Throwable t) {
                delist(revisionDescriptorStore, false);
                // Wrap everything else in a ServiceAccessException
                throw new ServiceAccessException
                    (revisionDescriptorStore, t);
            }
            delist(revisionDescriptorStore);
        } else {
            try {
                revisionDescriptor =
                    revisionDescriptorStore.retrieveRevisionDescriptor
                    (uri, revisionNumber);
            } catch (ServiceAccessException e) {
                throw e;
            } catch (RevisionDescriptorNotFoundException e) {
                throw e;
            } catch (Error e) {
                throw e;
            } catch (Throwable t) {
                // Wrap everything else in a ServiceAccessException
                throw new ServiceAccessException
                    (revisionDescriptorStore, t);
            }
        }
        revisionDescriptor.validate();
        return revisionDescriptor;
    }
    
    
    /**
     * Create new revision descriptor.
     *
     * @param uri Uri
     * @param revisionDescriptor Node revision descriptor
     * @exception ServiceAccessException Service access error
     */
    public void createRevisionDescriptor
        (Uri uri, NodeRevisionDescriptor revisionDescriptor)
        throws ServiceAccessException {
        revisionDescriptor.validate();
        
        enlist(propertiesIndexer);
        enlist(revisionDescriptorStore);
        try {
            propertiesIndexer.createIndex (uri, revisionDescriptor, null);
            revisionDescriptorStore.createRevisionDescriptor
                (uri, revisionDescriptor);
            revisionDescriptor.resetRemovedProperties();
            revisionDescriptor.resetUpdatedProperties();
            
        } catch (ServiceAccessException e) {
            delist (propertiesIndexer, false);
            delist(revisionDescriptorStore, false);
            throw e;
        } catch (Error e) {
            delist (propertiesIndexer, false);
            delist(revisionDescriptorStore, false);
            throw e;
        } catch (Throwable t) {
            delist (propertiesIndexer, false);
            delist(revisionDescriptorStore, false);
            // Wrap everything else in a ServiceAccessException
            throw new ServiceAccessException
                (revisionDescriptorStore, t);
        }
        delist (propertiesIndexer);
        delist (revisionDescriptorStore);
    }
    
    
    /**
     * Update revision descriptor.
     *
     * @param uri Uri
     * @param revisionDescriptor Node revision descriptor
     * @exception ServiceAccessException Service access error
     * @exception RevisionDescriptorNotFoundException Revision descriptor
     * was not found
     */
    public void storeRevisionDescriptor
        (Uri uri, NodeRevisionDescriptor revisionDescriptor)
        throws ServiceAccessException, RevisionDescriptorNotFoundException {
        revisionDescriptor.validate();
        
        enlist (propertiesIndexer);
        enlist(revisionDescriptorStore);
        try {
            propertiesIndexer.updateIndex (uri, revisionDescriptor, null);
            revisionDescriptorStore.storeRevisionDescriptor
                (uri, revisionDescriptor);
            revisionDescriptor.resetRemovedProperties();
            revisionDescriptor.resetUpdatedProperties();
        } catch (ServiceAccessException e) {
            delist (propertiesIndexer, false);
            delist(revisionDescriptorStore, false);
            throw e;
        } catch (RevisionDescriptorNotFoundException e) {
            delist (propertiesIndexer);
            delist(revisionDescriptorStore);
            throw e;
        } catch (Error e) {
            delist (propertiesIndexer, false);
            delist(revisionDescriptorStore, false);
            throw e;
        } catch (Throwable t) {
            delist (propertiesIndexer, false);
            delist(revisionDescriptorStore, false);
            // Wrap everything else in a ServiceAccessException
            throw new ServiceAccessException
                (revisionDescriptorStore, t);
        }
        delist (propertiesIndexer);
        delist(revisionDescriptorStore);
    }
    
    
    /**
     * Remove revision descriptor.
     *
     * @param uri Uri
     * @param revisionNumber Revision number
     * @exception ServiceAccessException Service access error
     */
    public void removeRevisionDescriptor (Uri uri, NodeRevisionNumber number)
        throws ServiceAccessException
    {
        number.validate();
        
        enlist(propertiesIndexer);
        enlist(revisionDescriptorStore);
        
        try {
            propertiesIndexer.dropIndex (uri, number);
            revisionDescriptorStore.removeRevisionDescriptor(uri, number);
        } catch (ServiceAccessException e) {
            delist(propertiesIndexer, false);
            delist(revisionDescriptorStore, false);
            throw e;
        } catch (Error e) {
            delist (propertiesIndexer, false);
            delist(revisionDescriptorStore, false);
            throw e;
        } catch (Throwable t) {
            delist(propertiesIndexer, false);
            delist(revisionDescriptorStore, false);
            
            // Wrap everything else in a ServiceAccessException
            throw new ServiceAccessException
                (revisionDescriptorStore, t);
        }
        delist(propertiesIndexer);
        delist(revisionDescriptorStore);
    }
    
    
    /**
     * Retrive revision content.
     *
     * @param uri Uri
     * @param revisionDescriptor Node revision descriptor
     */
    public NodeRevisionContent retrieveRevisionContent
        (Uri uri, NodeRevisionDescriptor revisionDescriptor)
        throws ServiceAccessException, RevisionNotFoundException {
        NodeRevisionContent revisionContent = null;
        if (isForceStoreEnlistment(uri)) {
            enlist(contentStore);
            try {
                revisionContent =
                    contentStore.retrieveRevisionContent
                    (uri, revisionDescriptor);
                
            } catch (ServiceAccessException e) {
                delist(contentStore, false);
                throw e;
            } catch (RevisionNotFoundException e) {
                // Normal read failures aren't considered fatal
                delist(contentStore);
                throw e;
            } catch (Error e) {
                delist(contentStore, false);
                throw e;
            } catch (Throwable t) {
                delist(contentStore, false);
                // Wrap everything else in a ServiceAccessException
                throw new ServiceAccessException
                    (contentStore, t);
            }
            delist(contentStore);
        } else {
            try {
                revisionContent =
                    contentStore.retrieveRevisionContent
                    (uri, revisionDescriptor);
            } catch (ServiceAccessException e) {
                throw e;
            } catch (RevisionNotFoundException e) {
                throw e;
            } catch (Error e) {
                throw e;
            } catch (Throwable t) {
                // Wrap everything else in a ServiceAccessException
                throw new ServiceAccessException
                    (contentStore, t);
            }
        }
        revisionContent.validate();
        return revisionContent;
    }
    
    
    /**
     * Create a new revision
     *
     * @param uri Uri
     * @param revisionDescriptor Node revision descriptor
     * @param revisionContent Node revision content
     */
    public void createRevisionContent
        (Uri uri, NodeRevisionDescriptor revisionDescriptor,
         NodeRevisionContent revisionContent)
        throws ServiceAccessException, RevisionAlreadyExistException
    {
        revisionDescriptor.validate();
        revisionContent.validate();
        
        enlist(contentIndexer);
        enlist(contentStore);
        try {
            contentIndexer.createIndex (uri, revisionDescriptor, revisionContent);
            contentStore.createRevisionContent(uri, revisionDescriptor,
                                               revisionContent);
            
        } catch (ServiceAccessException e) {
            delist(contentIndexer, false);
            delist(contentStore, false);
            throw e;
        } catch (RevisionAlreadyExistException e) {
            delist(contentIndexer);
            delist(contentStore);
            throw e;
        } catch (Error e) {
            delist(contentIndexer, false);
            delist(contentStore, false);
            throw e;
        } catch (Throwable t) {
            delist(contentIndexer, false);
            delist(contentStore, false);
            // Wrap everything else in a ServiceAccessException
            throw new ServiceAccessException(contentStore, t);
        }
        delist(contentIndexer);
        delist(contentStore);
    }
    
    
    /**
     * Modify the latest revision of an object.
     *
     * @param uri Uri
     * @param revisionDescriptor Node revision descriptor
     * @param revisionContent Node revision content
     */
    public void storeRevisionContent
        (Uri uri, NodeRevisionDescriptor revisionDescriptor,
         NodeRevisionContent revisionContent)
        throws ServiceAccessException, RevisionNotFoundException {
        revisionDescriptor.validate();
        revisionContent.validate();
        
        enlist (contentIndexer);
        enlist(contentStore);
        try {
            contentIndexer.updateIndex (uri, revisionDescriptor, revisionContent);
            contentStore.storeRevisionContent(uri, revisionDescriptor,
                                              revisionContent);
        } catch (ServiceAccessException e) {
            delist (contentIndexer, false);
            delist (contentStore, false);
            throw e;
        } catch (RevisionNotFoundException e) {
            delist(contentIndexer);
            delist(contentStore);
            throw e;
        } catch (Error e) {
            delist(contentIndexer, false);
            delist(contentStore, false);
            throw e;
        } catch (Throwable t) {
            delist(contentIndexer, false);
            delist(contentStore, false);
            
            // Wrap everything else in a ServiceAccessException
            throw new ServiceAccessException (contentStore, t);
        }
        
        delist(contentIndexer);
        delist(contentStore);
    }
    
    
    /**
     * Remove revision.
     *
     * @param uri Uri
     * @param revisionDescriptor Node revision descriptor
     */
    public void removeRevisionContent
        (Uri uri, NodeRevisionDescriptor revisionDescriptor)
        throws ServiceAccessException {
        revisionDescriptor.validate();
        
        enlist(contentIndexer);
        enlist(contentStore);
        
        try {
            contentIndexer.dropIndex (uri, revisionDescriptor.getRevisionNumber());
            contentStore.removeRevisionContent(uri, revisionDescriptor);
            
        } catch (ServiceAccessException e) {
            delist(contentIndexer, false);
            delist(contentStore, false);
            throw e;
        } catch (Error e) {
            delist(contentIndexer, false);
            delist(contentStore, false);
            throw e;
        } catch (Throwable t) {
            delist(contentIndexer, false);
            delist(contentStore, false);
            
            // Wrap everything else in a ServiceAccessException
            throw new ServiceAccessException(contentStore, t);
        }
        
        delist(contentIndexer);
        delist(contentStore);
    }
    
    
    public void createPropertiesIndex(Uri uri, NodeRevisionDescriptor revisionDescriptor,
            NodeRevisionContent revisionContent) throws ServiceAccessException, IndexException
	{
    	enlist(propertiesIndexer);
    	
    	try {
    		propertiesIndexer.createIndex(uri, revisionDescriptor, revisionContent);
    	} finally {
    		delist(propertiesIndexer);
    	}
	}
    
    public void createContentIndex(Uri uri, NodeRevisionDescriptor revisionDescriptor,
            NodeRevisionContent revisionContent) throws ServiceAccessException, IndexException
	{
    	enlist(contentIndexer);
    	
    	try {
    		contentIndexer.createIndex(uri, revisionDescriptor, revisionContent);
    	} finally {
    		delist(contentIndexer);
    	}
	}
    
    // ------------------------------------------------------ Protected Methods
    
    
    /**
     * Return true if the store should also be enlisted for a read operation.
     */
    protected boolean isForceStoreEnlistment(Uri uri) {
        
        SlideToken token = uri.getToken();
        
        if (token == null)
            return false;
        
        return token.isForceStoreEnlistment();
        
    }
    
    
    /**
     * Add a new resource manager.
     *
     * @param service New resource manager
     */
    protected void addResourceManager(Service service) {
        if (service == null)
            return;
        // First check if the new store isn't already in the array
        for (int i = 0; i < resourceManagers.length; i++) {
            try {
                if (resourceManagers[i].isSameRM(service))
                    return;
            } catch (XAException e) {
            }
        }
        Service results[] = new Service[resourceManagers.length + 1];
        System.arraycopy(resourceManagers, 0, results, 0,
                         resourceManagers.length);
        results[resourceManagers.length] = service;
        resourceManagers = results;
    }
    
    
    /**
     * Enlist the resource manager in the current transaction.
     */
    protected void enlist()
        throws ServiceAccessException {
        enlist(this);
    }
    
    
    /**
     * Enlist the resource manager in the current transaction.
     */
    protected void enlist(Service service)
        throws ServiceAccessException {
        // Note: No exception is thrown
        boolean enlisted = false;
        // FIXME : Enhance retries.
        int nbTry = 0;
        while ((!enlisted) && (nbTry++ < 20)) {
            Transaction transaction = null;
            try {
                transaction =
                    namespace.getTransactionManager().getTransaction();
            } catch (Exception e) {
            }
            if (transaction == null) {
                getLogger().log("WARNING: No active transaction", Logger.WARNING);
                return;
            }
            try {
                enlisted = transaction.enlistResource(service);
            } catch (Exception e) {
                // Something went wrong.
                setRollbackOnly();
                throw new ServiceAccessException(this, e);
            }
            if (!enlisted) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    // Then go on.
                }
            }
        }
        if (!enlisted) {
            String exMessage = Messages.format
                (AbstractStore.class.getName() + ".enlistFail", service);
            setRollbackOnly();
            throw new ServiceAccessException(this, exMessage);
        }
        if (getLogger().isEnabled(LOG_CHANNEL, Logger.DEBUG)) {
            String logMessage = Messages.format
                (AbstractStore.class.getName() + ".enlist", service);
            getLogger().log(logMessage, LOG_CHANNEL, Logger.DEBUG);
        }
    }
    
    
    /**
     * Delist (suspend) the resource manager in the current transaction.
     */
    protected void delist(boolean success)
        throws ServiceAccessException {
        delist(this, success);
    }
    
    
    /**
     * Delist (suspend) the resource manager in the current transaction.
     */
    protected void delist(Service service)
        throws ServiceAccessException {
        delist(service, true);
    }
    
    
    /**
     * Delist (suspend) the resource manager in the current transaction.
     */
    protected void delist(Service service, boolean success)
        throws ServiceAccessException {
        try {
            Transaction transaction =
                namespace.getTransactionManager().getTransaction();
            if (transaction == null)
                return;
            if (success) {
                transaction.delistResource(service, TMSUSPEND);
                if (getLogger().isEnabled(LOG_CHANNEL, Logger.DEBUG)) {
                    String logMessage = Messages.format
                        (AbstractStore.class.getName() + ".delist", service);
                    getLogger().log(logMessage, LOG_CHANNEL, Logger.DEBUG);
                }
            } else {
                transaction.delistResource(service, TMFAIL);
                String logMessage = Messages.format
                    (AbstractStore.class.getName() + ".delistFail", service);
                getLogger().log(logMessage, LOG_CHANNEL, Logger.DEBUG);
            }
        } catch (Exception e) {
            // Something went wrong.
            throw new ServiceAccessException(this, e);
        }
    }
    
    
    /**
     * Mark transaction as rollback in case of enlistment failure.
     */
    protected void setRollbackOnly() {
        try {
            Transaction transaction =
                namespace.getTransactionManager().getTransaction();
            if (transaction == null)
                return;
            transaction.setRollbackOnly();
        } catch (Exception e) {
        }
    }
    
    /**
     * Always returns false. Default implementation for this method
     * Stores that support binding should override this method.
     */
    public boolean useBinding() {
        return false;
    }
    
}
