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-21 00:11:34 +0200 (Mon, 21 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 }