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