1
2
3
4
5
6
7
8
9
10
11 package org.mule.util.xa;
12
13 import org.mule.config.i18n.CoreMessages;
14
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.Iterator;
19
20 import javax.transaction.Status;
21
22 import org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
24
25
26
27
28
29
30 public abstract class AbstractResourceManager
31 {
32
33
34
35
36 public static final int SHUTDOWN_MODE_NORMAL = 0;
37
38
39
40
41 public static final int SHUTDOWN_MODE_ROLLBACK = 1;
42
43
44
45
46 public static final int SHUTDOWN_MODE_KILL = 2;
47
48 protected static final int OPERATION_MODE_STOPPED = 0;
49 protected static final int OPERATION_MODE_STOPPING = 1;
50 protected static final int OPERATION_MODE_STARTED = 2;
51 protected static final int OPERATION_MODE_STARTING = 3;
52 protected static final int OPERATION_MODE_RECOVERING = 4;
53
54 protected static final int DEFAULT_TIMEOUT_MSECS = 5000;
55 protected static final int DEFAULT_COMMIT_TIMEOUT_FACTOR = 2;
56
57 protected Collection globalTransactions = Collections.synchronizedCollection(new ArrayList());
58 protected int operationMode = OPERATION_MODE_STOPPED;
59 protected long defaultTimeout = DEFAULT_TIMEOUT_MSECS;
60
61 protected Log logger = LogFactory.getLog(getClass());
62
63 protected boolean dirty = false;
64
65 public synchronized void start() throws ResourceManagerSystemException
66 {
67 logger.info("Starting ResourceManager");
68 operationMode = OPERATION_MODE_STARTING;
69
70 doStart();
71 recover();
72
73 operationMode = OPERATION_MODE_STARTED;
74 if (dirty)
75 {
76 logger
77 .warn("Started ResourceManager, but in dirty mode only (Recovery of pending transactions failed)");
78 }
79 else
80 {
81 logger.info("Started ResourceManager");
82 }
83 }
84
85 protected void doStart() throws ResourceManagerSystemException
86 {
87
88 }
89
90 protected void recover() throws ResourceManagerSystemException
91 {
92
93 }
94
95 public synchronized void stop() throws ResourceManagerSystemException
96 {
97 stop(SHUTDOWN_MODE_NORMAL);
98 }
99
100 public synchronized boolean stop(int mode) throws ResourceManagerSystemException
101 {
102 return stop(mode, getDefaultTransactionTimeout() * DEFAULT_COMMIT_TIMEOUT_FACTOR);
103 }
104
105 public synchronized boolean stop(int mode, long timeOut) throws ResourceManagerSystemException
106 {
107 logger.info("Stopping ResourceManager");
108 operationMode = OPERATION_MODE_STOPPING;
109
110
111 boolean success = shutdown(mode, timeOut);
112
113
114 if (success)
115 {
116 operationMode = OPERATION_MODE_STOPPED;
117 logger.info("Stopped ResourceManager");
118 }
119 else
120 {
121 logger.warn("Failed to stop ResourceManager");
122 }
123
124 return success;
125 }
126
127 protected boolean shutdown(int mode, long timeoutMSecs)
128 {
129 switch (mode)
130 {
131 case SHUTDOWN_MODE_NORMAL :
132 return waitForAllTxToStop(timeoutMSecs);
133 case SHUTDOWN_MODE_ROLLBACK :
134 throw new UnsupportedOperationException();
135
136 case SHUTDOWN_MODE_KILL :
137 return true;
138 default :
139 return false;
140 }
141 }
142
143
144
145
146 public long getDefaultTransactionTimeout()
147 {
148 return defaultTimeout;
149 }
150
151
152
153
154
155
156 public void setDefaultTransactionTimeout(long timeout)
157 {
158 defaultTimeout = timeout;
159 }
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176 public AbstractTransactionContext startTransaction(Object session) throws ResourceManagerException
177 {
178 return createTransactionContext(session);
179 }
180
181 public void beginTransaction(AbstractTransactionContext context) throws ResourceManagerException
182 {
183
184 assureStarted();
185
186 synchronized (context)
187 {
188 if (logger.isDebugEnabled())
189 {
190 logger.debug("Beginning transaction " + context);
191 }
192 doBegin(context);
193 context.status = Status.STATUS_ACTIVE;
194 if (logger.isDebugEnabled())
195 {
196 logger.debug("Began transaction " + context);
197 }
198 }
199 globalTransactions.add(context);
200 }
201
202 public int prepareTransaction(AbstractTransactionContext context) throws ResourceManagerException
203 {
204 assureReady();
205 synchronized (context)
206 {
207 if (logger.isDebugEnabled())
208 {
209 logger.debug("Preparing transaction " + context);
210 }
211 context.status = Status.STATUS_PREPARING;
212 int status = doPrepare(context);
213 context.status = Status.STATUS_PREPARED;
214 if (logger.isDebugEnabled())
215 {
216 logger.debug("Prepared transaction " + context);
217 }
218 return status;
219 }
220 }
221
222 public void rollbackTransaction(AbstractTransactionContext context) throws ResourceManagerException
223 {
224 assureReady();
225 synchronized (context)
226 {
227 if (logger.isDebugEnabled())
228 {
229 logger.debug("Rolling back transaction " + context);
230 }
231 try
232 {
233 context.status = Status.STATUS_ROLLING_BACK;
234 doRollback(context);
235 context.status = Status.STATUS_ROLLEDBACK;
236 }
237 catch (Error e)
238 {
239 setDirty(context, e);
240 throw e;
241 }
242 catch (RuntimeException e)
243 {
244 setDirty(context, e);
245 throw e;
246 }
247 catch (ResourceManagerSystemException e)
248 {
249 setDirty(context, e);
250 throw e;
251 }
252 finally
253 {
254 globalTransactions.remove(context);
255 context.finalCleanUp();
256
257 context.notifyFinish();
258 }
259 if (logger.isDebugEnabled())
260 {
261 logger.debug("Rolled back transaction " + context);
262 }
263 }
264 }
265
266 public void setTransactionRollbackOnly(AbstractTransactionContext context)
267 throws ResourceManagerException
268 {
269 context.status = Status.STATUS_MARKED_ROLLBACK;
270 }
271
272 public void commitTransaction(AbstractTransactionContext context) throws ResourceManagerException
273 {
274 assureReady();
275 if (context.status == Status.STATUS_MARKED_ROLLBACK)
276 {
277 throw new ResourceManagerException(CoreMessages.transactionMarkedForRollback());
278 }
279 synchronized (context)
280 {
281 if (logger.isDebugEnabled())
282 {
283 logger.debug("Committing transaction " + context);
284 }
285 try
286 {
287 context.status = Status.STATUS_COMMITTING;
288 doCommit(context);
289 context.status = Status.STATUS_COMMITTED;
290 }
291 catch (Error e)
292 {
293 setDirty(context, e);
294 throw e;
295 }
296 catch (RuntimeException e)
297 {
298 setDirty(context, e);
299 throw e;
300 }
301 catch (ResourceManagerSystemException e)
302 {
303 setDirty(context, e);
304 throw e;
305 }
306 catch (ResourceManagerException e)
307 {
308 logger.warn("Could not commit tx " + context + ", rolling back instead", e);
309 doRollback(context);
310 }
311 finally
312 {
313 globalTransactions.remove(context);
314 context.finalCleanUp();
315
316 context.notifyFinish();
317 }
318 if (logger.isDebugEnabled())
319 {
320 logger.debug("Committed transaction " + context);
321 }
322 }
323 }
324
325 protected abstract AbstractTransactionContext createTransactionContext(Object session);
326
327 protected abstract void doBegin(AbstractTransactionContext context);
328
329 protected abstract int doPrepare(AbstractTransactionContext context);
330
331 protected abstract void doCommit(AbstractTransactionContext context) throws ResourceManagerException;
332
333 protected abstract void doRollback(AbstractTransactionContext context) throws ResourceManagerException;
334
335
336
337
338
339 protected boolean waitForAllTxToStop(long timeoutMSecs)
340 {
341 long startTime = System.currentTimeMillis();
342
343
344
345
346
347
348
349
350
351 Collection transactionsToStop;
352 synchronized (globalTransactions)
353 {
354 transactionsToStop = new ArrayList(globalTransactions);
355 }
356 for (Iterator it = transactionsToStop.iterator(); it.hasNext();)
357 {
358 long remainingTimeout = startTime - System.currentTimeMillis() + timeoutMSecs;
359
360 if (remainingTimeout <= 0)
361 {
362 return false;
363 }
364
365 AbstractTransactionContext context = (AbstractTransactionContext) it.next();
366 synchronized (context)
367 {
368 if (!context.finished)
369 {
370 logger.info("Waiting for tx " + context + " to finish for " + remainingTimeout
371 + " milli seconds");
372 }
373 while (!context.finished && remainingTimeout > 0)
374 {
375 try
376 {
377 context.wait(remainingTimeout);
378 }
379 catch (InterruptedException e)
380 {
381 return false;
382 }
383 remainingTimeout = startTime - System.currentTimeMillis() + timeoutMSecs;
384 }
385 if (context.finished)
386 {
387 logger.info("Tx " + context + " finished");
388 }
389 else
390 {
391 logger.warn("Tx " + context + " failed to finish in given time");
392 }
393 }
394 }
395
396 return (globalTransactions.size() == 0);
397 }
398
399
400
401
402
403
404
405
406 protected void setDirty(AbstractTransactionContext context, Throwable t)
407 {
408 logger.error("Fatal error during critical commit/rollback of transaction " + context
409 + ", setting resource manager to dirty.", t);
410 dirty = true;
411 }
412
413
414
415
416
417
418 protected void assureStarted() throws ResourceManagerSystemException
419 {
420 if (operationMode != OPERATION_MODE_STARTED)
421 {
422 throw new ResourceManagerSystemException(CoreMessages.resourceManagerNotStarted());
423 }
424
425
426 if (dirty)
427 {
428 throw new ResourceManagerSystemException(CoreMessages.resourceManagerDirty());
429 }
430 }
431
432
433
434
435
436
437
438 protected void assureReady() throws ResourceManagerSystemException
439 {
440 if (operationMode != OPERATION_MODE_STARTED && operationMode != OPERATION_MODE_STOPPING)
441 {
442 throw new ResourceManagerSystemException(CoreMessages.resourceManagerNotReady());
443 }
444
445
446 if (dirty)
447 {
448 throw new ResourceManagerSystemException(CoreMessages.resourceManagerDirty());
449 }
450 }
451
452 }