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 }