KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > tm > recovery > RecoveryManager


1 /*
2   * JBoss, Home of Professional Open Source
3   * Copyright 2005, JBoss Inc., and individual contributors as indicated
4   * by the @authors tag. See the copyright.txt in the distribution for a
5   * full listing of individual contributors.
6   *
7   * This is free software; you can redistribute it and/or modify it
8   * under the terms of the GNU Lesser General Public License as
9   * published by the Free Software Foundation; either version 2.1 of
10   * the License, or (at your option) any later version.
11   *
12   * This software is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   * Lesser General Public License for more details.
16   *
17   * You should have received a copy of the GNU Lesser General Public
18   * License along with this software; if not, write to the Free
19   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21   */

22 package org.jboss.tm.recovery;
23
24 import java.util.ArrayList JavaDoc;
25 import java.util.Arrays JavaDoc;
26 import java.util.HashMap JavaDoc;
27 import java.util.HashSet JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Map JavaDoc;
31 import java.util.Set JavaDoc;
32
33 import javax.transaction.Status JavaDoc;
34 import javax.transaction.xa.XAException JavaDoc;
35 import javax.transaction.xa.XAResource JavaDoc;
36 import javax.transaction.xa.Xid JavaDoc;
37
38 import org.jboss.logging.Logger;
39 import org.jboss.tm.TxManager;
40 import org.jboss.tm.TxUtils;
41 import org.jboss.tm.XidFactoryBase;
42
43 /**
44  * A <code>RecoveryManager</code> object manages the crash recovery process.
45  * At recovery time, the <code>RecoveryManagerService</code> creates a
46  * <code>RecoveryManager</code> instance that interacts with the
47  * <code>RecoveryLogger</code> to read the transaction logs and identify
48  * the transactions that were active at the time of the crash. The
49  * <code>RecoveryManager</code> knows how to perform crash recovery for
50  * transactions involving <code>XAResource</code>s that correspond to
51  * <code>Recoverable</code> objects known in advance.
52  *
53  * @author <a HREF="mailto:bill@jboss.org">Bill Burke</a>
54  * @author <a HREF="mailto:reverbel@ime.usp.br">Francisco Reverbel</a>
55  * @version $Revision: 37459 $
56  */

