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.transaction.manager;
018    
019    import java.util.Collection;
020    import java.util.HashMap;
021    import java.util.Iterator;
022    import java.util.Map;
023    import javax.resource.spi.XATerminator;
024    import javax.transaction.InvalidTransactionException;
025    import javax.transaction.Status;
026    import javax.transaction.SystemException;
027    import javax.transaction.Transaction;
028    import javax.transaction.xa.XAException;
029    import javax.transaction.xa.XAResource;
030    import javax.transaction.xa.Xid;
031    
032    /**
033     * @version $Rev: 550546 $ $Date: 2007-06-25 12:52:11 -0400 (Mon, 25 Jun 2007) $
034     */
035    public class GeronimoTransactionManager extends TransactionManagerImpl implements XATerminator, XAWork {
036        private final Map importedTransactions = new HashMap();
037        private boolean isInRecovery = false;
038    
039        public GeronimoTransactionManager() throws XAException {
040        }
041    
042        public GeronimoTransactionManager(int defaultTransactionTimeoutSeconds) throws XAException {
043            super(defaultTransactionTimeoutSeconds);
044        }
045    
046        public GeronimoTransactionManager(int defaultTransactionTimeoutSeconds, TransactionLog transactionLog) throws XAException {
047            super(defaultTransactionTimeoutSeconds, transactionLog);
048        }
049    
050        public GeronimoTransactionManager(int defaultTransactionTimeoutSeconds, XidFactory xidFactory, TransactionLog transactionLog) throws XAException {
051            super(defaultTransactionTimeoutSeconds, xidFactory, transactionLog);
052        }
053    
054        /**
055         * @see javax.resource.spi.XATerminator#commit(javax.transaction.xa.Xid, boolean)
056         */
057        public void commit(Xid xid, boolean onePhase) throws XAException {
058            Transaction importedTransaction;
059            synchronized (importedTransactions) {
060                importedTransaction = (Transaction) importedTransactions.remove(xid);
061            }
062            if (importedTransaction == null) {
063                throw new XAException("No imported transaction for xid: " + xid);
064            }
065    
066            try {
067                int status = importedTransaction.getStatus();
068                assert status == Status.STATUS_ACTIVE || status == Status.STATUS_PREPARED: "invalid status: " + status;
069            } catch (SystemException e) {
070                throw (XAException)new XAException().initCause(e);
071            }
072            commit(importedTransaction, onePhase);
073        }
074    
075        /**
076         * @see javax.resource.spi.XATerminator#forget(javax.transaction.xa.Xid)
077         */
078        public void forget(Xid xid) throws XAException {
079            Transaction importedTransaction;
080            synchronized (importedTransactions) {
081                importedTransaction = (Transaction) importedTransactions.remove(xid);
082            }
083            if (importedTransaction == null) {
084                throw new XAException("No imported transaction for xid: " + xid);
085            }
086            //todo is there a correct status test here?
087    //        try {
088    //            int status = tx.getStatus();
089    //            assert status == Status.STATUS_ACTIVE || status == Status.STATUS_PREPARED;
090    //        } catch (SystemException e) {
091    //            throw new XAException();
092    //        }
093            forget(importedTransaction);
094        }
095    
096        /**
097         * @see javax.resource.spi.XATerminator#prepare(javax.transaction.xa.Xid)
098         */
099        public int prepare(Xid xid) throws XAException {
100            Transaction importedTransaction;
101            synchronized (importedTransactions) {
102                importedTransaction = (Transaction) importedTransactions.get(xid);
103            }
104            if (importedTransaction == null) {
105                throw new XAException("No imported transaction for xid: " + xid);
106            }
107            try {
108                int status = importedTransaction.getStatus();
109                assert status == Status.STATUS_ACTIVE;
110            } catch (SystemException e) {
111                throw (XAException)new XAException().initCause(e);
112            }
113            return prepare(importedTransaction);
114        }
115    
116        /**
117         * @see javax.resource.spi.XATerminator#recover(int)
118         */
119        public Xid[] recover(int flag) throws XAException {
120            if (!isInRecovery) {
121                if ((flag & XAResource.TMSTARTRSCAN) == 0) {
122                    throw new XAException(XAException.XAER_PROTO);
123                }
124                isInRecovery = true;
125            }
126            if ((flag & XAResource.TMENDRSCAN) != 0) {
127                isInRecovery = false;
128            }
129            //we always return all xids in first call.
130            //calling "startrscan" repeatedly starts at beginning of list again.
131            if ((flag & XAResource.TMSTARTRSCAN) != 0) {
132                Map recoveredXidMap = getExternalXids();
133                Xid[] recoveredXids = new Xid[recoveredXidMap.size()];
134                int i = 0;
135                synchronized (importedTransactions) {
136                    for (Iterator iterator = recoveredXidMap.entrySet().iterator(); iterator.hasNext();) {
137                        Map.Entry entry = (Map.Entry) iterator.next();
138                        Xid xid = (Xid) entry.getKey();
139                        recoveredXids[i++] = xid;
140                        Transaction transaction = (Transaction) entry.getValue();
141                        importedTransactions.put(xid, transaction);
142                    }
143                }
144                return recoveredXids;
145            } else {
146                return new Xid[0];
147            }
148        }
149    
150        /**
151         * @see javax.resource.spi.XATerminator#rollback(javax.transaction.xa.Xid)
152         */
153        public void rollback(Xid xid) throws XAException {
154            Transaction importedTransaction;
155            synchronized (importedTransactions) {
156                importedTransaction = (Transaction) importedTransactions.remove(xid);
157            }
158            if (importedTransaction == null) {
159                throw new XAException("No imported transaction for xid: " + xid);
160            }
161            try {
162                int status = importedTransaction.getStatus();
163                assert status == Status.STATUS_ACTIVE || status == Status.STATUS_PREPARED;
164            } catch (SystemException e) {
165                throw (XAException)new XAException().initCause(e);
166            }
167            rollback(importedTransaction);
168        }
169    
170    
171        //XAWork implementation
172        public void begin(Xid xid, long txTimeoutMillis) throws XAException, InvalidTransactionException, SystemException, ImportedTransactionActiveException {
173            Transaction importedTransaction;
174            synchronized (importedTransactions) {
175                importedTransaction = (Transaction) importedTransactions.get(xid);
176                if (importedTransaction == null) {
177                    // this does not associate tx with current thread.
178                    importedTransaction = importXid(xid, txTimeoutMillis);
179                    importedTransactions.put(xid, importedTransaction);
180                }
181                // associate the the imported transaction with the current thread
182                try {
183                    resume(importedTransaction);
184                } catch (InvalidTransactionException e) {
185                    // this occures if our transaciton is associated with another thread
186                    throw (ImportedTransactionActiveException)new ImportedTransactionActiveException(xid).initCause(e);
187                }
188            }
189        }
190    
191        public void end(Xid xid) throws XAException, SystemException {
192            synchronized (importedTransactions) {
193                Transaction importedTransaction = (Transaction) importedTransactions.get(xid);
194                if (importedTransaction == null) {
195                    throw new XAException("No imported transaction for xid: " + xid);
196                }
197                if (importedTransaction != getTransaction()) {
198                    throw new XAException("Imported transaction is not associated with the curren thread xid: " + xid);
199                }
200                suspend();
201            }
202        }
203    }