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