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}