001/* 002 * Copyright 2002-2012 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 java.lang.reflect.InvocationHandler; 020import java.lang.reflect.InvocationTargetException; 021import java.lang.reflect.Method; 022import java.lang.reflect.Proxy; 023import javax.resource.ResourceException; 024import javax.resource.cci.Connection; 025import javax.resource.cci.ConnectionFactory; 026 027/** 028 * Proxy for a target CCI {@link javax.resource.cci.ConnectionFactory}, adding 029 * awareness of Spring-managed transactions. Similar to a transactional JNDI 030 * ConnectionFactory as provided by a J2EE server. 031 * 032 * <p>Data access code that should remain unaware of Spring's data access support 033 * can work with this proxy to seamlessly participate in Spring-managed transactions. 034 * Note that the transaction manager, for example the {@link CciLocalTransactionManager}, 035 * still needs to work with underlying ConnectionFactory, <i>not</i> with this proxy. 036 * 037 * <p><b>Make sure that TransactionAwareConnectionFactoryProxy is the outermost 038 * ConnectionFactory of a chain of ConnectionFactory proxies/adapters.</b> 039 * TransactionAwareConnectionFactoryProxy can delegate either directly to the 040 * target connection pool or to some intermediate proxy/adapter like 041 * {@link ConnectionSpecConnectionFactoryAdapter}. 042 * 043 * <p>Delegates to {@link ConnectionFactoryUtils} for automatically participating in 044 * thread-bound transactions, for example managed by {@link CciLocalTransactionManager}. 045 * {@code getConnection} calls and {@code close} calls on returned Connections 046 * will behave properly within a transaction, i.e. always operate on the transactional 047 * Connection. If not within a transaction, normal ConnectionFactory behavior applies. 048 * 049 * <p>This proxy allows data access code to work with the plain JCA CCI API and still 050 * participate in Spring-managed transactions, similar to CCI code in a J2EE/JTA 051 * environment. However, if possible, use Spring's ConnectionFactoryUtils, CciTemplate or 052 * CCI operation objects to get transaction participation even without a proxy for 053 * the target ConnectionFactory, avoiding the need to define such a proxy in the first place. 054 * 055 * <p><b>NOTE:</b> This ConnectionFactory proxy needs to return wrapped Connections 056 * in order to handle close calls properly. Therefore, the returned Connections cannot 057 * be cast to a native CCI Connection type or to a connection pool implementation type. 058 * 059 * @author Juergen Hoeller 060 * @since 1.2 061 * @see javax.resource.cci.ConnectionFactory#getConnection 062 * @see javax.resource.cci.Connection#close 063 * @see ConnectionFactoryUtils#doGetConnection 064 * @see ConnectionFactoryUtils#doReleaseConnection 065 */ 066@SuppressWarnings("serial") 067public class TransactionAwareConnectionFactoryProxy extends DelegatingConnectionFactory { 068 069 /** 070 * Create a new TransactionAwareConnectionFactoryProxy. 071 * @see #setTargetConnectionFactory 072 */ 073 public TransactionAwareConnectionFactoryProxy() { 074 } 075 076 /** 077 * Create a new TransactionAwareConnectionFactoryProxy. 078 * @param targetConnectionFactory the target ConnectionFactory 079 */ 080 public TransactionAwareConnectionFactoryProxy(ConnectionFactory targetConnectionFactory) { 081 setTargetConnectionFactory(targetConnectionFactory); 082 afterPropertiesSet(); 083 } 084 085 086 /** 087 * Delegate to ConnectionFactoryUtils for automatically participating in Spring-managed 088 * transactions. Throws the original ResourceException, if any. 089 * @return a transactional Connection if any, a new one else 090 * @see org.springframework.jca.cci.connection.ConnectionFactoryUtils#doGetConnection 091 */ 092 @Override 093 public Connection getConnection() throws ResourceException { 094 Connection con = ConnectionFactoryUtils.doGetConnection(getTargetConnectionFactory()); 095 return getTransactionAwareConnectionProxy(con, getTargetConnectionFactory()); 096 } 097 098 /** 099 * Wrap the given Connection with a proxy that delegates every method call to it 100 * but delegates {@code close} calls to ConnectionFactoryUtils. 101 * @param target the original Connection to wrap 102 * @param cf ConnectionFactory that the Connection came from 103 * @return the wrapped Connection 104 * @see javax.resource.cci.Connection#close() 105 * @see ConnectionFactoryUtils#doReleaseConnection 106 */ 107 protected Connection getTransactionAwareConnectionProxy(Connection target, ConnectionFactory cf) { 108 return (Connection) Proxy.newProxyInstance( 109 Connection.class.getClassLoader(), 110 new Class<?>[] {Connection.class}, 111 new TransactionAwareInvocationHandler(target, cf)); 112 } 113 114 115 /** 116 * Invocation handler that delegates close calls on CCI Connections 117 * to ConnectionFactoryUtils for being aware of thread-bound transactions. 118 */ 119 private static class TransactionAwareInvocationHandler implements InvocationHandler { 120 121 private final Connection target; 122 123 private final ConnectionFactory connectionFactory; 124 125 public TransactionAwareInvocationHandler(Connection target, ConnectionFactory cf) { 126 this.target = target; 127 this.connectionFactory = cf; 128 } 129 130 @Override 131 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 132 // Invocation on Connection interface coming in... 133 134 if (method.getName().equals("equals")) { 135 // Only consider equal when proxies are identical. 136 return (proxy == args[0]); 137 } 138 else if (method.getName().equals("hashCode")) { 139 // Use hashCode of Connection proxy. 140 return System.identityHashCode(proxy); 141 } 142 else if (method.getName().equals("getLocalTransaction")) { 143 if (ConnectionFactoryUtils.isConnectionTransactional(this.target, this.connectionFactory)) { 144 throw new javax.resource.spi.IllegalStateException( 145 "Local transaction handling not allowed within a managed transaction"); 146 } 147 } 148 else if (method.getName().equals("close")) { 149 // Handle close method: only close if not within a transaction. 150 ConnectionFactoryUtils.doReleaseConnection(this.target, this.connectionFactory); 151 return null; 152 } 153 154 // Invoke method on target Connection. 155 try { 156 return method.invoke(this.target, args); 157 } 158 catch (InvocationTargetException ex) { 159 throw ex.getTargetException(); 160 } 161 } 162 } 163 164}