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