001 /**
002 *
003 * Copyright 2006 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * 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: 437254 $ $Date: 2006-08-26 17:07:55 -0700 (Sat, 26 Aug 2006) $
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, Collection resourceManagers) throws XAException {
051 super(defaultTransactionTimeoutSeconds, xidFactory, transactionLog, resourceManagers);
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 new XAException();
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 new XAException();
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 new XAException();
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 new ImportedTransactionActiveException(xid);
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 }