57 public class RecoveryManager
58 {
59    /**
60     * Class <code>Logger</code> for trace messages.
61     */

62    private static Logger log = Logger.getLogger(RecoveryManager.class.getName());
63    
64    /**
65     * A <code>XAResourceAccess</code> implementation that calls
66     * <code>cleanUpResource</code> on a <code>Recoverable</code> instance
67     * if the <code>Recoverable</code> will not be used anymore. The
68     * last <code>release</code> call on an <code>XAResourceAccess</code>
69     * instance closes the underlying <code>XAConnection</code>.
70     */

71    public static class XAResourceAccessImpl
72       implements XAResourceAccess
73    {
74       private Recoverable recoverable;
75       private int refCount;
76       
77       // not public -- called only by the XAResXids constructor (see below)
78
XAResourceAccessImpl(Recoverable recoverable)
79       {
80          this.recoverable = recoverable;
81          refCount = 1;
82       }
83       
84       // not public -- called only by the XAWork constructor
85
synchronized XAResourceAccess duplicate()
86       {
87          refCount++;
88          return this;
89       }
90       
91       public synchronized void release()
92       {
93          if (refCount <= 0)
94             log.warn("release called, but refCount=" + refCount +
95                      ", this=" + this, new Throwable JavaDoc("[Stack trace]"));
96          
97          if (--refCount == 0)
98             recoverable.cleanupResource();
99       }
100       
101    }
102
103    /**
104     * "Struct class" that groups together a <code>Recoverable</code> object,
105     * the <code>XAResource</code> represented by the <code>Recoverable</code>,
106     * and a set of <code>Xids</code> that still need to be processed.
107     */

108    private static class XAResourceXids
109    {
110       public Recoverable recoverable;
111       public XAResource JavaDoc resource;
112       public XAResourceAccessImpl resourceAccess;
113       public Set JavaDoc xids;
114       
115       XAResourceXids(Recoverable recoverable)
116       {
117          this.recoverable = recoverable;
118          this.resource = recoverable.getResource();
119          // Note that the XAResourceAccess constructor is *not* called if a
120
// throwable occurs in recoverable.getResource().
121
this.resourceAccess = new XAResourceAccessImpl(recoverable);
122          this.xids = new HashSet JavaDoc();
123       }
124    }
125    
126    /**
127     * This implementation of <code>TxCompletionHandler</code> handles
128     * transaction completion for transactions recreated by the recovery
129     * process.
130     */

131    private static class CompletionHandler
132       implements TxCompletionHandler
133    {
134       /**
135        * The log reader associated with the transaction whose completion
136        * is handled by this <code>CompletionHandler</code>.
137        */

138       private RecoveryLogReader reader;
139       
140       /**
141        * Number of pending transactions associated with the log reader.
142        */

143       private int pendingTransactions;
144
145       /**
146        * Creates a new <code>CompletionHandler</code>.
147        *
148        * @param reader log reader associated with the transaction whose
149        * completion will be handled by the new
150        * <code>CompletionHandler</code>
151        * @param pendingTransactions number of pending transactions
152        * associated with the log reader above.
153        */

154       CompletionHandler(RecoveryLogReader reader, int pendingTransactions)
155       {
156          this.reader = reader;
157          this.pendingTransactions = pendingTransactions;
158       }
159
160       /**
161        * Signals the end of the two-phase commit protocol for a committed
162        * transaction. This method should be invoked when the second phase of the
163        * two-phase commit protocol completes successfully.
164        *
165        * @param localTransactionId the local id of the completed transaction.
166        */

167       public void handleTxCompletion(long localTransactionId)
168       {
169          if (--pendingTransactions == 0)
170             reader.finishRecovery();
171       }
172    }
173    
174    /**
175     * The Xid factory used by this <code>RecoveryManager</code>.
176     */

177    private XidFactoryBase xidFactory;
178
179    /**
180     * The transaction manager used by this <code>RecoveryManager</code>.
181     */

182    private TxManager txManager;
183
184    /**
185     * The recovery logger used by this <code>RecoveryManager</code>.
186     */

187    private RecoveryLogger recoveryLogger;
188
189    /**
190     * Constructs a new <code>RecoveryManager</code>.
191     *
192     * @param xidFactory the Xid factory that will be used by the new
193     * <code>RecoveryManager</code>
194     * @param txManager the transaction manager that will be used by the new
195     * <code>RecoveryManager</code>
196     * @param recoveryLogger the recovery logger that will be used by the new
197     * <code>RecoveryManager</code>
198     */

199    public RecoveryManager(XidFactoryBase xidFactory,
200                           TxManager txManager,
201                           RecoveryLogger recoveryLogger)
202    {
203       this.xidFactory = xidFactory;
204       this.txManager = txManager;
205       this.recoveryLogger = recoveryLogger;
206    }
207    
208    /**
209     * Performs crash recovery. This method reads the transaction logs,
210     * identifies the transactions that were active at the time of the crash,
211     * and performs crash recovery actions for each such transaction, which
212     * may involve any subset of the <code>XAResource</code>s associated with
213     * a given list of <code>Recoverable</code> objects
214     *
215     * @param rcoverables a list of <code>Recoverable</code> objects whose
216     * <code>XAResource</code>s may be involved in active
217     * transactions.
218     */

219    public void recover(ArrayList JavaDoc recoverables)
220    {
221       Map JavaDoc heuristicallyCompletedTransactions = new HashMap JavaDoc();
222
223       HeuristicStatusLogReader[] heurStatusLogReaders =
224          recoveryLogger.getHeuristicStatusLogs();
225             
226       if (heurStatusLogReaders != null)
227       {
228          // Get the heuristically completed transactions
229
// from the existing heuristic status log files
230
for (int i = 0; i < heurStatusLogReaders.length; i++)
231             heurStatusLogReaders[i].recover(heuristicallyCompletedTransactions);
232
233          // Save the heuristic status of those transactions
234
// to the current heuristic status log file
235
Iterator JavaDoc heurIt =
236             heuristicallyCompletedTransactions.keySet().iterator();
237          while (heurIt.hasNext())
238          {
239             Long JavaDoc localId = (Long JavaDoc) heurIt.next();
240             LogRecord.HeurData heurData =
241                (LogRecord.HeurData) heuristicallyCompletedTransactions.get(
242                                                                       localId);
243             recoveryLogger.saveHeuristicStatus(
244                                       heurData.localTransactionId,
245                                       heurData.foreignTx,
246                                       heurData.formatId,
247                                       heurData.globalTransactionId,
248                                       heurData.inboundBranchQualifier,
249                                       heurData.transactionStatus,
250                                       heurData.heuristicStatusCode,
251                                       heurData.locallyDetectedHeuristicHazard,
252                                       heurData.xaResourceHeuristics,
253                                       heurData.remoteResourceHeuristics);
254          }
255
256          // Get rid of the existing heuristic status log files
257
for (int i = 0; i < heurStatusLogReaders.length; i++)
258             heurStatusLogReaders[i].finishRecovery();
259       }
260       
261       RecoveryLogReader[] readers = recoveryLogger.getRecoveryLogs();
262       if (readers == null || readers.length == 0)
263          return;
264
265       // Obtain the set of branch qualifiers generated by this TM.
266
Set JavaDoc readerBranchQualifiers = new HashSet JavaDoc();
267       for (int i = 0; i < readers.length; i++)
268       {
269          String JavaDoc branchQualifier = null;
270          try
271          {
272             branchQualifier = readers[i].getBranchQualifier();
273          }
274          catch (Exception JavaDoc e)
275          {
276             log.error("logfile corrupted: "
277                       + readers[i].getLogFileName(), e);
278          }
279          readerBranchQualifiers.add(branchQualifier);
280          log.info("will recover transactions with branch qualifier " +
281                   branchQualifier +
282                   " (logFile: " + readers[i].getLogFileName() + ")");
283       }
284
285       Map JavaDoc toRecoverMap = new HashMap JavaDoc();
286       try
287       {
288          // Populate a map whose keys are Recoverable ids and whose values are
289
// XAResourceXids objects, each of which contains the set of Xids of
290
// the active XA transaction branches that involve a given XAResource
291
// and that require recovery actions.
292
for (int i = 0; i < recoverables.size(); i++)
293          {
294             Recoverable rec = (Recoverable) recoverables.get(i);
295             XAResourceXids xaResXids;
296             try
297             {
298                xaResXids = new XAResourceXids(rec);
299             }
300             catch (Throwable JavaDoc t)
301             {
302                throw new RuntimeException JavaDoc("Unable to getResource: "
303                                           + rec.getId()
304                                           + " aborting recovery.", t);
305             }
306             toRecoverMap.put(rec.getId(), xaResXids);
307             try
308             {
309                xaResXids.xids.addAll(pruneXidList(rec.scan(),
310                                                   readerBranchQualifiers,
311                                                   rec.getId()));
312             }
313             catch (XAException JavaDoc e)
314             {
315                // TODO also, what to do if this fails?
316
// Should we go on and still try to recover other resources?
317
throw new RuntimeException JavaDoc("Unable to scan: " + rec.getId(), e);
318             }
319          }
320          
321          // Perform the recovery actions.
322
recover(readers, heuristicallyCompletedTransactions, toRecoverMap);
323       }
324       finally
325       {
326          cleanupRecoverables(toRecoverMap.values().iterator());
327       }
328    }
329
330    /**
331     * Performs crash recovery using a given array of log readers and a map
332     * with information on the active XA transaction branches for which
333     * recovery actions must be taken.
334     *
335     * @param readers an array of transaction log readers
336     * @param heuristicallyCompletedTransactions
337     * @param toRecoverMap a map whose keys are <code>Recoverable</code> ids
338     * and whose values are <code>XAResourceXids<code>
339     * objects, each of which contains the set of
340     * <code>Xids</code> for the active XA transaction
341     * branches that involve a given <code>XAResource<code>
342     * and that require recovery actions.
343     */

344    private void recover(RecoveryLogReader[] readers,
345                         Map JavaDoc heuristicallyCompletedTransactions,
346                         Map JavaDoc toRecoverMap)
347    {
348       boolean presumeRollback = true;
349       CorruptedLogRecordException corruptedLogRecordException = null;
350       
351       // Recreate the pending committed and in-doubt transactions, including the
352
// ones that are pending just because they were heuristically completed.
353
for (int i = 0; i < readers.length; i++)
354       {
355          log.info("recovering log file " + readers[i].getLogFileName());
356          List JavaDoc committedSingleTmTransactions = new ArrayList JavaDoc();
357          List JavaDoc committedMultiTmTransactions = new ArrayList JavaDoc();
358          List JavaDoc inDoubtTransactions = new ArrayList JavaDoc();
359          List JavaDoc inDoubtJcaTransactions = new ArrayList JavaDoc();
360
361          try
362          {
363             readers[i].recover(committedSingleTmTransactions,
364                                committedMultiTmTransactions,
365                                inDoubtTransactions,
366                                inDoubtJcaTransactions);
367          }
368          catch (CorruptedLogRecordException e)
369          {
370             log.trace("reader threw CorruptedLogRecordException with " +
371                       "disablePresumedRollback=" + e.disablePresumedRollback);
372             corruptedLogRecordException = e;
373             if (corruptedLogRecordException.disablePresumedRollback)
374                presumeRollback = false;
375          }
376          
377          int pendingTransactions = committedSingleTmTransactions.size() +
378                                     committedMultiTmTransactions.size() +
379                                     inDoubtTransactions.size() +
380                                     inDoubtJcaTransactions.size();
381          
382          if (pendingTransactions == 0)
383             readers[i].finishRecovery();
384          else
385          {
386             CompletionHandler completionHandler =
387                new CompletionHandler(readers[i], pendingTransactions);
388         
389             resumePendingTransactions(heuristicallyCompletedTransactions,
390                                       committedSingleTmTransactions,
391                                       committedMultiTmTransactions,
392                                       inDoubtTransactions,
393                                       inDoubtJcaTransactions,
394                                       toRecoverMap,
395                                       completionHandler);
396          }
397       }
398       
399       // Recreate the remaining heuristically completed transactions
400
// (these transactions are in the rolledback state).
401
Iterator JavaDoc heurIt = heuristicallyCompletedTransactions.keySet().iterator();
402       while (heurIt.hasNext())
403       {
404          Long JavaDoc localId = (Long JavaDoc) heurIt.next();
405          LogRecord.HeurData heurData =
406             (LogRecord.HeurData) heuristicallyCompletedTransactions.get(localId);
407          heurIt.remove(); // heuristicallyCompletedTransactions.remove(localId)
408
byte[] globalId = heurData.globalTransactionId;
409          List JavaDoc xaResourcesWithHeuristics = getXAWork(globalId, toRecoverMap);
410          txManager.recreateTransaction(heurData,
411                                        xaResourcesWithHeuristics,
412                                        null /* no completion handler */);
413       }
414       
415       if (!presumeRollback)
416       {
417          log.info("PRESUMED ROLLBACK IS DISABLED DUE TO LOG FILE CORRUPTION.");
418       }
419       
420       // Rollback the transactions that remained in the toRecoverMap.
421
// (This is presumed rollback.)
422
Iterator JavaDoc rit = toRecoverMap.values().iterator();
423       while (rit.hasNext())
424       {
425          XAResourceXids xaResXids = (XAResourceXids) rit.next();
426          Iterator JavaDoc it = xaResXids.xids.iterator();
427          while (it.hasNext())
428          {
429             Xid JavaDoc xid = (Xid JavaDoc) it.next();
430             if (!presumeRollback)
431             {
432                log.info("WOULD ROLLBACK " + xidFactory.toString(xid) +
433                         " ON RECOVERABLE XAResource " +
434                         xaResXids.recoverable.getId() +
435                         ", BUT PRESUMED ROLLBACK IS DISABLED");
436             }
437             else
438             {
439                try
440                {
441                   xaResXids.resource.rollback(xid);
442                   log.info("rolledback " + xidFactory.toString(xid) +
443                            " on recoverable XAResource " +
444                            xaResXids.recoverable.getId());
445                }
446                catch (XAException JavaDoc e)
447                {
448                   log.warn("XAException in recover (when rolling back " +
449                            "res " + xaResXids.recoverable.getId() + ", xid=" +
450                            xidFactory.toString(xid) + "): errorCode="
451                            + TxUtils.getXAErrorCodeAsString(e.errorCode), e);
452                   // TODO: check the errorCode and retry the rollback if
453
// XAER_RMFAIL or XAER_RMFAIL.
454
}
455             }
456          }
457       }
458       
459       if (corruptedLogRecordException != null)
460          throw corruptedLogRecordException;
461    }
462
463    /**
464     * Resumes the pending transactions specified by the <code>List</code>
465     * arguments.
466     *
467     * @param committedSingleTmTransactions a list of
468     * <code>LogRecord.Data</code> objects
469     * for the committed single-TM
470     * transactions
471     * @param committedMultiTmTransactions a list of <code>LogRecord.Data</code>
472     * objects for the committed multi-TM
473     * transactions
474     * @param inDoubtTransactions a list of <code>LogRecord.Data</code> objects
475     * for the in-doubt transactions that entered
476     * this virtual machine in transaction contexts
477     * propagated along with remote method invocations
478     * @param inDoubtJcaTransactions a list of <code>LogRecord.Data</code> objects
479     * for the in-doubt transactions that entered
480     * this virtual machine through JCA transaction
481     * inflow
482     * @param toRecoverMap a map whose keys are <code>Recoverable</code> ids and
483     * whose values are <code>XAResourceXids<code> objects,
484     * each of which contains the set of <code>Xids</code>
485     * for all active XA transaction branches that involve
486     * a given <code>XAResource<code> and that require
487     * recovery actions
488     * @param completionHandler a <code>TxCompletionHandler</code> that handles
489     * the completion of all transactions specified by
490     * the preceding arguments.
491     */

492    private void resumePendingTransactions(Map JavaDoc heuristicallyCompletedTransactions,
493                                           List JavaDoc committedSingleTmTransactions,
494                                           List JavaDoc committedMultiTmTransactions,
495                                           List JavaDoc inDoubtTransactions,
496                                           List JavaDoc inDoubtJcaTransactions,
497                                           Map JavaDoc toRecoverMap,
498                                           TxCompletionHandler completionHandler)
499    {
500       Iterator JavaDoc it;
501       LogRecord.Data data;
502       LogRecord.HeurData heurData;
503       
504       it = committedSingleTmTransactions.iterator();
505       while (it.hasNext())
506       {
507          data = (LogRecord.Data) it.next();
508          byte[] globalId = data.globalTransactionId;
509          Long JavaDoc localId = new Long JavaDoc(data.localTransactionId);
510          heurData =
511             (LogRecord.HeurData)heuristicallyCompletedTransactions.get(localId);
512
513          if (heurData != null)
514             heuristicallyCompletedTransactions.remove(localId);
515          
516          if (heurData != null && !heurData.locallyDetectedHeuristicHazard)
517          {
518             List JavaDoc xaResourcesWithHeuristics = getXAWork(globalId, toRecoverMap);
519             txManager.recreateTransaction(heurData,
520                                           xaResourcesWithHeuristics,
521                                           completionHandler);
522          }
523          else
524          {
525             // Either heurData is null or it has a
526
// locally-detected heuristic hazard.
527

528             List JavaDoc pendingXAWorkList = commitXAWork(globalId, toRecoverMap);
529             if (pendingXAWorkList.isEmpty())
530             {
531                if (heurData == null)
532                {
533                   completionHandler.handleTxCompletion(data.localTransactionId);
534                }
535                else
536                {
537                   // just the locally-detected heuristic hazard
538
txManager.recreateTransaction(heurData,
539                                                 pendingXAWorkList,
540                                                 completionHandler);
541                }
542             }
543             else
544             {
545                txManager.recreateTransaction(data.localTransactionId,
546                                              pendingXAWorkList,
547                                              completionHandler,
548                                              heurData);
549             }
550          }
551       }
552
553       it = committedMultiTmTransactions.iterator();
554       while (it.hasNext())
555       {
556          data = (LogRecord.Data) it.next();
557          byte[] globalId = data.globalTransactionId;
558          Long JavaDoc localId = new Long JavaDoc(data.localTransactionId);
559          heurData =
560             (LogRecord.HeurData)heuristicallyCompletedTransactions.get(localId);
561          
562          if (heurData != null)
563             heuristicallyCompletedTransactions.remove(localId);
564          
565          if (heurData != null && !heurData.locallyDetectedHeuristicHazard)
566          {
567             List JavaDoc xaResourcesWithHeuristics = getXAWork(globalId, toRecoverMap);
568             txManager.recreateTransaction(heurData,
569                                           xaResourcesWithHeuristics,
570                                           completionHandler);
571          }
572          else
573          {
574             // Either heurData is null or it has a
575
// locally-detected heuristic hazard.
576

577             List JavaDoc pendingXAWorkList = commitXAWork(globalId, toRecoverMap);
578             txManager.recreateTransaction(data.localTransactionId,
579                                           pendingXAWorkList,
580                                           data.resources,
581                                           completionHandler,
582                                           heurData);
583          }
584       }
585       
586       it = inDoubtTransactions.iterator();
587       while (it.hasNext())
588       {
589          data = (LogRecord.Data) it.next();
590          byte[] globalId = data.globalTransactionId;
591          Long JavaDoc localId = new Long JavaDoc(data.localTransactionId);
592          heurData =
593             (LogRecord.HeurData)heuristicallyCompletedTransactions.get(localId);
594
595          if (heurData != null)
596             heuristicallyCompletedTransactions.remove(localId);
597          
598          if (heurData != null && !heurData.locallyDetectedHeuristicHazard)
599          {
600             heuristicallyCompletedTransactions.remove(localId);
601             List JavaDoc xaResourcesWithHeuristics = getXAWork(globalId, toRecoverMap);
602             txManager.recreateTransaction(heurData,
603                                           xaResourcesWithHeuristics,
604                                           completionHandler);
605          }
606          else
607          {
608             // Either heurData is null or it has a
609
// locally-detected heuristic hazard.
610

611             if (heurData == null)
612             {
613                List JavaDoc preparedXAWorkList = getXAWork(globalId, toRecoverMap);
614                txManager.recreateTransaction(data.localTransactionId,
615                                              data.inboundFormatId,
616                                              data.globalTransactionId,
617                                              data.recoveryCoordinator,
618                                              preparedXAWorkList,
619                                              data.resources,
620                                              completionHandler,
621                                              null);
622             }
623             else
624             {
625                // locally-detected heuristic hazard
626
if (heurData.transactionStatus == Status.STATUS_COMMITTING)
627                {
628                   List JavaDoc pendingXAWorkList = commitXAWork(globalId, toRecoverMap);
629                   txManager.recreateTransaction(data.localTransactionId,
630                                                 data.inboundFormatId,
631                                                 data.globalTransactionId,
632                                                 data.recoveryCoordinator,
633                                                 pendingXAWorkList,
634                                                 data.resources,
635                                                 completionHandler,
636                                                 heurData);
637                }
638                else if (heurData.transactionStatus == Status.STATUS_ROLLING_BACK)
639                {
640                   List JavaDoc pendingXAWorkList =
641                      rollbackXAWork(globalId, toRecoverMap);
642                   txManager.recreateTransaction(data.localTransactionId,
643                                                 data.inboundFormatId,
644                                                 data.globalTransactionId,
645                                                 data.recoveryCoordinator,
646                                                 pendingXAWorkList,
647                                                 data.resources,
648                                                 completionHandler,
649                                                 heurData);
650                }
651                else
652                {
653                   log.warn("Cannot recover tx=" + toString() +
654                            "\nInconsistent state",
655                            new Throwable JavaDoc("[Stack trace]"));
656                }
657             }
658          }
659       }
660    
661       it = inDoubtJcaTransactions.iterator();
662       while (it.hasNext())
663       {
664          data = (LogRecord.Data) it.next();
665          byte[] globalId = data.globalTransactionId;
666          Long JavaDoc localId = new Long JavaDoc(data.localTransactionId);
667          heurData =
668             (LogRecord.HeurData)heuristicallyCompletedTransactions.get(localId);
669
670          if (heurData != null)
671             heuristicallyCompletedTransactions.remove(localId);
672
673          if (heurData != null && !heurData.locallyDetectedHeuristicHazard)
674          {
675             List JavaDoc xaResourcesWithHeuristics = getXAWork(globalId, toRecoverMap);
676             txManager.recreateTransaction(heurData,
677                                           xaResourcesWithHeuristics,
678                                           completionHandler);
679          }
680          else
681          {
682             // Either heurData is null or it has a
683
// locally-detected heuristic hazard.
684

685             List JavaDoc preparedXAWorkList = getXAWork(globalId, toRecoverMap);
686             if (heurData == null)
687             {
688                txManager.recreateTransaction(data.localTransactionId,
689                                              data.inboundFormatId,
690                                              data.globalTransactionId,
691                                              data.inboundBranchQualifier,
692                                              preparedXAWorkList,
693                                              data.resources,
694                                              completionHandler,
695                                              null);
696             }
697             else
698             {
699                // locally-detected heuristic hazard
700
if (heurData.transactionStatus == Status.STATUS_COMMITTING)
701                {
702                   List JavaDoc pendingXAWorkList = commitXAWork(globalId, toRecoverMap);
703                   txManager.recreateTransaction(data.localTransactionId,
704                                                 data.inboundFormatId,
705                                                 data.globalTransactionId,
706                                                 data.inboundBranchQualifier,
707                                                 pendingXAWorkList,
708                                                 data.resources,
709                                                 completionHandler,
710                                                 heurData);
711                }
712                else if (heurData.transactionStatus == Status.STATUS_ROLLING_BACK)
713                {
714                   List JavaDoc pendingXAWorkList =
715                      rollbackXAWork(globalId, toRecoverMap);
716                   txManager.recreateTransaction(data.localTransactionId,
717                                                 data.inboundFormatId,
718                                                 data.globalTransactionId,
719                                                 data.inboundBranchQualifier,
720                                                 pendingXAWorkList,
721                                                 data.resources,
722                                                 completionHandler,
723                                                 heurData);
724                }
725                else
726                {
727                   log.warn("Cannot recover tx=" + toString() +
728                            "\nInconsistent state",
729                            new Throwable JavaDoc("[Stack trace]"));
730                }
731             }
732          }
733       }
734    }
735
736    /**
737     * Commits the XA work associated with a given global transaction id. This
738     * method receives a <code>toRecoverMap</code> whose values are
739     * <code>XAResourceXids<code> objects that contain the <code>Xids</code>
740     * of all active XA transaction branches that require recovery actions.
741     * It removes from the <code>toRecoverMap</code> all <code>Xids</code>
742     * that correspond to the XA work committed (those associated with the
743     * specified <code>globalId</code>).
744     *
745     * @param globalId the global transaction id associated with the work to
746     * commit
747     * @param toRecoverMap a map whose keys are <code>Recoverable</code> ids and
748     * whose values are <code>XAResourceXids<code> objects,
749     * each of which contains the set of <code>Xids</code>
750     * for all active XA transaction branches that involve
751     * a given <code>XAResource<code> and that require
752     * recovery actions
753     * @return a "pending work" list containing <code>XAWork</code> instances
754     * describing the work that should have been committed, but could
755     * not be committed due to transient problems. The caller should
756     * try to commit the pending work again, at a later time.
757     */

758    private List JavaDoc commitXAWork(byte[] globalId, Map JavaDoc toRecoverMap)
759    {
760       log.info("*** trying to complete XA work with globalId " +
761                new String JavaDoc(globalId).trim());
762       globalId = pad(globalId);
763       List JavaDoc pendingXAWorkList = new ArrayList JavaDoc();
764       Iterator JavaDoc rit = toRecoverMap.values().iterator();
765       while (rit.hasNext())
766       {
767          XAResourceXids toRecover = (XAResourceXids) rit.next();
768          log.info(" looking at resource " + toRecover.recoverable.getId());
769
770          Iterator JavaDoc resXidIt = toRecover.xids.iterator();
771          while (resXidIt.hasNext())
772          {
773             Xid JavaDoc resXid = (Xid JavaDoc) resXidIt.next();
774             byte[] resGlobalId = pad(resXid.getGlobalTransactionId());
775             if (!Arrays.equals(globalId, resGlobalId))
776                continue;
777             try
778             {
779                toRecover.resource.commit(resXid, false);
780                log.info(" committed: " + resXid);
781             }
782             catch (XAException JavaDoc e)
783             {
784                switch (e.errorCode)
785                {
786                case XAException.XA_HEURCOM:
787                   // Ignore this exception, as the the heuristic outcome
788
// is the one we wanted anyway.
789
log.trace("commitXAWork ignored XAException.XA_HEURCOM", e);
790                   try
791                   {
792                      toRecover.resource.forget(resXid);
793                   }
794                   catch (XAException JavaDoc xae)
795                   {
796                      log.warn("XAException in commitXAWork (when forgetting " +
797                               "XA_HEURCOM): errorCode=" +
798                               TxUtils.getXAErrorCodeAsString(xae.errorCode),
799                               xae);
800                   }
801                   break;
802                case XAException.XA_HEURRB:
803                case XAException.XA_HEURMIX:
804                case XAException.XA_HEURHAZ:
805                   log.warn("Heuristic XAException in commitXAWork: errorCode=" +
806                            TxUtils.getXAErrorCodeAsString(e.errorCode) +
807                            "\nWill deal with the heuristic later", e);
808                   XAWork postponedWork = new XAWork(toRecover.resource,
809                                                     resXid,
810                                                     toRecover.resourceAccess);
811                   pendingXAWorkList.add(postponedWork);
812                   break;
813                case XAException.XAER_RMERR:
814                   log.warn("Unexpected XAException in commitXAWork: errorCode="
815                            + TxUtils.getXAErrorCodeAsString(e.errorCode), e);
816                   break;
817                case XAException.XAER_RMFAIL:
818                case XAException.XA_RETRY:
819                   log.warn("XAException in commitXAWork: errorCode=" +
820                            TxUtils.getXAErrorCodeAsString(e.errorCode) +
821                            "\nWill attempt to commit the XAResource later", e);
822                   XAWork pendingXAWork = new XAWork(toRecover.resource,
823                                                     resXid,
824                                                     toRecover.resourceAccess);
825                   pendingXAWorkList.add(pendingXAWork);
826                   break;
827                case XAException.XAER_NOTA:
828                case XAException.XAER_INVAL:
829                case XAException.XAER_PROTO:
830                default:
831                   // This should never happen!
832
log.warn("Could not recover from unexpected XAException: " +
833                            " errorCode=" +
834                            TxUtils.getXAErrorCodeAsString(e.errorCode), e);
835                   break;
836                }
837             }
838             finally
839             {
840                resXidIt.remove(); // remove resXid from toRecover.xids
841
}
842          }
843          if (toRecover.xids.isEmpty())
844             rit.remove(); // remove toRecover from toRecoverMap
845
}
846       return pendingXAWorkList;
847    }
848
849    private List JavaDoc rollbackXAWork(byte[] globalId, Map JavaDoc toRecoverMap)
850    {
851       log.info("*** trying to rollback XA work with globalId " +
852                new String JavaDoc(globalId).trim());
853       globalId = pad(globalId);
854       List JavaDoc pendingXAWorkList = new ArrayList JavaDoc();
855       Iterator JavaDoc rit = toRecoverMap.values().iterator();
856       while (rit.hasNext())
857       {
858          XAResourceXids toRecover = (XAResourceXids) rit.next();
859          log.info(" looking at resource " + toRecover.recoverable.getId());
860
861          Iterator JavaDoc resXidIt = toRecover.xids.iterator();
862          while (resXidIt.hasNext())
863          {
864             Xid JavaDoc resXid = (Xid JavaDoc) resXidIt.next();
865             byte[] resGlobalId = pad(resXid.getGlobalTransactionId());
866             if (!Arrays.equals(globalId, resGlobalId))
867                continue;
868             try
869             {
870                toRecover.resource.rollback(resXid);
871                log.info(" rolledback: " + resXid);
872             }
873             catch (XAException JavaDoc e)
874             {
875                switch (e.errorCode)
876                {
877                case XAException.XA_HEURRB:
878                   // Ignore this exception, as the the heuristic outcome
879
// is the one we wanted anyway.
880
log.trace("rollbackXAWork ignored XAException.XA_HEURRB", e);
881                   try
882                   {
883                      toRecover.resource.forget(resXid);
884                   }
885                   catch (XAException JavaDoc xae)
886                   {
887                      log.warn("XAException in rollbackXAWork (when forgetting "
888                               + "XA_HEURRB): errorCode=" +
889                               TxUtils.getXAErrorCodeAsString(xae.errorCode),
890                               xae);
891                   }
892                   break;
893                case XAException.XA_HEURCOM:
894                case XAException.XA_HEURMIX:
895                case XAException.XA_HEURHAZ:
896                   log.warn("Heuristic XAException in rollbackXAWork: errorCode="
897                            + TxUtils.getXAErrorCodeAsString(e.errorCode) +
898                            "\nWill deal with the heuristic later", e);
899                   XAWork postponedWork = new XAWork(toRecover.resource,
900                                                     resXid,
901                                                     toRecover.resourceAccess);
902                   pendingXAWorkList.add(postponedWork);
903                   break;
904                case XAException.XAER_RMERR:
905                   log.warn("Unexpected XAException in rollbackXAWork: " +
906                            "errorCode="
907                            + TxUtils.getXAErrorCodeAsString(e.errorCode), e);
908                   break;
909                case XAException.XAER_RMFAIL:
910                case XAException.XA_RETRY:
911                   log.warn("XAException in rollbackXAWork: errorCode=" +
912                            TxUtils.getXAErrorCodeAsString(e.errorCode) +
913                            "\nWill attempt to rollback the XAResource later", e);
914                   XAWork pendingXAWork = new XAWork(toRecover.resource,
915                                                     resXid,
916                                                     toRecover.resourceAccess);
917                   pendingXAWorkList.add(pendingXAWork);
918                   break;
919                case XAException.XAER_NOTA:
920                case XAException.XAER_INVAL:
921                case XAException.XAER_PROTO:
922                default:
923                   // This should never happen!
924
log.warn("Could not recover from unexpected XAException: " +
925                            " errorCode=" +
926                            TxUtils.getXAErrorCodeAsString(e.errorCode), e);
927                   break;
928                }
929             }
930             finally
931             {
932                resXidIt.remove(); // remove resXid from toRecover.xids
933
}
934          }
935          if (toRecover.xids.isEmpty())
936             rit.remove(); // remove toRecover from toRecoverMap
937
}
938       return pendingXAWorkList;
939    }
940    
941    /**
942     * Extracts from a <code>toRecoverMap</code> all the XA work that is
943     * associated with a given global transaction id. This method scans
944     * the <code>toRecoverMap</code> and builds a list of <code>XAWork</code>
945     * instances whose <code>Xids</code> contain the specified global id.
946     * It removes all those <code>Xids</code> from the the
947     * <code>toRecoverMap</code>.
948     *
949     * @param globalId the global transaction id
950     * @param toRecoverMap a map whose keys are <code>Recoverable</code> ids
951     * and whose values are <code>XAResourceXids<code>
952     * objects, each of which contains the set of
953     * <code>Xids</code> for the active XA transaction
954     * branches that involve a given <code>XAResource<code>
955     * and that require recovery actions.
956     * @return a <code>List</code> of <code>XAWork</code> instances with
957     * <code>Xid</code> fields that were taken from the
958     * <code>toRecoverMap</code> and that contain the specified global
959     * transaction id.
960     */

961    private List JavaDoc getXAWork(byte[] globalId, Map JavaDoc toRecoverMap)
962    {
963       log.info("*** getting XA work with globalId " +
964                new String JavaDoc(globalId).trim());
965       globalId = pad(globalId);
966       List JavaDoc xaWorkList = new ArrayList JavaDoc();
967       Iterator JavaDoc rit = toRecoverMap.values().iterator();
968       while (rit.hasNext())
969       {
970          XAResourceXids toRecover = (XAResourceXids) rit.next();
971          log.info(" looking at resource " + toRecover.recoverable.getId());
972
973          Iterator JavaDoc resXidIt = toRecover.xids.iterator();
974          while (resXidIt.hasNext())
975          {
976             Xid JavaDoc resXid = (Xid JavaDoc) resXidIt.next();
977             byte[] resGlobalId = pad(resXid.getGlobalTransactionId());
978             if (!Arrays.equals(globalId, resGlobalId))
979                continue;
980
981             XAWork preparedXAWork = new XAWork(toRecover.resource,
982                                                resXid,
983                                                toRecover.resourceAccess);
984             xaWorkList.add(preparedXAWork);
985             resXidIt.remove(); // remove resXid from toRecover.xids
986
}
987       }
988       return xaWorkList;
989    }
990
991    /**
992     * Takes an iterator for a collection of <code>XAResourceXids</code>
993     * instances and cleans up every <code>Recoverable</code> in the
994     * <code>recoverable</code> field of an element of the collection.
995     */

996    private void cleanupRecoverables(Iterator JavaDoc it)
997    {
998       while (it.hasNext())
999       {
1000         XAResourceXids xaResXids = (XAResourceXids) it.next();
1001         try
1002         {
1003            xaResXids.resourceAccess.release();
1004         }
1005         catch (Exception JavaDoc ignored)
1006         {
1007         }
1008      }
1009   }
1010
1011   /**
1012    * Filters out every xid whose branch qualifier field was not generated by
1013    * transaction manager. This is to avoid rolling back transaction branches
1014    * that are not ours.
1015    */

1016   private List JavaDoc pruneXidList(Xid JavaDoc[] xids,
1017                             Set JavaDoc branchQualifiers,
1018                             String JavaDoc resourceName)
1019   {
1020      ArrayList JavaDoc list = new ArrayList JavaDoc();
1021      for (int i = 0; i < xids.length; i++)
1022      {
1023         byte[] branchQual = xids[i].getBranchQualifier();
1024         String JavaDoc baseBranchQual = xidFactory.getBaseBranchQualifier(branchQual);
1025         if (branchQualifiers.contains(baseBranchQual))
1026         {
1027            list.add(xids[i]);
1028            log.info("Adding xid " + xidFactory.toString(xids[i]) +
1029                     " to pruned Xid list for " + resourceName);
1030                     
1031         }
1032      }
1033      return list;
1034   }
1035
1036   /**
1037    * Pads a byte array with null bytes so that the length of the padded
1038    * array is <code>Xid.MAXGTRIDSIZE</code>. Called before comparing
1039    * global transaction ids.
1040    *
1041    */

1042   private byte[] pad(byte[] globalId)
1043   {
1044      if (globalId.length < Xid.MAXGTRIDSIZE)
1045      {
1046         byte[] bytes = new byte[Xid.MAXGTRIDSIZE];
1047         System.arraycopy(globalId, 0, bytes, 0, globalId.length);
1048         globalId = bytes;
1049      }
1050      return globalId;
1051   }
1052   
1053}
1054
Popular Tags