001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.geronimo.connector.outbound; 019 020 import java.util.HashSet; 021 import java.util.Set; 022 023 import javax.resource.ResourceException; 024 import javax.transaction.SystemException; 025 import javax.transaction.Transaction; 026 import javax.transaction.TransactionManager; 027 028 import org.apache.commons.logging.Log; 029 import org.apache.commons.logging.LogFactory; 030 import org.apache.geronimo.connector.ConnectionReleaser; 031 import org.apache.geronimo.connector.ConnectorTransactionContext; 032 033 /** 034 * TransactionCachingInterceptor.java 035 * TODO: This implementation does not take account of unshareable resources 036 * TODO: This implementation does not take account of application security 037 * where several connections with different security info are obtained. 038 * TODO: This implementation does not take account of container managed security where, 039 * within one transaction, a security domain boundary is crossed 040 * and connections are obtained with two (or more) different subjects. 041 * <p/> 042 * I suggest a state pattern, with the state set in a threadlocal upon entering a component, 043 * will be a usable implementation. 044 * <p/> 045 * The afterCompletion method will need to move to an interface, and that interface include the 046 * security info to distinguish connections. 047 * <p/> 048 * <p/> 049 * Created: Mon Sep 29 15:07:07 2003 050 * 051 * @version 1.0 052 */ 053 public class TransactionCachingInterceptor implements ConnectionInterceptor, ConnectionReleaser { 054 protected static Log log = LogFactory.getLog(TransactionCachingInterceptor.class.getName()); 055 056 private final ConnectionInterceptor next; 057 private final TransactionManager transactionManager; 058 059 public TransactionCachingInterceptor(ConnectionInterceptor next, TransactionManager transactionManager) { 060 this.next = next; 061 this.transactionManager = transactionManager; 062 } 063 064 public void getConnection(ConnectionInfo connectionInfo) throws ResourceException { 065 //There can be an inactive transaction context when a connection is requested in 066 //Synchronization.afterCompletion(). 067 068 // get the current transation and status... if there is a problem just assume there is no transaction present 069 Transaction transaction = TxUtil.getTransactionIfActive(transactionManager); 070 if (transaction != null) { 071 ManagedConnectionInfos managedConnectionInfos = ConnectorTransactionContext.get(transaction, this); 072 if (connectionInfo.isUnshareable()) { 073 if (!managedConnectionInfos.containsUnshared(connectionInfo.getManagedConnectionInfo())) { 074 next.getConnection(connectionInfo); 075 managedConnectionInfos.addUnshared(connectionInfo.getManagedConnectionInfo()); 076 } 077 } else { 078 ManagedConnectionInfo managedConnectionInfo = managedConnectionInfos.getShared(); 079 if (managedConnectionInfo != null) { 080 connectionInfo.setManagedConnectionInfo(managedConnectionInfo); 081 //return; 082 if (log.isTraceEnabled()) { 083 log.trace("supplying connection from tx cache " + connectionInfo.getConnectionHandle() + " for managed connection " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to tx caching interceptor " + this); 084 } 085 } else { 086 next.getConnection(connectionInfo); 087 managedConnectionInfos.setShared(connectionInfo.getManagedConnectionInfo()); 088 if (log.isTraceEnabled()) { 089 log.trace("supplying connection from pool " + connectionInfo.getConnectionHandle() + " for managed connection " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to tx caching interceptor " + this); 090 } 091 } 092 } 093 } else { 094 next.getConnection(connectionInfo); 095 } 096 } 097 098 public void returnConnection(ConnectionInfo connectionInfo, ConnectionReturnAction connectionReturnAction) { 099 100 if (connectionReturnAction == ConnectionReturnAction.DESTROY) { 101 if (log.isTraceEnabled()) { 102 log.trace("destroying connection" + connectionInfo.getConnectionHandle() + " for managed connection " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to tx caching interceptor " + this); 103 } 104 next.returnConnection(connectionInfo, connectionReturnAction); 105 return; 106 } 107 Transaction transaction; 108 try { 109 transaction = transactionManager.getTransaction(); 110 if (transaction != null) { 111 if (TxUtil.isActive(transaction)) { 112 if (log.isTraceEnabled()) { 113 log.trace("tx active, not returning connection" + connectionInfo.getConnectionHandle() + " for managed connection " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to tx caching interceptor " + this); 114 } 115 return; 116 } 117 //We are called from an afterCompletion synchronization. Remove the MCI from the ManagedConnectionInfos 118 //so we don't close it twice 119 ManagedConnectionInfos managedConnectionInfos = ConnectorTransactionContext.get(transaction, this); 120 managedConnectionInfos.remove(connectionInfo.getManagedConnectionInfo()); 121 if (log.isTraceEnabled()) { 122 log.trace("tx ended, but not removed"); 123 } 124 } 125 } catch (SystemException e) { 126 //ignore 127 } 128 if (log.isTraceEnabled()) { 129 log.trace("tx ended, returning connection" + connectionInfo.getConnectionHandle() + " for managed connection " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to tx caching interceptor " + this); 130 } 131 internalReturn(connectionInfo, connectionReturnAction); 132 } 133 134 private void internalReturn(ConnectionInfo connectionInfo, ConnectionReturnAction connectionReturnAction) { 135 if (connectionInfo.getManagedConnectionInfo().hasConnectionHandles()) { 136 if (log.isTraceEnabled()) { 137 log.trace("not returning connection from tx cache (has handles) " + connectionInfo.getConnectionHandle() + " for managed connection " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to tx caching interceptor " + this); 138 } 139 return; 140 } 141 //No transaction, no handles, we return it. 142 next.returnConnection(connectionInfo, connectionReturnAction); 143 if (log.isTraceEnabled()) { 144 log.trace("completed return of connection through tx cache " + connectionInfo.getConnectionHandle() + " for MCI: " + connectionInfo.getManagedConnectionInfo() + " and MC " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to tx caching interceptor " + this); 145 } 146 } 147 148 public void destroy() { 149 next.destroy(); 150 } 151 152 public void afterCompletion(Object stuff) { 153 ManagedConnectionInfos managedConnectionInfos = (ManagedConnectionInfos) stuff; 154 ManagedConnectionInfo sharedMCI = managedConnectionInfos.getShared(); 155 if (sharedMCI != null) { 156 if (log.isTraceEnabled()) { 157 log.trace("Transaction completed, attempting to return shared connection MCI: " + sharedMCI + " for managed connection " + sharedMCI.getManagedConnection() + " to tx caching interceptor " + this); 158 } 159 returnHandle(sharedMCI); 160 } 161 for (ManagedConnectionInfo managedConnectionInfo : managedConnectionInfos.getUnshared()) { 162 if (log.isTraceEnabled()) { 163 log.trace("Transaction completed, attempting to return unshared connection MCI: " + managedConnectionInfo + " for managed connection " + managedConnectionInfo.getManagedConnection() + " to tx caching interceptor " + this); 164 } 165 returnHandle(managedConnectionInfo); 166 } 167 } 168 169 private void returnHandle(ManagedConnectionInfo managedConnectionInfo) { 170 ConnectionInfo connectionInfo = new ConnectionInfo(); 171 connectionInfo.setManagedConnectionInfo(managedConnectionInfo); 172 internalReturn(connectionInfo, ConnectionReturnAction.RETURN_HANDLE); 173 } 174 175 public static class ManagedConnectionInfos { 176 private ManagedConnectionInfo shared; 177 private Set<ManagedConnectionInfo> unshared = new HashSet<ManagedConnectionInfo>(1); 178 179 public ManagedConnectionInfo getShared() { 180 return shared; 181 } 182 183 public void setShared(ManagedConnectionInfo shared) { 184 this.shared = shared; 185 } 186 187 public Set<ManagedConnectionInfo> getUnshared() { 188 return unshared; 189 } 190 191 public void addUnshared(ManagedConnectionInfo unsharedMCI) { 192 unshared.add(unsharedMCI); 193 } 194 195 public boolean containsUnshared(ManagedConnectionInfo unsharedMCI) { 196 return unshared.contains(unsharedMCI); 197 } 198 199 public void remove(ManagedConnectionInfo managedConnectionInfo) { 200 if (shared == managedConnectionInfo) { 201 shared = null; 202 } else { 203 unshared.remove(managedConnectionInfo); 204 } 205 } 206 } 207 }