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