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.geronimo.connector.ConnectionReleaser;
029 import org.apache.geronimo.connector.ConnectorTransactionContext;
030 import org.slf4j.Logger;
031 import org.slf4j.LoggerFactory;
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 Logger log = LoggerFactory.getLogger(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(
084 "supplying connection from tx cache " + connectionInfo.getConnectionHandle() + " for managed connection " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to tx caching interceptor " + this);
085 }
086 } else {
087 next.getConnection(connectionInfo);
088 managedConnectionInfos.setShared(connectionInfo.getManagedConnectionInfo());
089 if (log.isTraceEnabled()) {
090 log.trace(
091 "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(
116 "tx active, not returning connection" + connectionInfo.getConnectionHandle() + " for managed connection " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to tx caching interceptor " + this);
117 }
118 return;
119 }
120 //We are called from an afterCompletion synchronization. Remove the MCI from the ManagedConnectionInfos
121 //so we don't close it twice
122 ManagedConnectionInfos managedConnectionInfos = ConnectorTransactionContext.get(transaction, this);
123 managedConnectionInfos.remove(connectionInfo.getManagedConnectionInfo());
124 if (log.isTraceEnabled()) {
125 log.trace("tx ended, but not removed");
126 }
127 }
128 } catch (SystemException e) {
129 //ignore
130 }
131 if (log.isTraceEnabled()) {
132 log.trace("tx ended, returning connection" + connectionInfo.getConnectionHandle() + " for managed connection " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to tx caching interceptor " + this);
133 }
134 internalReturn(connectionInfo, connectionReturnAction);
135 }
136
137 private void internalReturn(ConnectionInfo connectionInfo, ConnectionReturnAction connectionReturnAction) {
138 if (connectionInfo.getManagedConnectionInfo().hasConnectionHandles()) {
139 if (log.isTraceEnabled()) {
140 log.trace(
141 "not returning connection from tx cache (has handles) " + connectionInfo.getConnectionHandle() + " for managed connection " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to tx caching interceptor " + this);
142 }
143 return;
144 }
145 //No transaction, no handles, we return it.
146 next.returnConnection(connectionInfo, connectionReturnAction);
147 if (log.isTraceEnabled()) {
148 log.trace(
149 "completed return of connection through tx cache " + connectionInfo.getConnectionHandle() + " for MCI: " + connectionInfo.getManagedConnectionInfo() + " and MC " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to tx caching interceptor " + this);
150 }
151 }
152
153 public void destroy() {
154 next.destroy();
155 }
156
157 public void afterCompletion(Object stuff) {
158 ManagedConnectionInfos managedConnectionInfos = (ManagedConnectionInfos) stuff;
159 ManagedConnectionInfo sharedMCI = managedConnectionInfos.getShared();
160 if (sharedMCI != null) {
161 if (log.isTraceEnabled()) {
162 log.trace("Transaction completed, attempting to return shared connection MCI: " + sharedMCI + " for managed connection " + sharedMCI.getManagedConnection() + " to tx caching interceptor " + this);
163 }
164 returnHandle(sharedMCI);
165 }
166 for (ManagedConnectionInfo managedConnectionInfo : managedConnectionInfos.getUnshared()) {
167 if (log.isTraceEnabled()) {
168 log.trace("Transaction completed, attempting to return unshared connection MCI: " + managedConnectionInfo + " for managed connection " + managedConnectionInfo.getManagedConnection() + " to tx caching interceptor " + this);
169 }
170 returnHandle(managedConnectionInfo);
171 }
172 }
173
174 private void returnHandle(ManagedConnectionInfo managedConnectionInfo) {
175 ConnectionInfo connectionInfo = new ConnectionInfo();
176 connectionInfo.setManagedConnectionInfo(managedConnectionInfo);
177 internalReturn(connectionInfo, ConnectionReturnAction.RETURN_HANDLE);
178 }
179
180 public static class ManagedConnectionInfos {
181 private ManagedConnectionInfo shared;
182 private Set<ManagedConnectionInfo> unshared = new HashSet<ManagedConnectionInfo>(1);
183
184 public ManagedConnectionInfo getShared() {
185 return shared;
186 }
187
188 public void setShared(ManagedConnectionInfo shared) {
189 this.shared = shared;
190 }
191
192 public Set<ManagedConnectionInfo> getUnshared() {
193 return unshared;
194 }
195
196 public void addUnshared(ManagedConnectionInfo unsharedMCI) {
197 unshared.add(unsharedMCI);
198 }
199
200 public boolean containsUnshared(ManagedConnectionInfo unsharedMCI) {
201 return unshared.contains(unsharedMCI);
202 }
203
204 public void remove(ManagedConnectionInfo managedConnectionInfo) {
205 if (shared == managedConnectionInfo) {
206 shared = null;
207 } else {
208 unshared.remove(managedConnectionInfo);
209 }
210 }
211 }
212 }