View Javadoc

1   /**
2    *  Licensed to the Apache Software Foundation (ASF) under one or more
3    *  contributor license agreements.  See the NOTICE file distributed with
4    *  this work for additional information regarding copyright ownership.
5    *  The ASF licenses this file to You under the Apache License, Version 2.0
6    *  (the "License"); you may not use this file except in compliance with
7    *  the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  package org.apache.geronimo.transaction.manager;
18  
19  import java.util.Collection;
20  import java.util.HashMap;
21  import java.util.Iterator;
22  import java.util.Map;
23  import javax.resource.spi.XATerminator;
24  import javax.transaction.InvalidTransactionException;
25  import javax.transaction.Status;
26  import javax.transaction.SystemException;
27  import javax.transaction.Transaction;
28  import javax.transaction.xa.XAException;
29  import javax.transaction.xa.XAResource;
30  import javax.transaction.xa.Xid;
31  
32  /**
33   * @version $Rev: 550546 $ $Date: 2007-06-25 12:52:11 -0400 (Mon, 25 Jun 2007) $
34   */
35  public class GeronimoTransactionManager extends TransactionManagerImpl implements XATerminator, XAWork {
36      private final Map importedTransactions = new HashMap();
37      private boolean isInRecovery = false;
38  
39      public GeronimoTransactionManager() throws XAException {
40      }
41  
42      public GeronimoTransactionManager(int defaultTransactionTimeoutSeconds) throws XAException {
43          super(defaultTransactionTimeoutSeconds);
44      }
45  
46      public GeronimoTransactionManager(int defaultTransactionTimeoutSeconds, TransactionLog transactionLog) throws XAException {
47          super(defaultTransactionTimeoutSeconds, transactionLog);
48      }
49  
50      public GeronimoTransactionManager(int defaultTransactionTimeoutSeconds, XidFactory xidFactory, TransactionLog transactionLog) throws XAException {
51          super(defaultTransactionTimeoutSeconds, xidFactory, transactionLog);
52      }
53  
54      /**
55       * @see javax.resource.spi.XATerminator#commit(javax.transaction.xa.Xid, boolean)
56       */
57      public void commit(Xid xid, boolean onePhase) throws XAException {
58          Transaction importedTransaction;
59          synchronized (importedTransactions) {
60              importedTransaction = (Transaction) importedTransactions.remove(xid);
61          }
62          if (importedTransaction == null) {
63              throw new XAException("No imported transaction for xid: " + xid);
64          }
65  
66          try {
67              int status = importedTransaction.getStatus();
68              assert status == Status.STATUS_ACTIVE || status == Status.STATUS_PREPARED: "invalid status: " + status;
69          } catch (SystemException e) {
70              throw (XAException)new XAException().initCause(e);
71          }
72          commit(importedTransaction, onePhase);
73      }
74  
75      /**
76       * @see javax.resource.spi.XATerminator#forget(javax.transaction.xa.Xid)
77       */
78      public void forget(Xid xid) throws XAException {
79          Transaction importedTransaction;
80          synchronized (importedTransactions) {
81              importedTransaction = (Transaction) importedTransactions.remove(xid);
82          }
83          if (importedTransaction == null) {
84              throw new XAException("No imported transaction for xid: " + xid);
85          }
86          //todo is there a correct status test here?
87  //        try {
88  //            int status = tx.getStatus();
89  //            assert status == Status.STATUS_ACTIVE || status == Status.STATUS_PREPARED;
90  //        } catch (SystemException e) {
91  //            throw new XAException();
92  //        }
93          forget(importedTransaction);
94      }
95  
96      /**
97       * @see javax.resource.spi.XATerminator#prepare(javax.transaction.xa.Xid)
98       */
99      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 }