/*
 * Copyright 2002-2018 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.lang.Nullable;
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.ResourceTransactionManager;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;

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.

Application code is required to retrieve the CCI Connection via ConnectionFactoryUtils.getConnection(ConnectionFactory) instead of a standard Java EE-style ConnectionFactory.getConnection() call. Spring classes such as CciTemplate use this strategy implicitly. If not used in combination with this transaction manager, the ConnectionFactoryUtils lookup strategy behaves exactly like the native DataSource lookup; it can thus be used in a portable fashion.

Alternatively, you can allow application code to work with the standard Java EE lookup pattern ConnectionFactory.getConnection(), for example for legacy code that is not aware of Spring at all. In that case, define a TransactionAwareConnectionFactoryProxy for your target ConnectionFactory, which will automatically participate in Spring-managed transactions.

Author:Thierry Templier, Juergen Hoeller
See Also:
Since:1.2
/** * {@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 * Java EE-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 * Java EE 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 */
@SuppressWarnings("serial") public class CciLocalTransactionManager extends AbstractPlatformTransactionManager implements ResourceTransactionManager, InitializingBean { @Nullable private ConnectionFactory connectionFactory;
Create a new CciLocalTransactionManager instance. A ConnectionFactory has to be set to be able to use it.
See Also:
  • setConnectionFactory
/** * 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.
Params:
  • connectionFactory – the CCI ConnectionFactory to manage local transactions for
/** * Create a new CciLocalTransactionManager instance. * @param connectionFactory the 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.
/** * Set the CCI ConnectionFactory that this instance should manage local * transactions for. */
public void setConnectionFactory(@Nullable 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 = ((TransactionAwareConnectionFactoryProxy) cf).getTargetConnectionFactory(); } else { this.connectionFactory = cf; } }
Return the CCI ConnectionFactory that this instance manages local transactions for.
/** * Return the CCI ConnectionFactory that this instance manages local * transactions for. */
@Nullable public ConnectionFactory getConnectionFactory() { return this.connectionFactory; } private ConnectionFactory obtainConnectionFactory() { ConnectionFactory connectionFactory = getConnectionFactory(); Assert.state(connectionFactory != null, "No ConnectionFactory set"); return connectionFactory; } @Override public void afterPropertiesSet() { if (getConnectionFactory() == null) { throw new IllegalArgumentException("Property 'connectionFactory' is required"); } } @Override public Object getResourceFactory() { return obtainConnectionFactory(); } @Override protected Object doGetTransaction() { CciLocalTransactionObject txObject = new CciLocalTransactionObject(); ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainConnectionFactory()); txObject.setConnectionHolder(conHolder); return txObject; } @Override protected boolean isExistingTransaction(Object transaction) { CciLocalTransactionObject txObject = (CciLocalTransactionObject) transaction; // Consider a pre-bound connection as transaction. return txObject.hasConnectionHolder(); } @Override protected void doBegin(Object transaction, TransactionDefinition definition) { CciLocalTransactionObject txObject = (CciLocalTransactionObject) transaction; ConnectionFactory connectionFactory = obtainConnectionFactory(); Connection con = null; try { con = connectionFactory.getConnection(); if (logger.isDebugEnabled()) { logger.debug("Acquired Connection [" + con + "] for local CCI transaction"); } ConnectionHolder connectionHolder = new ConnectionHolder(con); connectionHolder.setSynchronizedWithTransaction(true); con.getLocalTransaction().begin(); int timeout = determineTimeout(definition); if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { connectionHolder.setTimeoutInSeconds(timeout); } txObject.setConnectionHolder(connectionHolder); TransactionSynchronizationManager.bindResource(connectionFactory, connectionHolder); } catch (NotSupportedException ex) { ConnectionFactoryUtils.releaseConnection(con, connectionFactory); throw new CannotCreateTransactionException("CCI Connection does not support local transactions", ex); } catch (LocalTransactionException ex) { ConnectionFactoryUtils.releaseConnection(con, connectionFactory); throw new CannotCreateTransactionException("Could not begin local CCI transaction", ex); } catch (Throwable ex) { ConnectionFactoryUtils.releaseConnection(con, connectionFactory); throw new TransactionSystemException("Unexpected failure on begin of CCI local transaction", ex); } } @Override protected Object doSuspend(Object transaction) { CciLocalTransactionObject txObject = (CciLocalTransactionObject) transaction; txObject.setConnectionHolder(null); return TransactionSynchronizationManager.unbindResource(obtainConnectionFactory()); } @Override protected void doResume(@Nullable Object transaction, Object suspendedResources) { ConnectionHolder conHolder = (ConnectionHolder) suspendedResources; TransactionSynchronizationManager.bindResource(obtainConnectionFactory(), conHolder); } protected boolean isRollbackOnly(Object transaction) throws TransactionException { CciLocalTransactionObject txObject = (CciLocalTransactionObject) transaction; return txObject.getConnectionHolder().isRollbackOnly(); } @Override protected void doCommit(DefaultTransactionStatus status) { CciLocalTransactionObject txObject = (CciLocalTransactionObject) status.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 = (CciLocalTransactionObject) status.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 = (CciLocalTransactionObject) status.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 = (CciLocalTransactionObject) transaction; ConnectionFactory connectionFactory = obtainConnectionFactory(); // Remove the connection holder from the thread. TransactionSynchronizationManager.unbindResource(connectionFactory); txObject.getConnectionHolder().clear(); Connection con = txObject.getConnectionHolder().getConnection(); if (logger.isDebugEnabled()) { logger.debug("Releasing CCI Connection [" + con + "] after transaction"); } ConnectionFactoryUtils.releaseConnection(con, connectionFactory); }
CCI local transaction object, representing a ConnectionHolder. Used as transaction object by CciLocalTransactionManager.
See Also:
  • ConnectionHolder
/** * CCI local transaction object, representing a ConnectionHolder. * Used as transaction object by CciLocalTransactionManager. * @see ConnectionHolder */
private static class CciLocalTransactionObject { @Nullable private ConnectionHolder connectionHolder; public void setConnectionHolder(@Nullable ConnectionHolder connectionHolder) { this.connectionHolder = connectionHolder; } public ConnectionHolder getConnectionHolder() { Assert.state(this.connectionHolder != null, "No ConnectionHolder available"); return this.connectionHolder; } public boolean hasConnectionHolder() { return (this.connectionHolder != null); } } }