Weird, the copy I sent to myself did contain the files. I'm inlining them in
the mail body.

-Alejandro

--- AbstractService.java ---

/*
 * $Header$
 * $Revision$
 * $Date$
 *
 * ====================================================================
 *
 * 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.common;

import java.util.Hashtable;
import java.util.Vector;
import java.util.Arrays;

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

import org.apache.slide.transaction.SlideTransactionManager;

/**
 * Slide Service abstract implementation with support for multiple
 * simultaneous transaction context.
 *
 * @version $Revision$
 */
public abstract class AbstractService extends AbstractServiceBase
    implements Service {


    // --------------------------------------------------------------
Constants


    public static final int TX_IDLE = 0;
    public static final int TX_PREPARED = 1;
    public static final int TX_SUSPENDED = 2;


    // ----------------------------------------------------- Instance
Variables


    /**
     * Current transaction context.
     */
    protected Hashtable currentContexts = new Hashtable();


    // ----------------------------------------------------- XAResource
Mathods


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

        ContextTuple currentContextTuple =
            (ContextTuple) currentContexts.get(Thread.currentThread());
        Xid currentContext =
            currentContextTuple!=null?currentContextTuple.getXid():null;

        if (currentContext == null)
            throw new XAException(XAException.XAER_NOTA);
        if (xid == null)
            throw new XAException(XAException.XAER_INVAL);
        if (!Arrays.equals(currentContext.getGlobalTransactionId(),
            xid.getGlobalTransactionId()))
            throw new XAException(XAException.XAER_PROTO);

        if (!onePhase && currentContextTuple.getStatus() != TX_PREPARED)
            throw new XAException(XAException.XAER_PROTO);
        if (onePhase &&
            (!((currentContextTuple.getStatus() == TX_IDLE) ||
               (currentContextTuple.getStatus() == TX_SUSPENDED))))
            throw new XAException(XAException.XAER_PROTO);

        if (((ContextTuple) currentContexts.get(Thread.currentThread()))
            .getRollbackOnly()) {
            rollback(xid);
            throw new XAException(XAException.XA_RBROLLBACK);
        }

        currentContexts.remove(Thread.currentThread());

    }


    /**
     * Ends the work performed on behalf of a transaction branch. The
     * resource manager disassociates the XA resource from the transaction
     * branch specified and let the transaction be completed.
     * If TMSUSPEND is specified in flags, the transaction branch is
     * temporarily suspended in incomplete state. The transaction context is
     * in suspened state and must be resumed via start with TMRESUME
specified.
     * If TMFAIL is specified, the portion of work has failed. The resource
     * manager may mark the transaction as rollback-only.
     * If TMSUCCESS is specified, the portion of work has completed
     * successfully.
     *
     * @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 {

        ContextTuple currentContextTuple =
            (ContextTuple) currentContexts.get(Thread.currentThread());
        Xid currentContext =
            currentContextTuple!=null?currentContextTuple.getXid():null;

        if (currentContext == null)
            throw new XAException(XAException.XAER_NOTA);
        if (xid == null)
            throw new XAException(XAException.XAER_INVAL);
        if (!Arrays.equals(currentContext.getGlobalTransactionId(),
            xid.getGlobalTransactionId()))
            throw new XAException(XAException.XAER_PROTO);

        if (flags == XAResource.TMSUSPEND)
            currentContextTuple.setStatus(TX_SUSPENDED);

        if (flags == XAResource.TMFAIL)
            ((ContextTuple) currentContexts.get(Thread.currentThread()))
                .setRollbackOnly(true);

    }


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

        ContextTuple currentContextTuple =
            (ContextTuple) currentContexts.get(Thread.currentThread());
        Xid currentContext =
            currentContextTuple!=null?currentContextTuple.getXid():null;

        if (currentContext == null)
            throw new XAException(XAException.XAER_NOTA);
        if (xid == null)
            throw new XAException(XAException.XAER_INVAL);
        if (!Arrays.equals(currentContext.getGlobalTransactionId(),
            xid.getGlobalTransactionId()))
            throw new XAException(XAException.XAER_PROTO);

        currentContexts.remove(Thread.currentThread());

    }


    /**
     * Obtain the current transaction timeout value set for this XAResource
     * instance. If XAResource.setTransactionTimeout was not use prior to
     * invoking this method, the return value is the default timeout set for
     * the resource manager; otherwise, the value used in the previous
     * setTransactionTimeout call is returned.
     *
     * @return the transaction timeout value in seconds.
     * @exception XAException An error has occurred. Possible exception
     * values are XAER_RMERR, XAER_RMFAIL.
     */
    public int getTransactionTimeout()
        throws XAException {

        return ((ContextTuple) currentContexts.get(Thread.currentThread()))
            .getTransactionTimeout();

    }


    /**
     * This method is called to determine if the resource manager instance
     * represented by the target object is the same as the resouce manager
     * instance represented by the parameter xares.
     *
     * @param xares An XAResource object whose resource manager instance is
     * to be compared with the resource manager instance of the target
object.
     * @return true if it's the same RM instance; otherwise false.
     * @exception XAException An error has occurred. Possible exception
values
     * are XAER_RMERR, XAER_RMFAIL.
     */
    public boolean isSameRM(XAResource xares)
        throws XAException {

        if (xares == null)
            return false;

        return (this == xares);

    }


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

        ContextTuple currentContextTuple =
            (ContextTuple) currentContexts.get(Thread.currentThread());
        Xid currentContext =
            currentContextTuple!=null?currentContextTuple.getXid():null;

        if (currentContext == null)
            throw new XAException(XAException.XAER_NOTA);
        if (xid == null)
            throw new XAException(XAException.XAER_INVAL);

        if (!Arrays.equals(currentContext.getGlobalTransactionId(),
            xid.getGlobalTransactionId()))
            throw new XAException(XAException.XAER_PROTO);

        if (!((currentContextTuple.getStatus() == TX_IDLE) ||
              (currentContextTuple.getStatus() == TX_SUSPENDED)))
            throw new XAException(XAException.XAER_PROTO);

        if (((ContextTuple) currentContexts.get(Thread.currentThread()))
            .getRollbackOnly()) {
            // FIXME: Don't know if should be automatically rollbacked in
that
            // case
            throw new XAException(XAException.XA_RBROLLBACK);
        }

        currentContextTuple.setStatus(TX_PREPARED);

        return XAResource.XA_OK;

    }


    /**
     * Obtain a list of prepared transaction branches from a resource
manager.
     * The transaction manager calls this method during recovery to obtain
the
     * list of transaction branches that are currently in prepared or
     * heuristically completed states.
     *
     * @param flag One of TMSTARTRSCAN, TMENDRSCAN, TMNOFLAGS. TMNOFLAGS
must
     * be used when no other flags are set in flags.
     * @return The resource manager returns zero or more XIDs for the
     * transaction branches that are currently in a prepared or
heuristically
     * completed state. If an error occurs during the operation, the
resource
     * manager should throw the appropriate XAException.
     * @exception XAException An error has occurred. Possible values are
     * XAER_RMERR, XAER_RMFAIL, XAER_INVAL, and XAER_PROTO.
     */
    public Xid[] recover(int flag)
        throws XAException {

        ContextTuple currentContextTuple =
           (ContextTuple) currentContexts.get(Thread.currentThread());
        Xid currentContext =
           currentContextTuple!=null?currentContextTuple.getXid():null;

        Vector list = new Vector();

        if ((currentContextTuple.getStatus() == TX_PREPARED) &&
            (currentContext != null))
           list.addElement(currentContext);

        return (Xid[]) list.toArray(new Xid[list.size()]);

    }


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

        ContextTuple currentContextTuple =
            (ContextTuple) currentContexts.get(Thread.currentThread());
        Xid currentContext =
            currentContextTuple!=null?currentContextTuple.getXid():null;

        if (currentContext == null)
            throw new XAException(XAException.XAER_NOTA);
        if (xid == null)
            throw new XAException(XAException.XAER_INVAL);
        if (!Arrays.equals(currentContext.getGlobalTransactionId(),
            xid.getGlobalTransactionId()))
            throw new XAException(XAException.XAER_PROTO);

        currentContexts.remove(Thread.currentThread());

    }


    /**
     * Set the current transaction timeout value for this XAResource
instance.
     *
     * @param seconds the transaction timeout value in seconds.
     * @return true if transaction timeout value is set successfully;
     * otherwise false.
     * @exception XAException An error has occurred. Possible exception
     * values are XAER_RMERR, XAER_RMFAIL, or XAER_INVAL.
     */
    public boolean setTransactionTimeout(int seconds)
        throws XAException {

        ((ContextTuple) currentContexts.get(Thread.currentThread()))
            .setTransactionTimeout(seconds);
        return true;

    }


    /**
     * Start work on behalf of a transaction branch specified in xid If
     * TMJOIN is specified, the start is for joining a transaction
previously
     * seen by the resource manager. If TMRESUME is specified, the start is
     * to resume a suspended transaction specified in the parameter xid. If
     * neither TMJOIN nor TMRESUME is specified and the transaction
specified
     * by xid has previously been seen by the resource manager, the resource
     * manager throws the XAException exception with XAER_DUPID error code.
     *
     * @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 {

        ContextTuple currentContextTuple =
            (ContextTuple) currentContexts.get(Thread.currentThread());
        Xid currentContext =
            currentContextTuple!=null?currentContextTuple.getXid():null;

        if (xid == null)
            throw new XAException(XAException.XAER_INVAL);
        if ( (currentContext != null) &&
             (!Arrays.equals(currentContext.getGlobalTransactionId(),
              xid.getGlobalTransactionId())) )
            throw new XAException(XAException.XAER_OUTSIDE);

        switch (flags) {
        case XAResource.TMNOFLAGS:
            if (currentContext != null)
                throw new XAException(XAException.XAER_INVAL);
            currentContext = xid;
            // is the idle status really ok ???
            currentContexts.put
                (Thread.currentThread(),new ContextTuple
                    (xid, TX_IDLE,
                     SlideTransactionManager.DEFAULT_TRANSACTION_TIMEOUT,
                     false));
            break;
        case XAResource.TMJOIN:
            if (currentContext == null)
                throw new XAException(XAException.XAER_NOTA);
            if (!Arrays.equals(currentContext.getGlobalTransactionId(),
                xid.getGlobalTransactionId()))
                throw new XAException(XAException.XAER_INVAL);
            break;
        case XAResource.TMRESUME:
            if (currentContext == null)
                throw new XAException(XAException.XAER_NOTA);
            if (currentContextTuple.getStatus() != TX_SUSPENDED)
                throw new XAException(XAException.XAER_INVAL);
            currentContextTuple.setStatus(TX_IDLE);
            break;
        }

    }


    // ------------------------------------------------ ContextTuple
InnerClass


    /**
     * inner class to pack both the xid and the status
     **/
    private class ContextTuple {
        private Xid xid;
        private int status;
        private int transactionTimeout;
        private boolean rollbackOnly;
        public ContextTuple(Xid xid, int status, int transactionTimeout,
                            boolean rollbackOnly) {
            this.xid    = xid;
            setStatus(status);
            setTransactionTimeout(transactionTimeout);
            setRollbackOnly(rollbackOnly);
        }
        public Xid getXid() {
            return xid;
        }
        public int getStatus() {
            return status;
        }
        public void setStatus(int status) {
            this.status = status;
        }
        public int getTransactionTimeout() {
            return transactionTimeout;
        }
        public void setTransactionTimeout(int transactionTimeout) {
            this.transactionTimeout = transactionTimeout;
        }
        public boolean getRollbackOnly() {
            return rollbackOnly;
        }
        public void setRollbackOnly(boolean rollbackOnly) {
            this.rollbackOnly = rollbackOnly;
        }

    }
}


