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.Collections;
021 import java.util.HashSet;
022 import java.util.Iterator;
023 import java.util.Set;
024
025 import javax.resource.ResourceException;
026 import javax.transaction.SystemException;
027 import javax.transaction.Transaction;
028 import javax.transaction.TransactionManager;
029
030 import org.apache.commons.logging.Log;
031 import org.apache.commons.logging.LogFactory;
032 import org.apache.geronimo.connector.ConnectionReleaser;
033 import org.apache.geronimo.connector.ConnectorTransactionContext;
034
035 /**
036 * TransactionCachingInterceptor.java
037 * TODO: This implementation does not take account of unshareable resources
038 * TODO: This implementation does not take account of application security
039 * where several connections with different security info are obtained.
040 * TODO: This implementation does not take account of container managed security where,
041 * within one transaction, a security domain boundary is crossed
042 * and connections are obtained with two (or more) different subjects.
043 * <p/>
044 * I suggest a state pattern, with the state set in a threadlocal upon entering a component,
045 * will be a usable implementation.
046 * <p/>
047 * The afterCompletion method will need to move to an interface, and that interface include the
048 * security info to distinguish connections.
049 * <p/>
050 * <p/>
051 * Created: Mon Sep 29 15:07:07 2003
052 *
053 * @version 1.0
054 */
055 public class TransactionCachingInterceptor implements ConnectionInterceptor, ConnectionReleaser {
056 protected static Log log = LogFactory.getLog(TransactionCachingInterceptor.class.getName());
057
058 private final ConnectionInterceptor next;
059 private final TransactionManager transactionManager;
060
061 public TransactionCachingInterceptor(ConnectionInterceptor next, TransactionManager transactionManager) {
062 this.next = next;
063 this.transactionManager = transactionManager;
064 }
065
066 public void getConnection(ConnectionInfo connectionInfo) throws ResourceException {
067 //There can be an inactive transaction context when a connection is requested in
068 //Synchronization.afterCompletion().
069
070 // get the current transation and status... if there is a problem just assume there is no transaction present
071 Transaction transaction = TxUtil.getTransactionIfActive(transactionManager);
072 if (transaction != null) {
073 ManagedConnectionInfos managedConnectionInfos = ConnectorTransactionContext.get(transaction, this);
074 if (connectionInfo.isUnshareable()) {
075 if (!managedConnectionInfos.containsUnshared(connectionInfo.getManagedConnectionInfo())) {
076 next.getConnection(connectionInfo);
077 managedConnectionInfos.addUnshared(connectionInfo.getManagedConnectionInfo());
078 }
079 } else {
080 ManagedConnectionInfo managedConnectionInfo = managedConnectionInfos.getShared();
081 if (managedConnectionInfo != null) {
082 connectionInfo.setManagedConnectionInfo(managedConnectionInfo);
083 //return;
084 if (log.isTraceEnabled()) {
085 log.trace("supplying connection from tx cache " + connectionInfo.getConnectionHandle() + " for managed connection " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to tx caching interceptor " + this);
086 }
087 } else {
088 next.getConnection(connectionInfo);
089 managedConnectionInfos.setShared(connectionInfo.getManagedConnectionInfo());
090 if (log.isTraceEnabled()) {
091 log.trace("supplying connection from pool " + connectionInfo.getConnectionHandle() + " for managed connection " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to tx caching interceptor " + this);
092 }
093 }
094 }
095 } else {
096 next.getConnection(connectionInfo);
097 }
098 }
099
100 public void returnConnection(ConnectionInfo connectionInfo, ConnectionReturnAction connectionReturnAction) {
101
102 if (connectionReturnAction == ConnectionReturnAction.DESTROY) {
103 if (log.isTraceEnabled()) {
104 log.trace("destroying connection" + connectionInfo.getConnectionHandle() + " for managed connection " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to tx caching interceptor " + this);
105 }
106 next.returnConnection(connectionInfo, connectionReturnAction);
107 return;
108 }
109 Transaction transaction;
110 try {
111 transaction = transactionManager.getTransaction();
112 if (transaction != null) {
113 if (TxUtil.isActive(transaction)) {
114 if (log.isTraceEnabled()) {
115 log.trace("tx active, not returning connection" + connectionInfo.getConnectionHandle() + " for managed connection " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to tx caching interceptor " + this);
116 }
117 return;
118 }
119 //We are called from an afterCompletion synchronization. Remove the MCI from the ManagedConnectionInfos
120 //so we don't close it twice
121 ManagedConnectionInfos managedConnectionInfos = ConnectorTransactionContext.get(transaction, this);
122 managedConnectionInfos.remove(connectionInfo.getManagedConnectionInfo());
123 if (log.isTraceEnabled()) {
124 log.trace("tx ended, but not removed");
125 }
126 }
127 } catch (SystemException e) {
128 //ignore
129 }
130 if (log.isTraceEnabled()) {
131 log.trace("tx ended, returning connection" + connectionInfo.getConnectionHandle() + " for managed connection " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to tx caching interceptor " + this);
132 }
133 internalReturn(connectionInfo, connectionReturnAction);
134 }
135
136 private void internalReturn(ConnectionInfo connectionInfo, ConnectionReturnAction connectionReturnAction) {
137 if (connectionInfo.getManagedConnectionInfo().hasConnectionHandles()) {
138 if (log.isTraceEnabled()) {
139 log.trace("not returning connection from tx cache (has handles) " + connectionInfo.getConnectionHandle() + " for managed connection " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to tx caching interceptor " + this);
140 }
141 return;
142 }
143 //No transaction, no handles, we return it.
144 next.returnConnection(connectionInfo, connectionReturnAction);
145 if (log.isTraceEnabled()) {
146 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);
147 }
148 }
149
150 public void destroy() {
151 next.destroy();
152 }
153
154 public void afterCompletion(Object stuff) {
155 ManagedConnectionInfos managedConnectionInfos = (ManagedConnectionInfos) stuff;
156 ManagedConnectionInfo sharedMCI = managedConnectionInfos.getShared();
157 if (sharedMCI != null) {
158 if (log.isTraceEnabled()) {
159 log.trace("Transaction completed, attempting to return shared connection MCI: " + sharedMCI + " for managed connection " + sharedMCI.getManagedConnection() + " to tx caching interceptor " + this);
160 }
161 returnHandle(sharedMCI);
162 }
163 for (Iterator iterator = managedConnectionInfos.getUnshared().iterator(); iterator.hasNext();) {
164 ManagedConnectionInfo managedConnectionInfo = (ManagedConnectionInfo) iterator.next();
165 if (log.isTraceEnabled()) {
166 log.trace("Transaction completed, attempting to return unshared connection MCI: " + managedConnectionInfo + " for managed connection " + managedConnectionInfo.getManagedConnection() + " to tx caching interceptor " + this);
167 }
168 returnHandle(managedConnectionInfo);
169 }
170 }
171
172 private void returnHandle(ManagedConnectionInfo managedConnectionInfo) {
173 ConnectionInfo connectionInfo = new ConnectionInfo();
174 connectionInfo.setManagedConnectionInfo(managedConnectionInfo);
175 internalReturn(connectionInfo, ConnectionReturnAction.RETURN_HANDLE);
176 }
177
178 public static class ManagedConnectionInfos {
179 private ManagedConnectionInfo shared;
180 private Set unshared = Collections.EMPTY_SET;
181
182 public ManagedConnectionInfo getShared() {
183 return shared;
184 }
185
186 public void setShared(ManagedConnectionInfo shared) {
187 this.shared = shared;
188 }
189
190 public Set getUnshared() {
191 return unshared;
192 }
193
194 public void addUnshared(ManagedConnectionInfo unsharedMCI) {
195 if (this.unshared == Collections.EMPTY_SET) {
196 this.unshared = new HashSet();
197 }
198 this.unshared.add(unsharedMCI);
199 }
200
201 public boolean containsUnshared(ManagedConnectionInfo unsharedMCI) {
202 return this.unshared.contains(unsharedMCI);
203 }
204
205 public void remove(ManagedConnectionInfo managedConnectionInfo) {
206 if (shared == managedConnectionInfo) {
207 shared = null;
208 } else {
209 unshared.remove(managedConnectionInfo);
210 }
211 }
212 }
213
214 }