Open Source Repository

Home /spring/spring-transaction-3.0.5 | Repository Home



org/springframework/jca/cci/connection/CciLocalTransactionManager.java
/*
 * Copyright 2002-2007 the original author or authors.
 *
 * 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.springframework.jca.cci.connection;

import javax.resource.NotSupportedException;
import javax.resource.ResourceException;
import javax.resource.cci.Connection;
import javax.resource.cci.ConnectionFactory;
import javax.resource.spi.LocalTransactionException;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.transaction.CannotCreateTransactionException;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionSystemException;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
import org.springframework.transaction.support.DefaultTransactionStatus;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.transaction.support.ResourceTransactionManager;

/**
 {@link org.springframework.transaction.PlatformTransactionManager} implementation
 * that manages local transactions for a single CCI ConnectionFactory.
 * Binds a CCI Connection from the specified ConnectionFactory to the thread,
 * potentially allowing for one thread-bound Connection per ConnectionFactory.
 *
 <p>Application code is required to retrieve the CCI Connection via
 {@link ConnectionFactoryUtils#getConnection(ConnectionFactory)} instead of a standard
 * J2EE-style {@link ConnectionFactory#getConnection()} call. Spring classes such as
 {@link org.springframework.jca.cci.core.CciTemplate} use this strategy implicitly.
 * If not used in combination with this transaction manager, the
 {@link ConnectionFactoryUtils} lookup strategy behaves exactly like the native
 * DataSource lookup; it can thus be used in a portable fashion.
 *
 <p>Alternatively, you can allow application code to work with the standard
 * J2EE lookup pattern {@link ConnectionFactory#getConnection()}, for example
 * for legacy code that is not aware of Spring at all. In that case, define a
 {@link TransactionAwareConnectionFactoryProxy} for your target ConnectionFactory,
 * which will automatically participate in Spring-managed transactions.
 *
 @author Thierry Templier
 @author Juergen Hoeller
 @since 1.2
 @see ConnectionFactoryUtils#getConnection(javax.resource.cci.ConnectionFactory)
 @see ConnectionFactoryUtils#releaseConnection
 @see TransactionAwareConnectionFactoryProxy
 @see org.springframework.jca.cci.core.CciTemplate
 */
