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