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 package org.apache.geronimo.connector;
018
019 import java.util.HashMap;
020 import java.util.Map;
021 import java.util.concurrent.ConcurrentHashMap;
022
023 import javax.transaction.Status;
024 import javax.transaction.Synchronization;
025 import javax.transaction.SystemException;
026 import javax.transaction.Transaction;
027
028 import org.apache.geronimo.connector.outbound.TransactionCachingInterceptor;
029 import org.apache.geronimo.transaction.manager.TransactionImpl;
030
031 /**
032 * @version $Rev: 539962 $ $Date: 2007-05-20 18:11:34 -0400 (Sun, 20 May 2007) $
033 */
034 public class ConnectorTransactionContext {
035 private static final ConcurrentHashMap<Transaction, ConnectorTransactionContext> DATA_INDEX = new ConcurrentHashMap<Transaction, ConnectorTransactionContext>();
036
037 public static ConnectorTransactionContext get(Transaction transaction) {
038 if (transaction == null) {
039 throw new NullPointerException("transaction is null");
040 }
041
042 ConnectorTransactionContext ctx = DATA_INDEX.get(transaction);
043 if (ctx == null) {
044 ctx = new ConnectorTransactionContext();
045
046 try {
047 int status = transaction.getStatus();
048 if (status != Status.STATUS_COMMITTED && status != Status.STATUS_ROLLEDBACK && status != Status.STATUS_UNKNOWN) {
049 ((TransactionImpl)transaction).registerInterposedSynchronization(new ConnectorSynchronization(ctx, transaction));
050 // Note: no synchronization is necessary here. Since a transaction can only be associated with a single
051 // thread at a time, it should not be possible for someone else to have snuck in and created a
052 // ConnectorTransactionContext for this transaction. We still protect against that with the putIfAbsent
053 // call below, and we simply have an extra transaction synchronization registered that won't do anything
054 DATA_INDEX.putIfAbsent(transaction, ctx);
055 }
056 // } catch (RollbackException e) {
057 // throw (IllegalStateException) new IllegalStateException("Transaction is already rolled back").initCause(e);
058 } catch (SystemException e) {
059 throw new RuntimeException("Unable to register ejb transaction synchronization callback", e);
060 }
061
062 }
063 return ctx;
064 }
065
066 public static TransactionCachingInterceptor.ManagedConnectionInfos get(Transaction transaction, ConnectionReleaser key) {
067 ConnectorTransactionContext ctx = get(transaction);
068 TransactionCachingInterceptor.ManagedConnectionInfos infos = ctx.getManagedConnectionInfo(key);
069 if (infos == null) {
070 infos = new TransactionCachingInterceptor.ManagedConnectionInfos();
071 ctx.setManagedConnectionInfo(key, infos);
072 }
073 return infos;
074 }
075
076 private static void remove(Transaction transaction) {
077 DATA_INDEX.remove(transaction);
078 }
079
080 private Map<ConnectionReleaser, TransactionCachingInterceptor.ManagedConnectionInfos> managedConnections;
081
082 private synchronized TransactionCachingInterceptor.ManagedConnectionInfos getManagedConnectionInfo(ConnectionReleaser key) {
083 if (managedConnections == null) {
084 return null;
085 }
086 return managedConnections.get(key);
087 }
088
089 private synchronized void setManagedConnectionInfo(ConnectionReleaser key, TransactionCachingInterceptor.ManagedConnectionInfos info) {
090 if (managedConnections == null) {
091 managedConnections = new HashMap<ConnectionReleaser, TransactionCachingInterceptor.ManagedConnectionInfos>();
092 }
093 managedConnections.put(key, info);
094 }
095
096 private static class ConnectorSynchronization implements Synchronization {
097 private final ConnectorTransactionContext ctx;
098 private final Transaction transaction;
099
100 public ConnectorSynchronization(ConnectorTransactionContext ctx, Transaction transaction) {
101 this.ctx = ctx;
102 this.transaction = transaction;
103 }
104
105 public void beforeCompletion() {
106 }
107
108 public void afterCompletion(int status) {
109 try {
110 synchronized (ctx) {
111 if (ctx.managedConnections != null) {
112 for (Map.Entry<ConnectionReleaser, TransactionCachingInterceptor.ManagedConnectionInfos> entry : ctx.managedConnections.entrySet()) {
113 ConnectionReleaser key = entry.getKey();
114 key.afterCompletion(entry.getValue());
115 }
116 //If BeanTransactionContext never reuses the same instance for sequential BMT, this
117 //clearing is unnecessary.
118 ctx.managedConnections.clear();
119 }
120 }
121 } finally {
122 remove(transaction);
123 }
124 }
125 }
126 }