public class CciLocalTransactionManager extends AbstractPlatformTransactionManager
    implements ResourceTransactionManager, InitializingBean {

  private ConnectionFactory connectionFactory;


  /**
   * Create a new CciLocalTransactionManager instance.
   * A ConnectionFactory has to be set to be able to use it.
   @see #setConnectionFactory
   */
  public CciLocalTransactionManager() {
  }

  /**
   * Create a new CciLocalTransactionManager instance.
   @param connectionFactory CCI ConnectionFactory to manage local transactions for
   */
  public CciLocalTransactionManager(ConnectionFactory connectionFactory) {
    setConnectionFactory(connectionFactory);
    afterPropertiesSet();
  }


  /**
   * Set the CCI ConnectionFactory that this instance should manage local
   * transactions for.
   */
  public void setConnectionFactory(ConnectionFactory cf) {
    if (cf instanceof TransactionAwareConnectionFactoryProxy) {
      // If we got a TransactionAwareConnectionFactoryProxy, we need to perform transactions
      // for its underlying target ConnectionFactory, else JMS access code won't see
      // properly exposed transactions (i.e. transactions for the target ConnectionFactory).
      this.connectionFactory = ((TransactionAwareConnectionFactoryProxycf).getTargetConnectionFactory();
    }
    else {
      this.connectionFactory = cf;
    }
  }

  /**
   * Return the CCI ConnectionFactory that this instance manages local
   * transactions for.
   */
  public ConnectionFactory getConnectionFactory() {
    return this.connectionFactory;
  }

  public void afterPropertiesSet() {
    if (getConnectionFactory() == null) {
      throw new IllegalArgumentException("Property 'connectionFactory' is required");
    }
  }


  public Object getResourceFactory() {
    return getConnectionFactory();
  }

  @Override
  protected Object doGetTransaction() {
    CciLocalTransactionObject txObject = new CciLocalTransactionObject();
    ConnectionHolder conHolder =
        (ConnectionHolderTransactionSynchronizationManager.getResource(getConnectionFactory());
    txObject.setConnectionHolder(conHolder);
    return txObject;
  }

  @Override
  protected boolean isExistingTransaction(Object transaction) {
    CciLocalTransactionObject txObject = (CciLocalTransactionObjecttransaction;
    // Consider a pre-bound connection as transaction.
    return (txObject.getConnectionHolder() != null);
  }

  @Override
  protected void doBegin(Object transaction, TransactionDefinition definition) {
    CciLocalTransactionObject txObject = (CciLocalTransactionObjecttransaction;

    Connection con = null;

    try {
      con = getConnectionFactory().getConnection();
      if (logger.isDebugEnabled()) {
        logger.debug("Acquired Connection [" + con + "] for local CCI transaction");
      }

      txObject.setConnectionHolder(new ConnectionHolder(con));
      txObject.getConnectionHolder().setSynchronizedWithTransaction(true);

      con.getLocalTransaction().begin();
      int timeout = determineTimeout(definition);
      if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
        txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
      }
      TransactionSynchronizationManager.bindResource(getConnectionFactory(), txObject.getConnectionHolder());
    }

    catch (NotSupportedException ex) {
      ConnectionFactoryUtils.releaseConnection(con, getConnectionFactory());
      throw new CannotCreateTransactionException("CCI Connection does not support local transactions", ex);
    }
    catch (LocalTransactionException ex) {
      ConnectionFactoryUtils.releaseConnection(con, getConnectionFactory());
      throw new CannotCreateTransactionException("Could not begin local CCI transaction", ex);
    }
    catch (ResourceException ex) {
      ConnectionFactoryUtils.releaseConnection(con, getConnectionFactory());
      throw new TransactionSystemException("Unexpected failure on begin of CCI local transaction", ex);
    }
  }

  @Override
  protected Object doSuspend(Object transaction) {
    CciLocalTransactionObject txObject = (CciLocalTransactionObjecttransaction;
    txObject.setConnectionHolder(null);
    return TransactionSynchronizationManager.unbindResource(getConnectionFactory());
  }

  @Override
  protected void doResume(Object transaction, Object suspendedResources) {
    ConnectionHolder conHolder = (ConnectionHoldersuspendedResources;
    TransactionSynchronizationManager.bindResource(getConnectionFactory(), conHolder);
  }

  protected boolean isRollbackOnly(Object transactionthrows TransactionException {
    CciLocalTransactionObject txObject = (CciLocalTransactionObjecttransaction;
    return txObject.getConnectionHolder().isRollbackOnly();
  }

  @Override
  protected void doCommit(DefaultTransactionStatus status) {
    CciLocalTransactionObject txObject = (CciLocalTransactionObjectstatus.getTransaction();
    Connection con = txObject.getConnectionHolder().getConnection();
    if (status.isDebug()) {
      logger.debug("Committing CCI local transaction on Connection [" + con + "]");
    }
    try {
      con.getLocalTransaction().commit();
    }
    catch (LocalTransactionException ex) {
      throw new TransactionSystemException("Could not commit CCI local transaction", ex);
    }
    catch (ResourceException ex) {
      throw new TransactionSystemException("Unexpected failure on commit of CCI local transaction", ex);
    }
  }

  @Override
  protected void doRollback(DefaultTransactionStatus status) {
    CciLocalTransactionObject txObject = (CciLocalTransactionObjectstatus.getTransaction();
    Connection con = txObject.getConnectionHolder().getConnection();
    if (status.isDebug()) {
      logger.debug("Rolling back CCI local transaction on Connection [" + con + "]");
    }
    try {
      con.getLocalTransaction().rollback();
    }
    catch (LocalTransactionException ex) {
      throw new TransactionSystemException("Could not roll back CCI local transaction", ex);
    }
    catch (ResourceException ex) {
      throw new TransactionSystemException("Unexpected failure on rollback of CCI local transaction", ex);
    }
  }

  @Override
  protected void doSetRollbackOnly(DefaultTransactionStatus status) {
    CciLocalTransactionObject txObject = (CciLocalTransactionObjectstatus.getTransaction();
    if (status.isDebug()) {
      logger.debug("Setting CCI local transaction [" + txObject.getConnectionHolder().getConnection() +
          "] rollback-only");
    }
    txObject.getConnectionHolder().setRollbackOnly();
  }

  @Override
  protected void doCleanupAfterCompletion(Object transaction) {
    CciLocalTransactionObject txObject = (CciLocalTransactionObjecttransaction;

    // Remove the connection holder from the thread.
    TransactionSynchronizationManager.unbindResource(getConnectionFactory());
    txObject.getConnectionHolder().clear();

    Connection con = txObject.getConnectionHolder().getConnection();
    if (logger.isDebugEnabled()) {
      logger.debug("Releasing CCI Connection [" + con + "] after transaction");
    }
    ConnectionFactoryUtils.releaseConnection(con, getConnectionFactory());
  }


  /**
   * CCI local transaction object, representing a ConnectionHolder.
   * Used as transaction object by CciLocalTransactionManager.
   @see ConnectionHolder
   */
  private static class CciLocalTransactionObject {

    private ConnectionHolder connectionHolder;

    public void setConnectionHolder(ConnectionHolder connectionHolder) {
      this.connectionHolder = connectionHolder;
    }

    public ConnectionHolder getConnectionHolder() {
      return connectionHolder;
    }
  }

}