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}