org.apache.slide.store.impl.rdbms.AbstractRDBMSStore.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.slide.store.impl.rdbms.AbstractRDBMSStore.java

Source

/*
 * $Header: /var/chroot/cvs/cvs/factsheetDesigner/extern/jakarta-slide-server-src-2.1-iPlus Edit/src/stores/org/apache/slide/store/impl/rdbms/AbstractRDBMSStore.java,v 1.3 2007-12-06 16:41:57 peter-cvs Exp $
 * $Revision: 1.3 $
 * $Date: 2007-12-06 16:41:57 $
 *
 * ====================================================================
 *
 * Copyright 1999-2003 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.impl.rdbms;

import java.lang.reflect.Constructor;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Hashtable;

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

import org.apache.commons.transaction.util.xa.AbstractTransactionalResource;
import org.apache.commons.transaction.util.xa.TransactionalResource;
import org.apache.slide.common.AbstractXAServiceBase;
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.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.basic.BasicExpressionFactory;
import org.apache.slide.search.basic.IBasicExpressionFactory;
import org.apache.slide.search.basic.IBasicExpressionFactoryProvider;
import org.apache.slide.security.NodePermission;
import org.apache.slide.store.ContentStore;
import org.apache.slide.store.LockStore;
import org.apache.slide.store.NodeStore;
import org.apache.slide.store.RevisionDescriptorStore;
import org.apache.slide.store.RevisionDescriptorsStore;
import org.apache.slide.store.SecurityStore;
import org.apache.slide.store.SequenceStore;
import org.apache.slide.store.impl.rdbms.expression.RDBMSExpressionFactory;
import org.apache.slide.structure.ObjectAlreadyExistsException;
import org.apache.slide.structure.ObjectNode;
import org.apache.slide.structure.ObjectNotFoundException;
import org.apache.slide.util.logger.Logger;

/**
 * J2EE store implementation - implements the shared parts of
 * the two (content, descriptors) J2EE stores for the new Indexed DB Schema.
 *
 * @version $Revision: 1.3 $
 */
