blob: 62dbf1382fdc72be55336c98808cdf44577234b4 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.geronimo.connector.outbound;
import java.util.HashSet;
import java.util.Set;
import javax.resource.ResourceException;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import org.apache.geronimo.connector.ConnectionReleaser;
import org.apache.geronimo.connector.ConnectorTransactionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* TransactionCachingInterceptor.java
* TODO: This implementation does not take account of unshareable resources
* TODO: This implementation does not take account of application security
* where several connections with different security info are obtained.
* TODO: This implementation does not take account of container managed security where,
* within one transaction, a security domain boundary is crossed
* and connections are obtained with two (or more) different subjects.
* <p/>
* I suggest a state pattern, with the state set in a threadlocal upon entering a component,
* will be a usable implementation.
* <p/>
* The afterCompletion method will need to move to an interface, and that interface include the
* security info to distinguish connections.
* <p/>
* <p/>
* Created: Mon Sep 29 15:07:07 2003
*
* @version 1.0
*/
public class TransactionCachingInterceptor implements ConnectionInterceptor, ConnectionReleaser {
protected static Logger log = LoggerFactory.getLogger(TransactionCachingInterceptor.class.getName());
private final ConnectionInterceptor next;
private final TransactionManager transactionManager;
public TransactionCachingInterceptor(ConnectionInterceptor next, TransactionManager transactionManager) {
this.next = next;
this.transactionManager = transactionManager;
}
public void getConnection(ConnectionInfo connectionInfo) throws ResourceException {
//There can be an inactive transaction context when a connection is requested in
//Synchronization.afterCompletion().
// get the current transation and status... if there is a problem just assume there is no transaction present
Transaction transaction = TxUtil.getTransactionIfActive(transactionManager);
if (transaction != null) {
ManagedConnectionInfos managedConnectionInfos = ConnectorTransactionContext.get(transaction, this);
if (connectionInfo.isUnshareable()) {
if (!managedConnectionInfos.containsUnshared(connectionInfo.getManagedConnectionInfo())) {
next.getConnection(connectionInfo);
managedConnectionInfos.addUnshared(connectionInfo.getManagedConnectionInfo());
}
} else {
ManagedConnectionInfo managedConnectionInfo = managedConnectionInfos.getShared();
if (managedConnectionInfo != null) {
ManagedConnectionInfo previousMci = connectionInfo.getManagedConnectionInfo();
if (previousMci != null && previousMci != managedConnectionInfo && previousMci.getManagedConnection() != null) {
//This might occur if more than one connection were obtained before a UserTransaction were started.
//enlists connection
next.getConnection(connectionInfo);
managedConnectionInfos.addUnshared(previousMci);
} else {
connectionInfo.setManagedConnectionInfo(managedConnectionInfo);
//return;
if (log.isTraceEnabled()) {
log.trace("supplying connection from tx cache " + connectionInfo.getConnectionHandle() + " for managed connection " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to tx caching interceptor " + this);
}
}
} else {
next.getConnection(connectionInfo);
managedConnectionInfos.setShared(connectionInfo.getManagedConnectionInfo());
if (log.isTraceEnabled()) {
log.trace("supplying connection from pool " + connectionInfo.getConnectionHandle() + " for managed connection " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to tx caching interceptor " + this);
}
}
}
} else {
next.getConnection(connectionInfo);
}
}
public void returnConnection(ConnectionInfo connectionInfo, ConnectionReturnAction connectionReturnAction) {
if (connectionReturnAction == ConnectionReturnAction.DESTROY) {
if (log.isTraceEnabled()) {
log.trace("destroying connection" + connectionInfo.getConnectionHandle() + " for managed connection " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to tx caching interceptor " + this);
}
next.returnConnection(connectionInfo, connectionReturnAction);
return;
}
Transaction transaction;
try {
transaction = transactionManager.getTransaction();
if (transaction != null) {
if (TxUtil.isActive(transaction)) {
if (log.isTraceEnabled()) {
log.trace("tx active, not returning connection" + connectionInfo.getConnectionHandle() + " for managed connection " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to tx caching interceptor " + this);
}
return;
}
//We are called from an afterCompletion synchronization. Remove the MCI from the ManagedConnectionInfos
//so we don't close it twice
ManagedConnectionInfos managedConnectionInfos = ConnectorTransactionContext.get(transaction, this);
managedConnectionInfos.remove(connectionInfo.getManagedConnectionInfo());
if (log.isTraceEnabled()) {
log.trace("tx ended, but not removed");
}
}
} catch (SystemException e) {
//ignore
}
if (log.isTraceEnabled()) {
log.trace("tx ended, returning connection" + connectionInfo.getConnectionHandle() + " for managed connection " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to tx caching interceptor " + this);
}
internalReturn(connectionInfo, connectionReturnAction);
}
private void internalReturn(ConnectionInfo connectionInfo, ConnectionReturnAction connectionReturnAction) {
if (connectionInfo.getManagedConnectionInfo().hasConnectionHandles()) {
if (log.isTraceEnabled()) {
log.trace("not returning connection from tx cache (has handles) " + connectionInfo.getConnectionHandle() + " for managed connection " + connectionInfo.getManagedConnectionInfo().getManagedConnection() + " to tx caching interceptor " + this);
}
return;
}
//No transaction, no handles, we return it.
next.returnConnection(connectionInfo, connectionReturnAction);
if (log.isTraceEnabled()) {
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);
}
}
public void destroy() {
next.destroy();
}
public void afterCompletion(Object stuff) {
ManagedConnectionInfos managedConnectionInfos = (ManagedConnectionInfos) stuff;
ManagedConnectionInfo sharedMCI = managedConnectionInfos.getShared();
if (sharedMCI != null) {
if (log.isTraceEnabled()) {
log.trace("Transaction completed, attempting to return shared connection MCI: " + sharedMCI + " for managed connection " + sharedMCI.getManagedConnection() + " to tx caching interceptor " + this);
}
returnHandle(sharedMCI);
}
for (ManagedConnectionInfo managedConnectionInfo : managedConnectionInfos.getUnshared()) {
if (log.isTraceEnabled()) {
log.trace("Transaction completed, attempting to return unshared connection MCI: " + managedConnectionInfo + " for managed connection " + managedConnectionInfo.getManagedConnection() + " to tx caching interceptor " + this);
}
returnHandle(managedConnectionInfo);
}
}
private void returnHandle(ManagedConnectionInfo managedConnectionInfo) {
ConnectionInfo connectionInfo = new ConnectionInfo();
connectionInfo.setManagedConnectionInfo(managedConnectionInfo);
internalReturn(connectionInfo, ConnectionReturnAction.RETURN_HANDLE);
}
public static class ManagedConnectionInfos {
private ManagedConnectionInfo shared;
private Set<ManagedConnectionInfo> unshared = new HashSet<ManagedConnectionInfo>(1);
public ManagedConnectionInfo getShared() {
return shared;
}
public void setShared(ManagedConnectionInfo shared) {
this.shared = shared;
}
public Set<ManagedConnectionInfo> getUnshared() {
return unshared;
}
public void addUnshared(ManagedConnectionInfo unsharedMCI) {
unshared.add(unsharedMCI);
}
public boolean containsUnshared(ManagedConnectionInfo unsharedMCI) {
return unshared.contains(unsharedMCI);
}
public void remove(ManagedConnectionInfo managedConnectionInfo) {
if (shared == managedConnectionInfo) {
shared = null;
} else {
unshared.remove(managedConnectionInfo);
}
}
}
}