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.ResourceException;
020import javax.resource.cci.Connection;
021import javax.resource.cci.ConnectionFactory;
022import javax.resource.cci.ConnectionSpec;
023
024import org.apache.commons.logging.Log;
025import org.apache.commons.logging.LogFactory;
026
027import org.springframework.jca.cci.CannotGetCciConnectionException;
028import org.springframework.lang.Nullable;
029import org.springframework.transaction.support.ResourceHolderSynchronization;
030import org.springframework.transaction.support.TransactionSynchronizationManager;
031import org.springframework.util.Assert;
032
033/**
034 * Helper class that provides static methods for obtaining CCI Connections
035 * from a {@link javax.resource.cci.ConnectionFactory}. Includes special
036 * support for Spring-managed transactional Connections, e.g. managed
037 * by {@link CciLocalTransactionManager} or
038 * {@link org.springframework.transaction.jta.JtaTransactionManager}.
039 *
040 * <p>Used internally by {@link org.springframework.jca.cci.core.CciTemplate},
041 * Spring's CCI operation objects and the {@link CciLocalTransactionManager}.
042 * Can also be used directly in application code.
043 *
044 * @author Thierry Templier
045 * @author Juergen Hoeller
046 * @since 1.2
047 * @see #getConnection
048 * @see #releaseConnection
049 * @see CciLocalTransactionManager
050 * @see org.springframework.transaction.jta.JtaTransactionManager
051 * @see org.springframework.transaction.support.TransactionSynchronizationManager
052 */
053public abstract class ConnectionFactoryUtils {
054
055        private static final Log logger = LogFactory.getLog(ConnectionFactoryUtils.class);
056
057
058        /**
059         * Obtain a Connection from the given ConnectionFactory. Translates ResourceExceptions
060         * into the Spring hierarchy of unchecked generic data access exceptions, simplifying
061         * calling code and making any exception that is thrown more meaningful.
062         * <p>Is aware of a corresponding Connection bound to the current thread, for example
063         * when using {@link CciLocalTransactionManager}. Will bind a Connection to the thread
064         * if transaction synchronization is active (e.g. if in a JTA transaction).
065         * @param cf the ConnectionFactory to obtain Connection from
066         * @return a CCI Connection from the given ConnectionFactory
067         * @throws org.springframework.jca.cci.CannotGetCciConnectionException
068         * if the attempt to get a Connection failed
069         * @see #releaseConnection
070         */
071        public static Connection getConnection(ConnectionFactory cf) throws CannotGetCciConnectionException {
072                return getConnection(cf, null);
073        }
074
075        /**
076         * Obtain a Connection from the given ConnectionFactory. Translates ResourceExceptions
077         * into the Spring hierarchy of unchecked generic data access exceptions, simplifying
078         * calling code and making any exception that is thrown more meaningful.
079         * <p>Is aware of a corresponding Connection bound to the current thread, for example
080         * when using {@link CciLocalTransactionManager}. Will bind a Connection to the thread
081         * if transaction synchronization is active (e.g. if in a JTA transaction).
082         * @param cf the ConnectionFactory to obtain Connection from
083         * @param spec the ConnectionSpec for the desired Connection (may be {@code null}).
084         * Note: If this is specified, a new Connection will be obtained for every call,
085         * without participating in a shared transactional Connection.
086         * @return a CCI Connection from the given ConnectionFactory
087         * @throws org.springframework.jca.cci.CannotGetCciConnectionException
088         * if the attempt to get a Connection failed
089         * @see #releaseConnection
090         */
091        public static Connection getConnection(ConnectionFactory cf, @Nullable ConnectionSpec spec)
092                        throws CannotGetCciConnectionException {
093                try {
094                        if (spec != null) {
095                                Assert.notNull(cf, "No ConnectionFactory specified");
096                                return cf.getConnection(spec);
097                        }
098                        else {
099                                return doGetConnection(cf);
100                        }
101                }
102                catch (ResourceException ex) {
103                        throw new CannotGetCciConnectionException("Could not get CCI Connection", ex);
104                }
105        }
106
107        /**
108         * Actually obtain a CCI Connection from the given ConnectionFactory.
109         * Same as {@link #getConnection}, but throwing the original ResourceException.
110         * <p>Is aware of a corresponding Connection bound to the current thread, for example
111         * when using {@link CciLocalTransactionManager}. Will bind a Connection to the thread
112         * if transaction synchronization is active (e.g. if in a JTA transaction).
113         * <p>Directly accessed by {@link TransactionAwareConnectionFactoryProxy}.
114         * @param cf the ConnectionFactory to obtain Connection from
115         * @return a CCI Connection from the given ConnectionFactory
116         * @throws ResourceException if thrown by CCI API methods
117         * @see #doReleaseConnection
118         */
119        public static Connection doGetConnection(ConnectionFactory cf) throws ResourceException {
120                Assert.notNull(cf, "No ConnectionFactory specified");
121
122                ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(cf);
123                if (conHolder != null) {
124                        return conHolder.getConnection();
125                }
126
127                logger.debug("Opening CCI Connection");
128                Connection con = cf.getConnection();
129
130                if (TransactionSynchronizationManager.isSynchronizationActive()) {
131                        conHolder = new ConnectionHolder(con);
132                        conHolder.setSynchronizedWithTransaction(true);
133                        TransactionSynchronizationManager.registerSynchronization(new ConnectionSynchronization(conHolder, cf));
134                        TransactionSynchronizationManager.bindResource(cf, conHolder);
135                }
136
137                return con;
138        }
139
140        /**
141         * Determine whether the given JCA CCI Connection is transactional, that is,
142         * bound to the current thread by Spring's transaction facilities.
143         * @param con the Connection to check
144         * @param cf the ConnectionFactory that the Connection was obtained from
145         * (may be {@code null})
146         * @return whether the Connection is transactional
147         */
148        public static boolean isConnectionTransactional(Connection con, @Nullable ConnectionFactory cf) {
149                if (cf == null) {
150                        return false;
151                }
152                ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(cf);
153                return (conHolder != null && conHolder.getConnection() == con);
154        }
155
156        /**
157         * Close the given Connection, obtained from the given ConnectionFactory,
158         * if it is not managed externally (that is, not bound to the thread).
159         * @param con the Connection to close if necessary
160         * (if this is {@code null}, the call will be ignored)
161         * @param cf the ConnectionFactory that the Connection was obtained from
162         * (can be {@code null})
163         * @see #getConnection
164         */
165        public static void releaseConnection(@Nullable Connection con, @Nullable ConnectionFactory cf) {
166                try {
167                        doReleaseConnection(con, cf);
168                }
169                catch (ResourceException ex) {
170                        logger.debug("Could not close CCI Connection", ex);
171                }
172                catch (Throwable ex) {
173                        // We don't trust the CCI driver: It might throw RuntimeException or Error.
174                        logger.debug("Unexpected exception on closing CCI Connection", ex);
175                }
176        }
177
178        /**
179         * Actually close the given Connection, obtained from the given ConnectionFactory.
180         * Same as {@link #releaseConnection}, but throwing the original ResourceException.
181         * <p>Directly accessed by {@link TransactionAwareConnectionFactoryProxy}.
182         * @param con the Connection to close if necessary
183         * (if this is {@code null}, the call will be ignored)
184         * @param cf the ConnectionFactory that the Connection was obtained from
185         * (can be {@code null})
186         * @throws ResourceException if thrown by JCA CCI methods
187         * @see #doGetConnection
188         */
189        public static void doReleaseConnection(@Nullable Connection con, @Nullable ConnectionFactory cf)
190                        throws ResourceException {
191
192                if (con == null || isConnectionTransactional(con, cf)) {
193                        return;
194                }
195                con.close();
196        }
197
198
199        /**
200         * Callback for resource cleanup at the end of a non-native CCI transaction
201         * (e.g. when participating in a JTA transaction).
202         */
203        private static class ConnectionSynchronization
204                        extends ResourceHolderSynchronization<ConnectionHolder, ConnectionFactory> {
205
206                public ConnectionSynchronization(ConnectionHolder connectionHolder, ConnectionFactory connectionFactory) {
207                        super(connectionHolder, connectionFactory);
208                }
209
210                @Override
211                protected void releaseResource(ConnectionHolder resourceHolder, ConnectionFactory resourceKey) {
212                        releaseConnection(resourceHolder.getConnection(), resourceKey);
213                }
214        }
215
216}