001/* 002 * Copyright 2002-2017 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.jca.cci.connection; 018 019import javax.resource.NotSupportedException; 020import javax.resource.ResourceException; 021import javax.resource.cci.Connection; 022import javax.resource.cci.ConnectionFactory; 023import javax.resource.spi.LocalTransactionException; 024 025import org.springframework.beans.factory.InitializingBean; 026import org.springframework.transaction.CannotCreateTransactionException; 027import org.springframework.transaction.TransactionDefinition; 028import org.springframework.transaction.TransactionException; 029import org.springframework.transaction.TransactionSystemException; 030import org.springframework.transaction.support.AbstractPlatformTransactionManager; 031import org.springframework.transaction.support.DefaultTransactionStatus; 032import org.springframework.transaction.support.ResourceTransactionManager; 033import org.springframework.transaction.support.TransactionSynchronizationManager; 034 035/** 036 * {@link org.springframework.transaction.PlatformTransactionManager} implementation 037 * that manages local transactions for a single CCI ConnectionFactory. 038 * Binds a CCI Connection from the specified ConnectionFactory to the thread, 039 * potentially allowing for one thread-bound Connection per ConnectionFactory. 040 * 041 * <p>Application code is required to retrieve the CCI Connection via 042 * {@link ConnectionFactoryUtils#getConnection(ConnectionFactory)} instead of a standard 043 * Java EE-style {@link ConnectionFactory#getConnection()} call. Spring classes such as 044 * {@link org.springframework.jca.cci.core.CciTemplate} use this strategy implicitly. 045 * If not used in combination with this transaction manager, the 046 * {@link ConnectionFactoryUtils} lookup strategy behaves exactly like the native 047 * DataSource lookup; it can thus be used in a portable fashion. 048 * 049 * <p>Alternatively, you can allow application code to work with the standard 050 * Java EE lookup pattern {@link ConnectionFactory#getConnection()}, for example 051 * for legacy code that is not aware of Spring at all. In that case, define a 052 * {@link TransactionAwareConnectionFactoryProxy} for your target ConnectionFactory, 053 * which will automatically participate in Spring-managed transactions. 054 * 055 * @author Thierry Templier 056 * @author Juergen Hoeller 057 * @since 1.2 058 * @see ConnectionFactoryUtils#getConnection(javax.resource.cci.ConnectionFactory) 059 * @see ConnectionFactoryUtils#releaseConnection 060 * @see TransactionAwareConnectionFactoryProxy 061 * @see org.springframework.jca.cci.core.CciTemplate 062 */ 063@SuppressWarnings("serial") 064public class CciLocalTransactionManager extends AbstractPlatformTransactionManager 065 implements ResourceTransactionManager, InitializingBean { 066 067 private ConnectionFactory connectionFactory; 068 069 070 /** 071 * Create a new CciLocalTransactionManager instance. 072 * A ConnectionFactory has to be set to be able to use it. 073 * @see #setConnectionFactory 074 */ 075 public CciLocalTransactionManager() { 076 } 077 078 /** 079 * Create a new CciLocalTransactionManager instance. 080 * @param connectionFactory CCI ConnectionFactory to manage local transactions for 081 */ 082 public CciLocalTransactionManager(ConnectionFactory connectionFactory) { 083 setConnectionFactory(connectionFactory); 084 afterPropertiesSet(); 085 } 086 087 088 /** 089 * Set the CCI ConnectionFactory that this instance should manage local 090 * transactions for. 091 */ 092 public void setConnectionFactory(ConnectionFactory cf) { 093 if (cf instanceof TransactionAwareConnectionFactoryProxy) { 094 // If we got a TransactionAwareConnectionFactoryProxy, we need to perform transactions 095 // for its underlying target ConnectionFactory, else JMS access code won't see 096 // properly exposed transactions (i.e. transactions for the target ConnectionFactory). 097 this.connectionFactory = ((TransactionAwareConnectionFactoryProxy) cf).getTargetConnectionFactory(); 098 } 099 else { 100 this.connectionFactory = cf; 101 } 102 } 103 104 /** 105 * Return the CCI ConnectionFactory that this instance manages local 106 * transactions for. 107 */ 108 public ConnectionFactory getConnectionFactory() { 109 return this.connectionFactory; 110 } 111 112 @Override 113 public void afterPropertiesSet() { 114 if (getConnectionFactory() == null) { 115 throw new IllegalArgumentException("Property 'connectionFactory' is required"); 116 } 117 } 118 119 120 @Override 121 public Object getResourceFactory() { 122 return getConnectionFactory(); 123 } 124 125 @Override 126 protected Object doGetTransaction() { 127 CciLocalTransactionObject txObject = new CciLocalTransactionObject(); 128 ConnectionHolder conHolder = 129 (ConnectionHolder) TransactionSynchronizationManager.getResource(getConnectionFactory()); 130 txObject.setConnectionHolder(conHolder); 131 return txObject; 132 } 133 134 @Override 135 protected boolean isExistingTransaction(Object transaction) { 136 CciLocalTransactionObject txObject = (CciLocalTransactionObject) transaction; 137 // Consider a pre-bound connection as transaction. 138 return txObject.hasConnectionHolder(); 139 } 140 141 @Override 142 protected void doBegin(Object transaction, TransactionDefinition definition) { 143 CciLocalTransactionObject txObject = (CciLocalTransactionObject) transaction; 144 Connection con = null; 145 146 try { 147 con = getConnectionFactory().getConnection(); 148 if (logger.isDebugEnabled()) { 149 logger.debug("Acquired Connection [" + con + "] for local CCI transaction"); 150 } 151 152 txObject.setConnectionHolder(new ConnectionHolder(con)); 153 txObject.getConnectionHolder().setSynchronizedWithTransaction(true); 154 155 con.getLocalTransaction().begin(); 156 int timeout = determineTimeout(definition); 157 if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { 158 txObject.getConnectionHolder().setTimeoutInSeconds(timeout); 159 } 160 TransactionSynchronizationManager.bindResource(getConnectionFactory(), txObject.getConnectionHolder()); 161 } 162 catch (NotSupportedException ex) { 163 ConnectionFactoryUtils.releaseConnection(con, getConnectionFactory()); 164 throw new CannotCreateTransactionException("CCI Connection does not support local transactions", ex); 165 } 166 catch (LocalTransactionException ex) { 167 ConnectionFactoryUtils.releaseConnection(con, getConnectionFactory()); 168 throw new CannotCreateTransactionException("Could not begin local CCI transaction", ex); 169 } 170 catch (Throwable ex) { 171 ConnectionFactoryUtils.releaseConnection(con, getConnectionFactory()); 172 throw new TransactionSystemException("Unexpected failure on begin of CCI local transaction", ex); 173 } 174 } 175 176 @Override 177 protected Object doSuspend(Object transaction) { 178 CciLocalTransactionObject txObject = (CciLocalTransactionObject) transaction; 179 txObject.setConnectionHolder(null); 180 return TransactionSynchronizationManager.unbindResource(getConnectionFactory()); 181 } 182 183 @Override 184 protected void doResume(Object transaction, Object suspendedResources) { 185 ConnectionHolder conHolder = (ConnectionHolder) suspendedResources; 186 TransactionSynchronizationManager.bindResource(getConnectionFactory(), conHolder); 187 } 188 189 protected boolean isRollbackOnly(Object transaction) throws TransactionException { 190 CciLocalTransactionObject txObject = (CciLocalTransactionObject) transaction; 191 return txObject.getConnectionHolder().isRollbackOnly(); 192 } 193 194 @Override 195 protected void doCommit(DefaultTransactionStatus status) { 196 CciLocalTransactionObject txObject = (CciLocalTransactionObject) status.getTransaction(); 197 Connection con = txObject.getConnectionHolder().getConnection(); 198 if (status.isDebug()) { 199 logger.debug("Committing CCI local transaction on Connection [" + con + "]"); 200 } 201 try { 202 con.getLocalTransaction().commit(); 203 } 204 catch (LocalTransactionException ex) { 205 throw new TransactionSystemException("Could not commit CCI local transaction", ex); 206 } 207 catch (ResourceException ex) { 208 throw new TransactionSystemException("Unexpected failure on commit of CCI local transaction", ex); 209 } 210 } 211 212 @Override 213 protected void doRollback(DefaultTransactionStatus status) { 214 CciLocalTransactionObject txObject = (CciLocalTransactionObject) status.getTransaction(); 215 Connection con = txObject.getConnectionHolder().getConnection(); 216 if (status.isDebug()) { 217 logger.debug("Rolling back CCI local transaction on Connection [" + con + "]"); 218 } 219 try { 220 con.getLocalTransaction().rollback(); 221 } 222 catch (LocalTransactionException ex) { 223 throw new TransactionSystemException("Could not roll back CCI local transaction", ex); 224 } 225 catch (ResourceException ex) { 226 throw new TransactionSystemException("Unexpected failure on rollback of CCI local transaction", ex); 227 } 228 } 229 230 @Override 231 protected void doSetRollbackOnly(DefaultTransactionStatus status) { 232 CciLocalTransactionObject txObject = (CciLocalTransactionObject) status.getTransaction(); 233 if (status.isDebug()) { 234 logger.debug("Setting CCI local transaction [" + txObject.getConnectionHolder().getConnection() + 235 "] rollback-only"); 236 } 237 txObject.getConnectionHolder().setRollbackOnly(); 238 } 239 240 @Override 241 protected void doCleanupAfterCompletion(Object transaction) { 242 CciLocalTransactionObject txObject = (CciLocalTransactionObject) transaction; 243 244 // Remove the connection holder from the thread. 245 TransactionSynchronizationManager.unbindResource(getConnectionFactory()); 246 txObject.getConnectionHolder().clear(); 247 248 Connection con = txObject.getConnectionHolder().getConnection(); 249 if (logger.isDebugEnabled()) { 250 logger.debug("Releasing CCI Connection [" + con + "] after transaction"); 251 } 252 ConnectionFactoryUtils.releaseConnection(con, getConnectionFactory()); 253 } 254 255 256 /** 257 * CCI local transaction object, representing a ConnectionHolder. 258 * Used as transaction object by CciLocalTransactionManager. 259 * @see ConnectionHolder 260 */ 261 private static class CciLocalTransactionObject { 262 263 private ConnectionHolder connectionHolder; 264 265 public void setConnectionHolder(ConnectionHolder connectionHolder) { 266 this.connectionHolder = connectionHolder; 267 } 268 269 public ConnectionHolder getConnectionHolder() { 270 return this.connectionHolder; 271 } 272 273 public boolean hasConnectionHolder() { 274 return (this.connectionHolder != null); 275 } 276 } 277 278}