--- ExternalTransactionStore.java ---

/*
 * $Header$
 * $Revision$
 * $Date$
 *
 * ====================================================================
 *
 * 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;

import java.util.Hashtable;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.transaction.TransactionManager;

import org.apache.slide.common.Namespace;
import org.apache.slide.common.ServiceParameterErrorException;
import org.apache.slide.common.ServiceParameterMissingException;
import org.apache.slide.util.logger.Logger;

/**
 * Store that allows for transactional caching of data over JTA transactions
 * provided by an external transaction manager.
 *
 * @version $Revision$
 */
public class ExternalTransactionStore extends ExtendedStore {
  private TransactionManager txManager;

  /**
   * Parameter to specify the JNDI name of the transaction manager.
   */
  public static final String TX_MANAGER_NAME_PARAMETER = "tx-manager-name";

  /**
   * Default value for the transaction manager name parameter.
   */
  public static final String DEFAULT_TX_MANAGER_NAME =
"java:/TransactionManager";

  private static final String LOG_CHANNEL = AppServerStore.class.getName();

  /**
   * Default constructor.
   */
  public ExternalTransactionStore() {
  }

  /* (non-Javadoc)
   * @see org.apache.slide.common.Service#setParameters(java.util.Hashtable)
   */
  public void setParameters(Hashtable parameters)
      throws ServiceParameterErrorException,
ServiceParameterMissingException {
    super.setParameters(parameters);

    String txManagerName = (String)
parameters.get(TX_MANAGER_NAME_PARAMETER);
    if (txManagerName == null) {
      txManagerName = DEFAULT_TX_MANAGER_NAME;
    }

    Logger logger = getLogger();
    logger.log("Setting transaction manager name for store " + getName() +
        " to " + txManagerName, LOG_CHANNEL, Logger.INFO);

    try {
      txManager = (TransactionManager) new
InitialContext().lookup(txManagerName);
    }
    catch (NamingException e) {
      logger.log("Could not retrieve transaction manager named: " +
          txManagerName + "from JNDI context! Using default value", e,
          LOG_CHANNEL, Logger.WARNING);
    }
  }

  /* (non-Javadoc)
   * @see org.apache.slide.common.Service#setNamespace(
   *  org.apache.slide.common.Namespace)
   */
  public void setNamespace(Namespace namespace) {
    super.setNamespace(namespace);
    namespace.initTransactionManager(txManager);
  }
}



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to