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    }