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 }