public abstract class AbstractRDBMSStore extends AbstractXAServiceBase
        implements LockStore, NodeStore, RevisionDescriptorsStore, RevisionDescriptorStore, SecurityStore,
        ContentStore, SequenceStore, IBasicExpressionFactoryProvider {

    protected static final String LOG_CHANNEL = AbstractRDBMSStore.class.getName();

    protected RDBMSAdapter adapter;

    protected boolean alreadyInitialized = false;

    protected int isSequenceSupported = 0;

    protected boolean useRdbmsExpressionFactory;

    /**
     * Indicates whether the transaction manager will commit / rollback
     * the transaction or the store is in charge of it. Slide's internal
     * TM does not commit / rollback, but TMs more aligned to the spec (e.g. JBoss' TM) do.
     */
    protected boolean tmCommits = false;

    /**
     * Initializes the data source with a set of parameters.
     *
     * @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 {

        // Adapter class
        String value = (String) parameters.get("adapter");
        if (value == null) {
            adapter = new StandardRDBMSAdapter(this, getLogger());
        } else {
            try {
                Class adapterClass = Class.forName(value);
                Constructor ctor = adapterClass.getConstructor(new Class[] { Service.class, Logger.class });
                adapter = (RDBMSAdapter) ctor.newInstance(new Object[] { this, getLogger() });
            } catch (Exception e) {
                // reflection exception
                getLogger().log("Error instantiating Adapter '" + value + "' (" + e.getMessage() + ")", LOG_CHANNEL,
                        Logger.ERROR);
            }
        }

        if (adapter != null) {
            adapter.setParameters(parameters);
        }

        // Use RDBMSExpressionFactory?
        value = (String) parameters.get("use-rdbms-expression-factory");
        this.useRdbmsExpressionFactory = Boolean.valueOf(value).booleanValue();

        // XXX need to initialize it here, as some security requests access the store before initialization
        try {
            initialize(null);
        } catch (ServiceInitializationFailedException e) {
            // XXX this is not very satisfactory...
            throw new ServiceParameterErrorException(this, e.getMessage());
        }
    }

    /**
     * Establishes the global connection to the data source.
     *
     * @exception ServiceConnectionFailedException if the connection failed
     */
    public void connect() throws ServiceConnectionFailedException {
    }

    /**
     * Returns connection status.
     */
    public boolean isConnected() {
        return true;
    }

    /**
     * Closes the global connection to the data source.
     *
     * @exception ServiceDisconnectionFailedException if closing the connection
     *            failed
     */
    public void disconnect() throws ServiceDisconnectionFailedException {
    }

    /**
     * Does nothing.
     *
     * @exception ServiceResetFailedException Reset failed
     */
    public void reset() throws ServiceResetFailedException {

    }

    // ----------------------------------------------------- XAResource Methods

    /**
     * Get the transaction timeout value for this XAResource.
     * Just returns 0, we don't have a way of doing transaction timeouts
     * with the connection.
     */
    public int getTransactionTimeout() throws XAException {
        return 0;
    }

    /**
     * Set transaction timeout, not implemented (returns false).
     */
    public boolean setTransactionTimeout(int timeout) throws XAException {
        return false;
    }

    public Xid[] recover(int flag) throws XAException {

        getLogger().log("recover() for thread: " + Thread.currentThread(), LOG_CHANNEL, Logger.DEBUG);
        TransactionalResource id = getCurrentlyActiveTransactionalResource();

        if (id != null && id.getStatus() == STATUS_PREPARED) {
            Xid[] xids = new Xid[1];
            xids[0] = id.getXid();
            return xids;
        } else
            return new Xid[0];
    }

    public boolean isSameRM(XAResource xares) throws XAException {
        return (xares == this);
    }

    // ----------------------------------------------- IBasicExpressionFactoryProvider Implementation

    public IBasicExpressionFactory getBasicExpressionFactory() {
        if (this.useRdbmsExpressionFactory) {
            return new RDBMSExpressionFactory(this);
        } else {
            return new BasicExpressionFactory();
        }
    }

    // ----------------------------------------------- SequenceStore Implementation

    /**
     * @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");
        }
        Connection connection = null;
        try {
            connection = getNewConnection();
            return ((SequenceAdapter) adapter).createSequence(connection, sequenceName);
        } catch (SQLException e) {
            throw new ServiceAccessException(this, e);
        } finally {
            if (connection != null) {
                try {
                    if (!tmCommits) {
                        connection.commit();
                    }
                } catch (SQLException e) {
                    throw new ServiceAccessException(this, e);
                } finally {
                    try {
                        connection.close();
                    } catch (SQLException e) {
                        getLogger().log(e, LOG_CHANNEL, Logger.WARNING);
                    }
                }
            }
        }
    }

    /**
     * @see org.apache.slide.store.SequenceStore#isSequenceSupported()
     */
    public boolean isSequenceSupported() {
        // cache it for better performance as support of an adapter will
        // not change will running, it is either yes or no all the time
        if (isSequenceSupported > 0)
            return true;
        if (isSequenceSupported < 0)
            return false;
        Connection connection = null;
        try {
            connection = getNewConnection();
            if (adapter instanceof SequenceAdapter && ((SequenceAdapter) adapter).isSequenceSupported(connection)) {
                isSequenceSupported = 1;
                return true;
            } else {
                isSequenceSupported = -1;
                return false;
            }
        } catch (SQLException e) {
            getLogger().log("Error while trying to check for sequence support. Assuming false", e, LOG_CHANNEL,
                    Logger.ERROR);
            isSequenceSupported = -1;
            return false;
        } finally {
            if (connection != null) {
                try {
                    if (!tmCommits) {
                        connection.commit();
                    }
                } catch (SQLException e) {
                    getLogger().log(e, LOG_CHANNEL, Logger.WARNING);
                } finally {
                    try {
                        connection.close();
                    } catch (SQLException e) {
                        getLogger().log(e, LOG_CHANNEL, Logger.WARNING);
                    }
                }
            }
        }
    }

    /**
     * @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");
        }
        Connection connection = null;
        try {
            connection = getNewConnection();
            //todo: remove
            //System.out.println("Getting sequence from the database: "+sequenceName);
            long value = ((SequenceAdapter) adapter).nextSequenceValue(connection, sequenceName);
            //System.out.println("Getting sequence from the database: "+sequenceName +"="+value);
            return value;
        } catch (SQLException e) {
            throw new ServiceAccessException(this, e);
        } finally {
            if (connection != null) {
                try {
                    if (!tmCommits) {
                        connection.commit();
                    }
                } catch (SQLException e) {
                    throw new ServiceAccessException(this, e);
                } finally {
                    try {
                        connection.close();
                    } catch (SQLException e) {
                        getLogger().log(e, LOG_CHANNEL, Logger.WARNING);
                    }
                }
            }
        }
    }

    /**
     * @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");
        }
        Connection connection = null;
        try {
            connection = getNewConnection();
            return ((SequenceAdapter) adapter).sequenceExists(connection, sequenceName);
        } catch (SQLException e) {
            throw new ServiceAccessException(this, e);
        } finally {
            if (connection != null) {
                try {
                    if (!tmCommits) {
                        connection.commit();
                    }
                } catch (SQLException e) {
                    throw new ServiceAccessException(this, e);
                } finally {
                    try {
                        connection.close();
                    } catch (SQLException e) {
                        getLogger().log(e, LOG_CHANNEL, Logger.WARNING);
                    }
                }
            }
        }
    }

    // ----------------------------------------------- NodeStore Implementation

    /**
     * Retrieve an object.
     *
     * @param uri Uri of the object we want to retrieve
     * @exception ServiceAccessException Error accessing the Service
     * @exception ObjectNotFoundException The object to retrieve was not found
     */
    public ObjectNode retrieveObject(Uri uri) throws ServiceAccessException, ObjectNotFoundException {

        if (getCurrentlyActiveTransactionalResource() == null) {
            Connection connection = null;
            try {
                connection = getNewConnection();
                return adapter.retrieveObject(connection, uri);
            } catch (SQLException e) {
                throw new ServiceAccessException(this, e);
            } finally {
                if (connection != null) {
                    try {
                        if (!tmCommits) {
                            connection.commit();
                        }
                    } catch (SQLException e) {
                        throw new ServiceAccessException(this, e);
                    } finally {
                        try {
                            connection.close();
                        } catch (SQLException e) {
                            getLogger().log(e, LOG_CHANNEL, Logger.WARNING);
                        }
                    }
                }
            }
        } else {
            return adapter.retrieveObject(getCurrentConnection(), uri);
        }
    }

    /**
     * Update an object.
     *
     * @param object Object to update
     * @exception ServiceAccessException Error accessing the Service
     * @exception ObjectNotFoundException The object to update was not found
     */
    public void storeObject(Uri uri, ObjectNode object) throws ServiceAccessException, ObjectNotFoundException {

        adapter.storeObject(getCurrentConnection(), uri, object);
    }

    /**
     * Create a new object.
     *
     * @param object ObjectNode
     * @param uri Uri of the object we want to create
     * @exception ServiceAccessException Error accessing the Service
     * @exception ObjectAlreadyExistsException An object already exists
     *            at this Uri
     */
    public void createObject(Uri uri, ObjectNode object)
            throws ServiceAccessException, ObjectAlreadyExistsException {

        adapter.createObject(getCurrentConnection(), uri, object);
    }

    /**
     * Remove an object.
     *
     * @param object Object to remove
     * @exception ServiceAccessException Error accessing the Service
     * @exception ObjectNotFoundException The object to remove was not found
     */
    public void removeObject(Uri uri, ObjectNode object) throws ServiceAccessException, ObjectNotFoundException {

        adapter.removeObject(getCurrentConnection(), uri, object);
    }

    // ------------------------------------------- SecurityStore Implementation

    /**
     * Grant a new permission.
     *
     * @param permission Permission we want to create
     * @exception ServiceAccessException Error accessing the Service
     */
    public void grantPermission(Uri uri, NodePermission permission) throws ServiceAccessException {

        adapter.grantPermission(getCurrentConnection(), uri, permission);
    }

    /**
     * Revoke a permission.
     *
     * @param permission Permission we want to create
     * @exception ServiceAccessException Error accessing the Service
     */
    public void revokePermission(Uri uri, NodePermission permission) throws ServiceAccessException {

        adapter.revokePermission(getCurrentConnection(), uri, permission);
    }

    /**
     * Revoke all the permissions on an object.
     *
     * @exception ServiceAccessException Error accessing the Service
     */
    public void revokePermissions(Uri uri) throws ServiceAccessException {

        adapter.revokePermissions(getCurrentConnection(), uri);
    }

    /**
     * Enumerate permissions on an object.
     *
     * @exception ServiceAccessException Error accessing the Service
     */
    public Enumeration enumeratePermissions(Uri uri) throws ServiceAccessException {
        if (getCurrentlyActiveTransactionalResource() == null) {
            Connection connection = null;
            try {
                connection = getNewConnection();
                return adapter.enumeratePermissions(connection, uri);
            } catch (SQLException e) {
                throw new ServiceAccessException(this, e);
            } finally {
                if (connection != null) {
                    try {
                        if (!tmCommits) {
                            connection.commit();
                        }
                    } catch (SQLException e) {
                        throw new ServiceAccessException(this, e);
                    } finally {
                        try {
                            connection.close();
                        } catch (SQLException e) {
                            getLogger().log(e, LOG_CHANNEL, Logger.WARNING);
                        }
                    }
                }
            }
        } else {
            return adapter.enumeratePermissions(getCurrentConnection(), uri);
        }
    }

    // ----------------------------------------------- LockStore Implementation

    /**
     * Create a new lock.
     *
     * @param lock Lock token
     * @exception ServiceAccessException Service access error
     */
    public void putLock(Uri uri, NodeLock lock) throws ServiceAccessException {

        adapter.putLock(getCurrentConnection(), uri, lock);
    }

    /**
     * Refresh a lock.
     *
     * @param lock the lock to renew
     * @exception ServiceAccessException Service access error
     * @exception LockTokenNotFoundException Lock token was not found
     */
    public void renewLock(Uri uri, NodeLock lock) throws ServiceAccessException, LockTokenNotFoundException {

        adapter.renewLock(getCurrentConnection(), uri, lock);
    }

    /**
     * Unlock.
     *
     * @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 {

        adapter.removeLock(getCurrentConnection(), uri, lock);
    }

    /**
     * Kill 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 {

        adapter.killLock(getCurrentConnection(), uri, lock);
    }

    /**
     * Enumerate locks on an object.
     *
     * @return Enumeration List of locks which have been put on the subject
     * @exception ServiceAccessException Service access error
     */
    public Enumeration enumerateLocks(Uri uri) throws ServiceAccessException {
        if (getCurrentlyActiveTransactionalResource() == null) {
            Connection connection = null;
            try {
                connection = getNewConnection();
                return adapter.enumerateLocks(connection, uri);
            } catch (SQLException e) {
                throw new ServiceAccessException(this, e);
            } finally {
                if (connection != null) {
                    try {
                        if (!tmCommits) {
                            connection.commit();
                        }
                    } catch (SQLException e) {
                        throw new ServiceAccessException(this, e);
                    } finally {
                        try {
                            connection.close();
                        } catch (SQLException e) {
                            getLogger().log(e, LOG_CHANNEL, Logger.WARNING);
                        }
                    }
                }
            }
        } else {
            return adapter.enumerateLocks(getCurrentConnection(), uri);
        }
    }

    // -------------------------------- RevisionDescriptorsStore Implementation

    /**
     * Retrieve the revisions informations of an object.
     *
     * @param uri Uri
     * @exception ServiceAccessException Service access error
     * @exception RevisionDescriptorNotFoundException Revision descriptor
     * was not found
     */
    public NodeRevisionDescriptors retrieveRevisionDescriptors(Uri uri)
            throws ServiceAccessException, RevisionDescriptorNotFoundException {
        if (getCurrentlyActiveTransactionalResource() == null) {
            Connection connection = null;
            try {
                connection = getNewConnection();
                return adapter.retrieveRevisionDescriptors(connection, uri);
            } catch (SQLException e) {
                throw new ServiceAccessException(this, e);
            } finally {
                if (connection != null) {
                    try {
                        if (!tmCommits) {
                            connection.commit();
                        }
                    } catch (SQLException e) {
                        throw new ServiceAccessException(this, e);
                    } finally {
                        try {
                            connection.close();
                        } catch (SQLException e) {
                            getLogger().log(e, LOG_CHANNEL, Logger.WARNING);
                        }
                    }
                }
            }
        } else {
            return adapter.retrieveRevisionDescriptors(getCurrentConnection(), uri);
        }
    }

    /**
     * Create a new revision information object.
     *
     * @param uri Uri
     * @param revisionDescriptors Node revision descriptors
     * @exception ServiceAccessException Service access error
     */
    public void createRevisionDescriptors(Uri uri, NodeRevisionDescriptors revisionDescriptors)
            throws ServiceAccessException {

        adapter.createRevisionDescriptors(getCurrentConnection(), uri, revisionDescriptors);
    }

    /**
     * Update revision information.
     *
     * @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 {

        adapter.storeRevisionDescriptors(getCurrentConnection(), uri, revisionDescriptors);
    }

    /**
     * Remove revision information.
     *
     * @param uri Uri
     * @exception ServiceAccessException Service access error
     */
    public void removeRevisionDescriptors(Uri uri) throws ServiceAccessException {

        adapter.removeRevisionDescriptors(getCurrentConnection(), uri);
    }

    // --------------------------------- RevisionDescriptorStore Implementation

    /**
     * Retrieve an individual object's revision descriptor.
     *
     * @param uri uri
     * @param revisionNumber Node revision number
     */
    public NodeRevisionDescriptor retrieveRevisionDescriptor(Uri uri, NodeRevisionNumber revisionNumber)
            throws ServiceAccessException, RevisionDescriptorNotFoundException {
        if (getCurrentlyActiveTransactionalResource() == null) {
            Connection connection = null;
            try {
                connection = getNewConnection();
                return adapter.retrieveRevisionDescriptor(connection, uri, revisionNumber);
            } catch (SQLException e) {
                throw new ServiceAccessException(this, e);
            } finally {
                if (connection != null) {
                    try {
                        if (!tmCommits) {
                            connection.commit();
                        }
                    } catch (SQLException e) {
                        throw new ServiceAccessException(this, e);
                    } finally {
                        try {
                            connection.close();
                        } catch (SQLException e) {
                            getLogger().log(e, LOG_CHANNEL, Logger.WARNING);
                        }
                    }
                }
            }
        } else {
            return adapter.retrieveRevisionDescriptor(getCurrentConnection(), uri, revisionNumber);
        }
    }

    /**
     * Create a 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 {

        adapter.createRevisionDescriptor(getCurrentConnection(), uri, revisionDescriptor);
    }

    /**
     * Update a 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 {

        adapter.storeRevisionDescriptor(getCurrentConnection(), uri, revisionDescriptor);
    }

    /**
     * Remove a revision descriptor.
     *
     * @param uri Uri
     * @param revisionNumber Revision number
     * @exception ServiceAccessException Service access error
     */
    public void removeRevisionDescriptor(Uri uri, NodeRevisionNumber revisionNumber) throws ServiceAccessException {

        adapter.removeRevisionDescriptor(getCurrentConnection(), uri, revisionNumber);
    }

    // -------------------------------------------- ContentStore Implementation

    /**
     * Retrieve revision content.
     *
     * @param uri Uri
     * @param revisionDescriptor Node revision number
     */
    public NodeRevisionContent retrieveRevisionContent(Uri uri, NodeRevisionDescriptor revisionDescriptor)
            throws ServiceAccessException, RevisionNotFoundException {
        if (getCurrentlyActiveTransactionalResource() == null) {
            Connection connection = null;
            try {
                connection = getNewConnection();
                return adapter.retrieveRevisionContent(connection, uri, revisionDescriptor, true);
            } catch (SQLException e) {
                // only close connection in case of error as standard closing
                // will be done in adapter or upon closing of stream
                if (connection != null) {
                    try {
                        if (!tmCommits) {
                            connection.rollback();
                        }
                    } catch (SQLException e2) {
                        throw new ServiceAccessException(this, e2);
                    } finally {
                        try {
                            connection.close();
                        } catch (SQLException e2) {
                            getLogger().log(e2, LOG_CHANNEL, Logger.WARNING);
                        }
                    }
                }
                throw new ServiceAccessException(this, e);
            }
        } else {
            return adapter.retrieveRevisionContent(getCurrentConnection(), uri, revisionDescriptor, false);
        }
    }

    /**
     * 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 {

        adapter.createRevisionContent(getCurrentConnection(), uri, revisionDescriptor, revisionContent);
    }

    /**
     * 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 {

        adapter.storeRevisionContent(getCurrentConnection(), uri, revisionDescriptor, revisionContent);
    }

    /**
     * Remove revision.
     *
     * @param uri Uri
     * @param revisionDescriptor Node revision number
     */
    public void removeRevisionContent(Uri uri, NodeRevisionDescriptor revisionDescriptor)
            throws ServiceAccessException {

        adapter.removeRevisionContent(getCurrentConnection(), uri, revisionDescriptor);
    }

    // ------------------------------------------------------ Protected Methods

    // XXX just for visibility in RDBMSComparableResourcesPool
    protected TransactionalResource getCurrentlyActiveTransactionalResource() {
        return super.getCurrentlyActiveTransactionalResource();
    }

    public void start(Xid xid, int flags) throws XAException {
        TransactionalResource resource = getCurrentlyActiveTransactionalResource();
        if (resource != null) {
            throw new XAException(XAException.XAER_INVAL);
        }
        if (getLoggerFacade().isFineEnabled()) {
            getLoggerFacade().logFine(new StringBuffer(128).append("Thread ").append(Thread.currentThread())
                    .append(flags == TMNOFLAGS ? " starts" : flags == TMJOIN ? " joins" : " resumes")
                    .append(" work on behalf of transaction branch ").append(xid).toString());
        }

        TransactionalResource ts;
        switch (flags) {
        // a new transaction
        case TMNOFLAGS:
        case TMJOIN:
        default:
            try {
                ts = createTransactionResource(xid);
                ts.begin();
            } catch (Exception e) {
                getLoggerFacade().logSevere("Could not create new transactional  resource", e);
                throw new XAException(e.getMessage());
            }
            break;
        case TMRESUME:
            ts = getSuspendedTransactionalResource(xid);
            if (ts == null) {
                throw new XAException(XAException.XAER_NOTA);
            }
            ts.resume();
            removeSuspendedTransactionalResource(xid);
            break;
        }
        setCurrentlyActiveTransactionalResource(ts);
        addAcitveTransactionalResource(xid, ts);
    }

    /**
     * Get the Connection object associated with the current transaction.
     */
    protected Connection getCurrentConnection() throws ServiceAccessException {

        getLogger().log("Getting current connection for thread " + Thread.currentThread(), LOG_CHANNEL,
                Logger.DEBUG);
        TransactionId id = (TransactionId) getCurrentlyActiveTransactionalResource();
        if (id == null) {
            getLogger().log("No id for current thread - called outside transaction?", LOG_CHANNEL, Logger.DEBUG);
            return null;
        }
        return id.connection;
    }

    abstract protected Connection getNewConnection() throws SQLException;

    protected TransactionalResource createTransactionResource(Xid xid) throws SQLException {
        return new TransactionId(xid);
    }

    private class TransactionId extends AbstractTransactionalResource {
        Xid xid;
        int status;
        Connection connection;

        TransactionId(Xid xid) throws SQLException {
            super(xid);

            status = STATUS_ACTIVE;
            connection = getNewConnection();
        }

        public void commit() throws XAException {
            try {
                if (!tmCommits) {
                    connection.commit();
                }
            } catch (SQLException e) {
                throw new XAException(XAException.XA_RBCOMMFAIL);
            } finally {
                try {
                    connection.close();
                } catch (SQLException e) {
                    getLogger().log(e, LOG_CHANNEL, Logger.WARNING);
                }
            }
        }

        public void rollback() throws XAException {
            try {
                if (!tmCommits) {
                    connection.rollback();
                }
            } catch (SQLException e) {
                throw new XAException(XAException.XA_RBCOMMFAIL);
            } finally {
                try {
                    connection.close();
                } catch (SQLException e) {
                    getLogger().log(e, LOG_CHANNEL, Logger.WARNING);
                }
            }
        }

        public int prepare() throws XAException {
            // no check possible
            return XA_OK;
        }

        public void begin() throws XAException {
        }

        public void suspend() throws XAException {
        }

        public void resume() throws XAException {
        }

    }

}