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