Source code: org/apache/geronimo/transaction/manager/TransactionManagerImpl.java
1 /**
2 *
3 * Copyright 2003-2004 The Apache Software Foundation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * 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
18 package org.apache.geronimo.transaction.manager;
19
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.HashMap;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.Map;
26 import javax.transaction.HeuristicMixedException;
27 import javax.transaction.HeuristicRollbackException;
28 import javax.transaction.InvalidTransactionException;
29 import javax.transaction.NotSupportedException;
30 import javax.transaction.RollbackException;
31 import javax.transaction.Status;
32 import javax.transaction.SystemException;
33 import javax.transaction.Transaction;
34 import javax.transaction.xa.XAException;
35 import javax.transaction.xa.Xid;
36
37 import org.apache.commons.logging.Log;
38 import org.apache.commons.logging.LogFactory;
39 import org.apache.geronimo.gbean.GBeanInfo;
40 import org.apache.geronimo.gbean.GBeanInfoBuilder;
41 import org.apache.geronimo.gbean.ReferenceCollection;
42 import org.apache.geronimo.gbean.ReferenceCollectionEvent;
43 import org.apache.geronimo.gbean.ReferenceCollectionListener;
44 import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
45 import org.apache.geronimo.transaction.ExtendedTransactionManager;
46 import org.apache.geronimo.transaction.log.UnrecoverableLog;
47
48 /**
49 * Simple implementation of a transaction manager.
50 *
51 * @version $Rev: 156292 $ $Date: 2005-03-05 18:48:02 -0800 (Sat, 05 Mar 2005) $
52 */
53 public class TransactionManagerImpl implements ExtendedTransactionManager, XidImporter {
54 final TransactionLog transactionLog;
55 final XidFactory xidFactory;
56 private final int defaultTransactionTimeoutMilliseconds;
57 private final ThreadLocal transactionTimeoutMilliseconds = new ThreadLocal();
58 private final ThreadLocal threadTx = new ThreadLocal();
59 private static final Log recoveryLog = LogFactory.getLog("RecoveryController");
60 final Recovery recovery;
61 final ReferenceCollection resourceManagers;
62 private List recoveryErrors = new ArrayList();
63
64 /**
65 * TODO NOTE!!! this should be called in an unspecified transaction context, but we cannot enforce this restriction!
66 */
67 public TransactionManagerImpl(int defaultTransactionTimeoutSeconds, TransactionLog transactionLog, Collection resourceManagers) throws XAException {
68 if (defaultTransactionTimeoutSeconds <= 0) {
69 throw new IllegalArgumentException("defaultTransactionTimeoutSeconds must be positive: attempted value: " + defaultTransactionTimeoutSeconds);
70 }
71
72 this.defaultTransactionTimeoutMilliseconds = defaultTransactionTimeoutSeconds * 1000;
73 this.transactionLog = transactionLog == null ? new UnrecoverableLog() : transactionLog;
74 this.xidFactory = new XidFactoryImpl("WHAT DO WE CALL IT?".getBytes());
75 this.resourceManagers = (ReferenceCollection) resourceManagers;
76 recovery = new RecoveryImpl(this.transactionLog, this.xidFactory);
77
78 if (resourceManagers != null) {
79 recovery.recoverLog();
80 List copy = null;
81 synchronized (resourceManagers) {
82 copy = new ArrayList(resourceManagers);
83 this.resourceManagers.addReferenceCollectionListener(new ReferenceCollectionListener() {
84 public void memberAdded(ReferenceCollectionEvent event) {
85 ResourceManager resourceManager = (ResourceManager) event.getMember();
86 recoverResourceManager(resourceManager);
87 }
88
89 public void memberRemoved(ReferenceCollectionEvent event) {
90 }
91
92 });
93 }
94 for (Iterator iterator = copy.iterator(); iterator.hasNext();) {
95 ResourceManager resourceManager = (ResourceManager) iterator.next();
96 recoverResourceManager(resourceManager);
97 }
98 //what to do if there are recovery errors? or not all resource managers are online?
99 }
100 }
101
102 public Transaction getTransaction() throws SystemException {
103 return (Transaction) threadTx.get();
104 }
105
106 public void setTransactionTimeout(int seconds) throws SystemException {
107 if (seconds < 0) {
108 throw new SystemException("transaction timeout must be positive or 0 to reset to default");
109 }
110 if (seconds == 0) {
111 transactionTimeoutMilliseconds.set(null);
112 } else {
113 transactionTimeoutMilliseconds.set(new Long(seconds * 1000));
114 }
115 }
116
117 public int getStatus() throws SystemException {
118 Transaction tx = getTransaction();
119 return (tx != null) ? tx.getStatus() : Status.STATUS_NO_TRANSACTION;
120 }
121
122 public void begin() throws NotSupportedException, SystemException {
123 begin(getTransactionTimeoutMilliseconds(0L));
124 }
125
126 public Transaction begin(long transactionTimeoutMilliseconds) throws NotSupportedException, SystemException {
127 if (getStatus() != Status.STATUS_NO_TRANSACTION) {
128 throw new NotSupportedException("Nested Transactions are not supported");
129 }
130 TransactionImpl tx = new TransactionImpl(xidFactory, transactionLog, getTransactionTimeoutMilliseconds(transactionTimeoutMilliseconds));
131 // timeoutTimer.schedule(tx, getTransactionTimeoutMilliseconds(transactionTimeoutMilliseconds));
132 threadTx.set(tx);
133 // Todo: Verify if this is correct thing to do. Use default timeout for next transaction.
134 this.transactionTimeoutMilliseconds.set(null);
135 return tx;
136 }
137
138 public Transaction suspend() throws SystemException {
139 Transaction tx = getTransaction();
140 if (tx != null) {
141 }
142 threadTx.set(null);
143 return tx;
144 }
145
146 public void resume(Transaction tx) throws IllegalStateException, InvalidTransactionException, SystemException {
147 if (threadTx.get() != null) {
148 throw new IllegalStateException("Transaction already associated with current thread");
149 }
150 if (tx instanceof TransactionImpl == false) {
151 throw new InvalidTransactionException("Cannot resume foreign transaction: " + tx);
152 }
153 threadTx.set(tx);
154 }
155
156 public void setRollbackOnly() throws IllegalStateException, SystemException {
157 Transaction tx = getTransaction();
158 if (tx == null) {
159 throw new IllegalStateException("No transaction associated with current thread");
160 }
161 tx.setRollbackOnly();
162 }
163
164 public void commit() throws HeuristicMixedException, HeuristicRollbackException, IllegalStateException, RollbackException, SecurityException, SystemException {
165 Transaction tx = getTransaction();
166 if (tx == null) {
167 throw new IllegalStateException("No transaction associated with current thread");
168 }
169 try {
170 tx.commit();
171 } finally {
172 threadTx.set(null);
173 }
174 }
175
176 public void rollback() throws IllegalStateException, SecurityException, SystemException {
177 Transaction tx = getTransaction();
178 if (tx == null) {
179 throw new IllegalStateException("No transaction associated with current thread");
180 }
181 try {
182 tx.rollback();
183 } finally {
184 threadTx.set(null);
185 }
186 }
187
188 //XidImporter implementation
189 public Transaction importXid(Xid xid, long transactionTimeoutMilliseconds) throws XAException, SystemException {
190 if (transactionTimeoutMilliseconds < 0) {
191 throw new SystemException("transaction timeout must be positive or 0 to reset to default");
192 }
193 TransactionImpl tx = new TransactionImpl(xid, xidFactory, transactionLog, getTransactionTimeoutMilliseconds(transactionTimeoutMilliseconds));
194 return tx;
195 }
196
197 public void commit(Transaction tx, boolean onePhase) throws XAException {
198 if (onePhase) {
199 try {
200 tx.commit();
201 } catch (HeuristicMixedException e) {
202 throw (XAException) new XAException().initCause(e);
203 } catch (HeuristicRollbackException e) {
204 throw (XAException) new XAException().initCause(e);
205 } catch (RollbackException e) {
206 throw (XAException) new XAException().initCause(e);
207 } catch (SecurityException e) {
208 throw (XAException) new XAException().initCause(e);
209 } catch (SystemException e) {
210 throw (XAException) new XAException().initCause(e);
211 }
212 } else {
213 try {
214 ((TransactionImpl) tx).preparedCommit();
215 } catch (SystemException e) {
216 throw (XAException) new XAException().initCause(e);
217 }
218 }
219 }
220
221 public void forget(Transaction tx) throws XAException {
222 //TODO implement this!
223 }
224
225 public int prepare(Transaction tx) throws XAException {
226 try {
227 return ((TransactionImpl) tx).prepare();
228 } catch (SystemException e) {
229 throw (XAException) new XAException().initCause(e);
230 } catch (RollbackException e) {
231 throw (XAException) new XAException().initCause(e);
232 }
233 }
234
235 public void rollback(Transaction tx) throws XAException {
236 try {
237 tx.rollback();
238 } catch (IllegalStateException e) {
239 throw (XAException) new XAException().initCause(e);
240 } catch (SystemException e) {
241 throw (XAException) new XAException().initCause(e);
242 }
243 }
244
245 long getTransactionTimeoutMilliseconds(long transactionTimeoutMilliseconds) {
246 if (transactionTimeoutMilliseconds != 0) {
247 return transactionTimeoutMilliseconds;
248 }
249 Long timeout = (Long) this.transactionTimeoutMilliseconds.get();
250 if (timeout != null) {
251 return timeout.longValue();
252 }
253 return defaultTransactionTimeoutMilliseconds;
254 }
255
256 protected void recoverResourceManager(ResourceManager resourceManager) {
257 NamedXAResource namedXAResource = null;
258 try {
259 namedXAResource = resourceManager.getRecoveryXAResources();
260 } catch (SystemException e) {
261 recoveryLog.error(e);
262 recoveryErrors.add(e);
263 return;
264 }
265 if (namedXAResource != null) {
266 try {
267 recovery.recoverResourceManager(namedXAResource);
268 } catch (XAException e) {
269 recoveryLog.error(e);
270 recoveryErrors.add(e);
271 } finally {
272 resourceManager.returnResource(namedXAResource);
273 }
274 }
275 }
276
277
278 public Map getExternalXids() {
279 return new HashMap(recovery.getExternalXids());
280 }
281
282 public static final GBeanInfo GBEAN_INFO;
283
284 static {
285 GBeanInfoBuilder infoBuilder = new GBeanInfoBuilder(TransactionManagerImpl.class, NameFactory.JTA_RESOURCE);
286
287 infoBuilder.addAttribute("defaultTransactionTimeoutSeconds", int.class, true);
288 infoBuilder.addReference("TransactionLog", TransactionLog.class, NameFactory.JTA_RESOURCE);
289 infoBuilder.addReference("ResourceManagers", ResourceManager.class);//two kinds of things, so specify the type in each pattern.
290
291 infoBuilder.addInterface(ExtendedTransactionManager.class);
292 infoBuilder.addInterface(XidImporter.class);
293
294 infoBuilder.setConstructor(new String[]{"defaultTransactionTimeoutSeconds", "TransactionLog", "ResourceManagers"});
295
296 GBEAN_INFO = infoBuilder.getBeanInfo();
297 }
298
299
300 public static GBeanInfo getGBeanInfo() {
301 return GBEAN_INFO;
302 }
303 }