KickJava   Java API By Example, From Geeks To Geeks.

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


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.nio.ByteBuffer JavaDoc;
25 import java.util.Arrays JavaDoc;
26 import java.util.zip.Adler32 JavaDoc;
27 import java.util.zip.Checksum JavaDoc;
28
29 import javax.transaction.xa.Xid JavaDoc;
30
31 import org.jboss.tm.TxUtils;
32
33 /**
34  * Utility class with static methods to create transaction log records and to
35  * extract information from transaction log records. It has static methods to
36  * create the following kinds of log records:
37  * <ul>
38  * <li><code>TX_COMMITTED</code> records, which are used for locally-started
39  * transactions that do not involve other transaction managers;</li>
40  * <li><code>MULTI_TM_TX_COMMITTED</code> records, which are used for
41  * locally-started transactions that involve other transaction
42  * managers;</li>
43  * <li><code>TX_PREPARED</code> records, which are used for foreign
44  * transactions that entered this virtual machine in transaction contexts
45  * propagated along with remote method invocations;</li>
46  * <li><code>JCA_TX_PREPARED</code> records, which are used for foreign
47  * transactions that entered this virtual machine through JCA transaction
48  * inflow;</li>
49  * <li><code>TX_END</code> records, which are used for distributed
50  * transactions and mark the end of the second phase of the 2PC subtree
51  * coordinated by this transaction manager. They are paired with
52  * <code>MULTI_TM_TX_COMMITTED</code>, <code>TX_PREPARED</code>, and
53  * <code>JCA_TX_PREPARED</code> records. No <code>TX_END</code> record
54  * is written out in the case of a locally-started transaction that
55  * involves no external transaction managers. In other words,
56  * <code>TX_END</code> records are not paired with
57  * <code>TX_COMMITTED</code> records;</li>
58  *<li><code>HEUR_STATUS</code> records, which are used to log the
59  * heuristic status of a distributed transaction;</li>
60  *<li><code>HEUR_FORGOTTEN</code> records, which are used to clear the
61  * heuristic status of a distributed transaction.</li>
62  * </ul>
63  * Layout of <code>MULTI_TM_TX_COMMITTED</code> records:
64  * <pre>
65  * - magicHeader (an array of HEADER_LEN bytes)
66  * - recordLength (short)
67  * - recordLengthCheck (short)
68  * - recordType (byte)
69  * - localTransactionId (long)
70  * - countOfDirEntries (N, a short)
71  * - varField 0 \
72  * ... | <------------ variable-sized fields
73  * - varField N-1 /
74  * - offset of varField N-1 \ directory of varFields: N dir entries
75  * - length of varField N-1 | (each entry contains the length of
76  * ... | <-- a varField and the offset of its
77  * - offset of varField 0 | first byte relative to the start
78  * - length of varField 0 / of the record)
79  * - checksum (int)
80  * </pre>
81  * The varFields of a <code>MULTI_TM_TX_COMMITTED</code> record contain
82  * stringfied references for the remote <code>Resource</code>s enlisted in the
83  * transaction. Note that the dir entries are stored backwards at the end of
84  * the record, that is, the one that appears last (just before the checksum)
85  * refers to the first varField, and the one that appears first refers to the
86  * last varField.
87  * <p>
88  * Layout of <code>TX_PREPARED</code> and <code>JCA_TX_PREPARED</code>
89  * records:
90  * <pre>
91  * - magicHeader (an array of HEADER_LEN bytes)
92  * - recordLength (short)
93  * - recordLengthCheck (short)
94  * - recordType (byte)
95  * - localTransactionId (long)
96  * - countOfDirEntries (N, a short)
97  * - inboundFormatId (int)
98  * - gidLength (short)
99  * - globalTransactionId (an array of gidLength bytes)
100  * - varField 0 \
101  * ... | <------------ variable-sized fields
102  * - varField N-1 /
103  * - offset of varField N-1 \ directory of varFields: N dir entries
104  * - length of varField N-1 | (each entry contains the length of
105  * ... | <-- a varField and the offset of its
106  * - offset of varField 0 | first byte relative to the start
107  * - length of varField 0 / of the record)
108  * - checksum (int)
109  * </pre>
110  * The inboundFormatId is the formatId of the foreign <code>Xid</code>.
111  * In a <code>TX_PREPARED</code> record, the first varField (the one referred
112  * to by the dir entry that immediately precedes the checksum) contains a
113  * stringfied reference for the <code>RecoveryCoordinator</code> of the
114  * transaction.
115  * In a <code>JCA_TX_PREPARED</code> record, the first varField contains the
116  * inbound branch qualifier, which is the branch qualifier part of the foreign
117  * <code>Xid</code> passed to <code>XATerminator.prepare()</code>.
118  * The remaining varFields of a <code>TX_PREPARED</code> or
119  * <code>JCA_TX_PREPARED</code> records contain stringfied references for the
120  * remote <code>Resource</code>s enlisted in the transaction.
121  * </p>
122  * Layout of <code>TX_COMMITTED</code> records:
123  * <pre>
124  * - magicHeader (an array of HEADER_LEN bytes)
125  * - recordLength (short)
126  * - recordLengthCheck (short)
127  * - recordType (byte)
128  * - localTransactionId (long),
129  * - checksum (int)
130  * </pre>
131  * <p>
132  * Layout of <code>TX_END</code> records:
133  * <pre>
134  * - magicHeader (an array of HEADER_LEN bytes)
135  * - recordLength (short)
136  * - recordLengthCheck (short)
137  * - recordType (byte)
138  * - localTransactionId (long),
139  * - checksum (int)
140  * </pre>
141  * <p>
142  * Layout of <code>HEUR_STATUS</code> records:
143  * <pre>
144  * - magicHeader (an array of HEADER_LEN bytes)
145  * - recordLength (short)
146  * - recordLengthCheck (short)
147  * - recordType (byte)
148  * - localTransactionId (long)
149  * - countOfDirEntries (N, a short)
150  * - transactionStatus (byte)
151  * - heuristicStatusCode (byte)
152  * - locallyDetectedHeuristicHazardFlag (byte)
153  * - isForeignTx (byte)
154  * - formatId (int)
155  * - gidLength (short)
156  * - globalTransactionId (an array of gidLength bytes)
157  * - varField 0 \
158  * ... | <------------ variable-sized fields
159  * - varField N-1 /
160  * - offset of varField N-1 \ directory of varFields: N dir entries
161  * - length of varField N-1 | (each entry contains the length of
162  * ... | <-- a varField and the offset of its
163  * - offset of varField 0 | first byte relative to the start
164  * - length of varField 0 / of the record)
165  * - checksum (int)
166  * </pre>
167  *
168  * In a <code>HEUR_STATUS</code> record, the first varField (the one referred
169  * to by the dir entry that immediately precedes the checksum) contains the
170  * the inbound branch qualifier of a transaction that entered the server via
171  * JCA inflow. This varField is empty (i.e., it has zero length) in the case of
172  * a transaction that did not enter the server via JCA inflow. The second
173  * varField contains a byte array with the heuristic status codes of the XA
174  * resources that reported heuristic decisions. This varField is empty if no
175  * XA resource reported heuristic decisions. The remaining varFields are
176  * associated with remote resources that reported heuristic decisions. Each
177  * such varField contains a byte with the heuristic status code reported by the
178  * remote resource, followed by a stringfied reference for the remote
179  * <code>Resource</code> instance that reported a heuristic decision.
180  * <p>
181  * Layout of <code>HEUR_FORGOTTEN</code> records:
182  * <pre>
183  * - magicHeader (an array of HEADER_LEN bytes)
184  * - recordLength (short)
185  * - recordLengthCheck (short)
186  * - recordType (byte)
187  * - localTransactionId (long),
188  * - checksum (int)
189  * </pre>
190  * <p>
191  * Note that <code>TX_COMMITTED</code>, <code>TX_END</code>, and
192  * <code>HEUR_FORGOTTEN</code> records have fixed size. The other types of
193  * records are variable-sized.
194  * <p>
195  * In all record types:
196  * <ul>
197  * <li>The recordLength is the number of bytes of the part of the record that
198  * follows the recordLengthCheck field. It counts the bytes from the
199  * recordType field up to (and including) the checksum field.</li>
200  * <li>The recordLengthCheck is the arithmetic negation of the recordLength
201  * value (i.e,, -recordLength).</li>
202  * <li>The checksum is the Adler-32 checksum of the part of the record that
203  * starts at the recordType field(which is included in the checksum) and
204  * ends at the checksum field (which is not included).</li>
205  * </ul>
206  * @author <a HREF="mailto:reverbel@ime.usp.br">Francisco Reverbel</a>
207  * @version $Revision: 37459 $
208  */

209 public class LogRecord
210 {
211    /** Magic header placed in all log records.*/
212    public static final byte[] HEADER = "Log".getBytes();
213    
214    /** Length of the magic header. */
215    public static final int HEADER_LEN = HEADER.length;
216    
217    /** Null header that will be read when there are no more log records */
218    private static final byte[] NULL_HEADER = {0, 0, 0};
219    
220    /** Size of a byte, in bytes. */
221    private static final int SIZEOF_BYTE = 1;
222    
223    /** Size of a short, in bytes. */
224    static final int SIZEOF_SHORT = 2;
225
226    /** Size of a long, in bytes. */
227    private static final int SIZEOF_LONG = 8;
228
229    /** Length of the inbound format id. */
230    private static final int FORMAT_ID_LEN = 4;
231    
232    /** Length of the checksum. */
233    private static final int CHKSUM_LEN = 4;
234    
235    /** Total length of the magic header plus record length fields. */
236    static final int FULL_HEADER_LEN = HEADER_LEN + 2 * SIZEOF_SHORT;
237    
238    /** Value of recordType field in a single-TM tx committed record */
239    static final byte TX_COMMITTED = (byte) 'C';
240    
241    /** Value of recordType field in a multi-TM tx committed record */
242    static final byte MULTI_TM_TX_COMMITTED = (byte) 'M';
243
244    /** Value of recordType field in a tx prepared record */
245    static final byte TX_PREPARED = (byte) 'P';
246
247    /** Value of recordType field in a JCA tx prepared record */
248    static final byte JCA_TX_PREPARED = (byte) 'R';
249
250    /** Value of recordType field in a tx end record */
251    static final byte TX_END = (byte) 'E';
252
253    /** Value of recordType field in a heuristic status record */
254    static final byte HEUR_STATUS = (byte) 'H';
255
256    /** Value of recordType field in a heuristic status record */
257    static final byte HEUR_FORGOTTEN = (byte) 'F';
258
259    /** Size of varField directory entry */
260    private static final int SIZEOF_DIR_ENTRY =
261          SIZEOF_SHORT /* offset */
262          + SIZEOF_SHORT; /* length */
263    
264    /**
265     * Minimum length of a multi-TM tx committed
266     * (<code>MULTI_TM_TX_COMMITTED</code>) record.
267     */

268    private static final int MIN_MULTI_TM_TX_COMMITTED_LEN =
269          HEADER_LEN /* magic header */
270          + SIZEOF_SHORT /* record length */
271          + SIZEOF_SHORT /* record length check */
272          + SIZEOF_BYTE /* record type */
273          + SIZEOF_LONG /* local transaction id */
274          + SIZEOF_SHORT /* count of dir entries */
275          + CHKSUM_LEN; /* checksum */
276    
277    /**
278     * Minimum length of a tx prepared (<code>TX_PREPARED</code>)
279     * or JCA tx prepared (<code>JCA_TX_PREPARED</code>) record.
280     */

281    private static final int MIN_TX_PREPARED_LEN =
282          HEADER_LEN /* magic header */
283          + SIZEOF_SHORT /* record length */
284          + SIZEOF_SHORT /* record length check */
285          + SIZEOF_BYTE /* record type */
286          + SIZEOF_LONG /* local transaction id */
287          + SIZEOF_SHORT /* count of dir entries */
288          + FORMAT_ID_LEN /* inbound format id */
289          + SIZEOF_SHORT /* length of global transaction id */
290          + CHKSUM_LEN; /* checksum */
291    
292    /**
293     * Fixed length of a single-TM tx committed (<code>TX_COMMITTED</code>)
294     * record.
295     */

296    private static final int TX_COMMITED_LEN =
297       HEADER_LEN /* magic header */
298       + SIZEOF_SHORT /* record length */
299       + SIZEOF_SHORT /* record length check */
300       + SIZEOF_BYTE /* record type (TX_COMMITTED) */
301       + SIZEOF_LONG /* local transaction id */
302       + CHKSUM_LEN; /* checksum */
303
304    /**
305     * Fixed length of a tx end (<code>TX_END</code>) record.
306     */

307    static final int TX_END_LEN =
308       HEADER_LEN /* magic header */
309       + SIZEOF_SHORT /* record length */
310       + SIZEOF_SHORT /* record length check */
311       + SIZEOF_BYTE /* record type (TX_END) */
312       + SIZEOF_LONG /* local transaction id */
313       + CHKSUM_LEN; /* checksum */
314
315    /**
316     * Minimum length of a heuristic status (<code>HEUR_STATUS</code>) record.
317     */

318    private static final int MIN_HEUR_STATUS_LEN =
319          HEADER_LEN /* magic header */
320          + SIZEOF_SHORT /* record length */
321          + SIZEOF_SHORT /* record length check */
322          + SIZEOF_BYTE /* record type (HEUR_STATUS) */
323          + SIZEOF_LONG /* local transaction id */
324          + SIZEOF_SHORT /* count of dir entries */
325          + SIZEOF_BYTE /* transaction status */
326          + SIZEOF_BYTE /* heuristic status code */
327          + SIZEOF_BYTE /* locally-detected heuristic hazard flag */
328          + SIZEOF_BYTE /* foreign tx flag */
329          + FORMAT_ID_LEN /* format id */
330          + SIZEOF_SHORT /* length of global transaction id */
331          + SIZEOF_DIR_ENTRY /* varField with inbound branch qualifier */
332          + SIZEOF_DIR_ENTRY /* varField with heuristic codes for XA resources */
333          + CHKSUM_LEN; /* checksum */
334    
335    /**
336     * Fixed length of a heuristic forgotten (HEUR_FORGOTTEN) record.
337     */

338    static final int HEUR_FORGOTTEN_LEN =
339       HEADER_LEN /* magic header */
340       + SIZEOF_SHORT /* record length */
341       + SIZEOF_SHORT /* record length check */
342       + SIZEOF_BYTE /* record type (HEUR_FORGOTTEN) */
343       + SIZEOF_LONG /* local transaction id */
344       + CHKSUM_LEN; /* checksum */
345
346    
347
348    /**
349     * Structure filled out by the method <code>LogRecord.getData()</code>.
350     */

351    public static class Data
352    {
353       public byte recordType;
354       public long localTransactionId;
355       public byte[] globalTransactionId;
356       public int inboundFormatId;
357       public String JavaDoc recoveryCoordinator;
358       public byte[] inboundBranchQualifier;
359       public String JavaDoc[] resources;
360    }
361    
362    /**
363     * Structure filled out by the method <code>LogRecord.getHeurData()</code>.
364     */

365    public static class HeurData
366    {
367       public byte recordType;
368       public long localTransactionId;
369       public boolean foreignTx;
370       public int formatId;
371       public byte[] globalTransactionId;
372       public byte[] inboundBranchQualifier;
373       public int transactionStatus;
374       public int heuristicStatusCode;
375       public boolean locallyDetectedHeuristicHazard;
376       public int[] xaResourceHeuristics;
377       public HeuristicStatus[] remoteResourceHeuristics;
378    }
379    
380    /**
381     * Private constructor to enforce non-instantiability.
382     */

383    private LogRecord()
384    {
385    }
386       
387    /**
388     * Creates a tx committed record for a locally-started transaction that
389     * does not involve other transaction managers.
390     *
391     * @param localTransactionId the local id of the transaction
392     * @return a <code>ByteBuffer</code> containing the tx committed record.
393     * The buffer position is set to zero and the buffer limit is set
394     * to the number of bytes in the commit record.
395     */

396    static ByteBuffer JavaDoc createTxCommittedRecord(long localTransactionId)
397    {
398       ByteBuffer JavaDoc buffer = ByteBuffer.allocate(TX_COMMITED_LEN);
399       
400       buffer.put(HEADER)
401             .putShort((short) (TX_COMMITED_LEN - FULL_HEADER_LEN))
402             .putShort((short) -(TX_COMMITED_LEN - FULL_HEADER_LEN))
403             .put(TX_COMMITTED)
404             .putLong(localTransactionId);
405       
406       Checksum JavaDoc checksum = new Adler32 JavaDoc();
407       checksum.update(buffer.array(),
408                       FULL_HEADER_LEN,
409                       SIZEOF_BYTE + SIZEOF_LONG);
410       buffer.putInt(TX_COMMITED_LEN - CHKSUM_LEN, (int) checksum.getValue());
411
412       return (ByteBuffer JavaDoc) buffer.position(0);
413    }
414    
415    /**
416     * Creates a multi-TM tx committed record for a distributed transaction.
417     *
418     * @param localTransactionId the local id of the transaction
419     * @param resources an array of stringfied references for the remote
420     * resources (<code>org.jboss.tm.remoting.Resource</code>
421     * instances) enlisted in the transaction.
422     * @return a <code>ByteBuffer</code> containing the tx committed record.
423     * The buffer position is set to zero and the buffer limit is set
424     * to the number of bytes in the record.
425     */

426    static ByteBuffer JavaDoc createTxCommittedRecord(long localTransactionId,
427                                              String JavaDoc[] resources)
428    {
429       int recordLen = MIN_MULTI_TM_TX_COMMITTED_LEN;
430       int resourceCount = 0;
431
432       if (resources != null && (resourceCount = resources.length) > 0)
433       {
434          for (int i = 0; i < resourceCount; i++)
435          {
436             recordLen += SIZEOF_DIR_ENTRY /* offset and length */
437                         + resources[i].length(); /* data */
438          }
439       }
440       else
441          throw new RuntimeException JavaDoc("No remote resources were specified");
442
443       ByteBuffer JavaDoc buffer = ByteBuffer.allocate(recordLen);
444       
445       buffer.put(HEADER)
446             .putShort((short) (recordLen - FULL_HEADER_LEN))
447             .putShort((short) -(recordLen - FULL_HEADER_LEN))
448             .put(MULTI_TM_TX_COMMITTED)
449             .putLong(localTransactionId)
450             .putShort((short) resourceCount);
451       
452       for (int i= 0; i < resourceCount; i++)
453       {
454          int offset = buffer.position();
455          int length = resources[i].length();
456          byte[] resourceBytes = new byte[length];
457          
458          resources[i].getBytes(0, length, resourceBytes, 0);
459          buffer.put(resourceBytes)
460                .putShort(recordLen
461                          - CHKSUM_LEN - SIZEOF_SHORT - SIZEOF_DIR_ENTRY * i,
462                          (short) length)
463                .putShort(recordLen
464                          - CHKSUM_LEN - SIZEOF_DIR_ENTRY - SIZEOF_DIR_ENTRY * i,
465                          (short) offset);
466       }
467       
468       Checksum JavaDoc checksum = new Adler32 JavaDoc();
469       checksum.update(buffer.array(),
470                       FULL_HEADER_LEN,
471                       recordLen - FULL_HEADER_LEN - CHKSUM_LEN);
472       buffer.putInt(recordLen - CHKSUM_LEN, (int) checksum.getValue());
473       
474       return (ByteBuffer JavaDoc) buffer.position(0);
475    }
476
477    /**
478     * Creates a tx prepared record or a JCA tx prepared record.
479     *
480     * @param localTransactionId the local id of the transaction
481     * @param inboundFormatId the format id of the foreign <code>Xid</code>
482     * @param globalTransactionId the global id of the transaction
483     * @param jcaInboundTransaction true if this method should create a JCA tx
484     * prepared record, false if this method should
485     * create a tx prepared record
486     * @param recoveryCoordOrInboundBranchQual an stringfied recovery
487     * coordinator converted to a byte array (if
488     * jcaInboundTransaction is false) or the inbound
489     * branch qualifier of a JCA inbound transaction (if
490     * jcaInboundTransaction is true)
491     * @param resources an array of stringfied references for the remote
492     * resources (<code>org.jboss.tm.remoting.Resource</code>
493     * instances) enlisted in the transaction.
494     * @return a <code>ByteBuffer</code> containing the tx prepared record or
495     * JCA tx prepared record. The buffer position is set to zero and
496     * the buffer limit is set to the number of bytes in the record.
497     */

498    private static ByteBuffer JavaDoc createTxPreparedRecord(
499                                        long localTransactionId,
500                                        int inboundFormatId,
501                                        byte[] globalTransactionId,
502                                        boolean jcaInboundTransaction,
503                                        byte[] recoveryCoordOrInboundBranchQual,
504                                        String JavaDoc[] resources)
505    {
506       int recordLen = MIN_TX_PREPARED_LEN;
507       int globalTxIdLen = 0;
508       int resourceCount = 0;
509
510       if (globalTransactionId != null &&
511             (globalTxIdLen = globalTransactionId.length) > 0)
512       {
513          recordLen += globalTxIdLen;
514       }
515       else
516          throw new RuntimeException JavaDoc("The global transaction id " +
517                                     "was not specified");
518          
519       if (resources != null && (resourceCount = resources.length) > 0)
520       {
521          for (int i = 0; i < resourceCount; i++)
522          {
523             recordLen += SIZEOF_DIR_ENTRY /* dir entry */
524                         + resources[i].length(); /* data */
525          }
526       }
527
528       recordLen += SIZEOF_DIR_ENTRY /* dir entry */
529                   + recoveryCoordOrInboundBranchQual.length; /* data */
530
531       ByteBuffer JavaDoc buffer = ByteBuffer.allocate(recordLen);
532
533       buffer.put(HEADER)
534             .putShort((short) (recordLen - FULL_HEADER_LEN))
535             .putShort((short) -(recordLen - FULL_HEADER_LEN))
536             .put(jcaInboundTransaction ? JCA_TX_PREPARED : TX_PREPARED)
537             .putLong(localTransactionId)
538             .putShort((short) (resourceCount + 1))
539             .putInt(inboundFormatId)
540             .putShort((short) globalTxIdLen)
541             .put(globalTransactionId);
542
543       int offset = buffer.position();
544       int length = recoveryCoordOrInboundBranchQual.length;
545
546       buffer.put(recoveryCoordOrInboundBranchQual)
547             .putShort(recordLen - CHKSUM_LEN - SIZEOF_SHORT, (short) length)
548             .putShort(recordLen - CHKSUM_LEN - SIZEOF_DIR_ENTRY,
549                       (short) offset);
550
551       for (int i = 0; i < resourceCount; )
552       {
553          offset = buffer.position();
554          length = resources[i].length();
555          byte[] byteArray = new byte[length];
556
557          resources[i].getBytes(0, length, byteArray, 0);
558          i++; // increment i *before* storing the entry (because entry 0 is
559
// the recovery coordinator or inbound branch qualifier)
560
buffer.put(byteArray)
561                .putShort(recordLen
562                          - CHKSUM_LEN - SIZEOF_SHORT - SIZEOF_DIR_ENTRY * i,
563                          (short) length)
564                .putShort(recordLen
565                          - CHKSUM_LEN - SIZEOF_DIR_ENTRY - SIZEOF_DIR_ENTRY * i,
566                          (short) offset);
567       }
568
569       Checksum JavaDoc checksum = new Adler32 JavaDoc();
570       checksum.update(buffer.array(),
571                       FULL_HEADER_LEN,
572                       recordLen - FULL_HEADER_LEN - CHKSUM_LEN);
573       buffer.putInt(recordLen - CHKSUM_LEN, (int) checksum.getValue());
574
575       return (ByteBuffer JavaDoc) buffer.position(0);
576    }
577
578    /**
579     * Creates a tx prepared record for a distributed transaction.
580     *
581     * @param localTransactionId the local id of the transaction
582     * @param inboundFormatId the format id of the foreign <code>Xid</code>
583     * @param globalTransactionId the global id of the transaction
584     * @param recoveryCoordinator a stringfied reference for the remote
585     * coordinator, an
586     * <code>org.jboss.tm.remoting.RecoveryCoordinator</code>
587     * instance
588     * @param resources an array of stringfied references for the remote
589     * resources (<code>org.jboss.tm.remoting.Resource</code>
590     * instances) enlisted in the transaction.
591     * @return a <code>ByteBuffer</code> containing the tx prepared record.
592     * The buffer position is set to zero and the buffer limit is set
593     * to the number of bytes in the record.
594     */

595    static ByteBuffer JavaDoc createTxPreparedRecord(long localTransactionId,
596                                             int inboundFormatId,
597                                             byte[] globalTransactionId,
598                                             String JavaDoc recoveryCoordinator,
599                                             String JavaDoc[] resources)
600    {
601       int len = recoveryCoordinator.length();
602       byte[] coordinatorByteArray = new byte[len];
603
604       recoveryCoordinator.getBytes(0, len, coordinatorByteArray, 0);
605       
606       return createTxPreparedRecord(localTransactionId,
607                                     inboundFormatId,
608                                     globalTransactionId,
609                                     false /* not JCA inbound */,
610                                     coordinatorByteArray,
611                                     resources);
612    }
613    
614    /**
615     * Creates a tx prepared record for a JCA inbound transaction.
616     *
617     * @param localTransactionId the local id of the transaction
618     * @param inboundXid a foreign <code>Xid</code> instance
619     * @param resources an array of stringfied references for the remote
620     * resources (<code>org.jboss.tm.remoting.Resource</code>
621     * instances) enlisted in the transaction.
622     * @return a <code>ByteBuffer</code> containing the JCA tx prepared record.
623     * The buffer position is set to zero and the buffer limit is set
624     * to the number of bytes in the record.
625     */

626    static ByteBuffer JavaDoc createJcaTxPreparedRecord(long localTransactionId,
627                                                Xid JavaDoc inboundXid,
628                                                String JavaDoc[] resources)
629    {
630       return createTxPreparedRecord(localTransactionId,
631                                     inboundXid.getFormatId(),
632                                     inboundXid.getGlobalTransactionId(),
633                                     true /* JCA inbound */,
634                                     inboundXid.getBranchQualifier(),
635                                     resources);
636    }
637
638    /**
639     * Creates a tx end record for a distributed transaction.
640     *
641     * @param localTransactionId the local id of the transaction.
642     * @return a <code>ByteBuffer</code> containing the end record. The
643     * buffer position is set to zero and the buffer limit is set to
644     * the number of bytes in the end record.
645     */

646    static ByteBuffer JavaDoc createTxEndRecord(long localTransactionId)
647    {
648       ByteBuffer JavaDoc buffer = ByteBuffer.allocate(TX_END_LEN);
649       
650       buffer.put(HEADER)
651             .putShort((short) (TX_END_LEN - FULL_HEADER_LEN))
652             .putShort((short) -(TX_END_LEN - FULL_HEADER_LEN))
653             .put(TX_END)
654             .putLong(localTransactionId);
655       
656       Checksum JavaDoc checksum = new Adler32 JavaDoc();
657       checksum.update(buffer.array(),
658                       FULL_HEADER_LEN,
659                       SIZEOF_BYTE + SIZEOF_LONG);
660       buffer.putInt(TX_END_LEN - CHKSUM_LEN, (int) checksum.getValue());
661
662       return (ByteBuffer JavaDoc) buffer.position(0);
663    }
664
665    /**
666     * Creates a heuristic status record for a transaction.
667     *
668     * @param localTransactionId the local id of the transaction
669     * @param foreignTx true if the transaction is a foreign one, false otherwise
670     * @param formatId the format id field of the transaction's <code>Xid</code>
671     * @param globalTransactionId the global id field of the transaction's
672     * <code>Xid</code>
673     * @param inboundBranchQualifier the inbound branch qualifier, in the case
674     * of a foreign transaction that has been imported via JCA
675     * transaction inflow, or null otherwise
676     * @param transactionStatus the transaction status
677     * (<code>javax.transaction.Status.STATUS_COMMITTING</code>, or
678     * <code>javax.transaction.Status.STATUS_COMMITTED</code>, or
679     * <code>javax.transaction.Status.STATUS_ROLLING_BACK</code>, or
680     * <code>javax.transaction.Status.STATUS_ROLLEDBACK</code>)
681     * @param heurStatusCode the heuristic status code, which takes the same
682     * values as the <code>errorCode</code> field of
683     * <code>javax.transaction.xa.XAException</code>
684     * @param locallyDetectedHeuristicHazard true if a heuristic hazard was
685     * detected locally and is still outstanding
686     * @param xaResourceHeuristics array with the heuristic status codes of
687     * the XA resources that reported heuristic decisions,
688     * or null if no XA resources reported heuristic decisions
689     * @param remoteResourceHeuristics array with the heuristic status of
690     * the remote resources that reported heuristic decisions,
691     * or null if no remote resources reported heuristic
692     * decisions
693     * @return a <code>ByteBuffer</code> containing the heuristic status record.
694     * The buffer position is set to zero and the buffer limit is set
695     * to the number of bytes in the record.
696     */

697    static ByteBuffer JavaDoc createHeurStatusRecord(
698                                     long localTransactionId,
699                                     boolean foreignTx,
700                                     int formatId,
701                                     byte[] globalTransactionId,
702                                     byte[] inboundBranchQualifier,
703                                     int transactionStatus,
704                                     int heurStatusCode,
705                                     boolean locallyDetectedHeuristicHazard,
706                                     int[] xaResourceHeuristics,
707                                     HeuristicStatus[] remoteResourceHeuristics)
708    {
709       int recordLen = MIN_HEUR_STATUS_LEN;
710       int globalTxIdLen = 0;
711       int remoteResourceHeuristicsCount = 0;
712       
713       if (globalTransactionId != null)
714          recordLen += (globalTxIdLen = globalTransactionId.length);
715       if (inboundBranchQualifier != null)
716          recordLen += inboundBranchQualifier.length;
717       if (xaResourceHeuristics != null)
718          recordLen += xaResourceHeuristics.length;
719       if (remoteResourceHeuristics != null)
720       {
721          remoteResourceHeuristicsCount = remoteResourceHeuristics.length;
722          for (int i = 0; i < remoteResourceHeuristicsCount; i++)
723          {
724             recordLen += SIZEOF_DIR_ENTRY /* offset and length */
725                         + SIZEOF_BYTE /* data */
726                         + remoteResourceHeuristics[i].resourceRef.length();
727          }
728       }
729
730       ByteBuffer JavaDoc buffer = ByteBuffer.allocate(recordLen);
731       
732       buffer.put(HEADER)
733             .putShort((short) (recordLen - FULL_HEADER_LEN))
734             .putShort((short) -(recordLen - FULL_HEADER_LEN))
735             .put(HEUR_STATUS)
736             .putLong(localTransactionId)
737             .putShort((short) (remoteResourceHeuristicsCount + 2))
738             .put((byte) transactionStatus)
739             .put((byte) heurStatusCode)
740             .put((locallyDetectedHeuristicHazard) ? (byte) 1 : (byte) 0)
741             .put((foreignTx) ? (byte) 1 : (byte) 0)
742             .putInt(formatId)
743             .putShort((short) globalTxIdLen)
744             .put(globalTransactionId);
745
746       
747       int offset, length;
748       
749       // varField 0: inboundBranchQualifier
750
offset = buffer.position();
751       length = (inboundBranchQualifier == null) ? 0
752                                                 : inboundBranchQualifier.length;
753       if (length > 0)
754          buffer.put(inboundBranchQualifier);
755       buffer.putShort(recordLen - CHKSUM_LEN - SIZEOF_SHORT, (short) length)
756             .putShort(recordLen - CHKSUM_LEN - SIZEOF_DIR_ENTRY, (short) offset);
757       
758       // varField 1: xaResourceHeuristics
759
offset = buffer.position();
760       length = (xaResourceHeuristics == null) ? 0
761                                                 : xaResourceHeuristics.length;
762       if (length > 0)
763       {
764          byte[] xaResHeurCodes = new byte[length];
765          for (int i = 0; i < length; i++)
766             xaResHeurCodes[i] = (byte) xaResourceHeuristics[i];
767          buffer.put(xaResHeurCodes);
768       }
769       buffer.putShort(recordLen - CHKSUM_LEN
770                                 - SIZEOF_SHORT - SIZEOF_DIR_ENTRY,
771                       (short) length)
772             .putShort(recordLen - CHKSUM_LEN
773                                 - SIZEOF_DIR_ENTRY - SIZEOF_DIR_ENTRY,
774                       (short) offset);
775       
776       // varField 2, ... : remoteResourceHeuristics
777
for (int i = 0 ; i < remoteResourceHeuristicsCount; i++)
778       {
779          String JavaDoc resourceRef = remoteResourceHeuristics[i].resourceRef;
780          offset = buffer.position();
781          length = resourceRef.length() + 1;
782          byte[] heurStatus = new byte[length];
783          heurStatus[0] = (byte) remoteResourceHeuristics[i].code;
784          resourceRef.getBytes(0, resourceRef.length(), heurStatus, 1);
785          buffer.put(heurStatus)
786                .putShort(recordLen - CHKSUM_LEN
787                                    - SIZEOF_SHORT - SIZEOF_DIR_ENTRY * (i + 2),
788                          (short) length)
789                .putShort(recordLen - CHKSUM_LEN
790                                    - SIZEOF_DIR_ENTRY - SIZEOF_DIR_ENTRY * (i + 2),
791                          (short) offset);
792       }
793
794       Checksum JavaDoc checksum = new Adler32 JavaDoc();
795       checksum.update(buffer.array(),
796                       FULL_HEADER_LEN,
797                       recordLen - FULL_HEADER_LEN - CHKSUM_LEN);
798       buffer.putInt(recordLen - CHKSUM_LEN, (int) checksum.getValue());
799       
800       return (ByteBuffer JavaDoc) buffer.position(0);
801    }
802    
803    /**
804     * Creates a heuristic forgotten record for a transaction.
805     *
806     * @param localTransactionId the local id of the transaction.
807     * @return a <code>ByteBuffer</code> containing the heuristic forgotten
808     * record. The buffer position is set to zero and the buffer limit
809     * is set to the number of bytes in the heuristic forgotten record.
810     */

811    static ByteBuffer JavaDoc createHeurForgottenRecord(long localTransactionId)
812    {
813       ByteBuffer JavaDoc buffer = ByteBuffer.allocate(HEUR_FORGOTTEN_LEN);
814       
815       buffer.put(HEADER)
816             .putShort((short) (HEUR_FORGOTTEN_LEN - FULL_HEADER_LEN))
817             .putShort((short) -(HEUR_FORGOTTEN_LEN - FULL_HEADER_LEN))
818             .put(HEUR_FORGOTTEN)
819             .putLong(localTransactionId);
820       
821       Checksum JavaDoc checksum = new Adler32 JavaDoc();
822       checksum.update(buffer.array(),
823                       FULL_HEADER_LEN,
824                       SIZEOF_BYTE + SIZEOF_LONG);
825       buffer.putInt(TX_END_LEN - CHKSUM_LEN, (int) checksum.getValue());
826
827       return (ByteBuffer JavaDoc) buffer.position(0);
828    }
829    
830    /**
831     * Fills out a <code>Data</code> structure with the information taken from
832     * the log record in a given <code>ByteBuffer</code>. The log record starts
833     * at the beginning of the buffer. It cannot be a log record with heuristic
834     * information (i.e., its type cannot be <code>HEUR_STATUS</code> or
835     * <code>HEUR_FORGOTTEN</code>). Its length is known in advance and may
836     * be smaller than the number of bytes in the buffer. The magic header and
837     * record length fields are not included in the <code>ByteBuffer</code>,
838     * whose first byte is the record type of the log record.
839     *
840     * @param buffer a <code>ByteBuffer</code> containing the part of a log
841     * record that starts at the record type field (which is the
842     * first byte of the <code>ByteBuffer</code> and goes until
843     * the checksum field (which is included in the
844     * <code>ByteBuffer</code>
845     * @param recLen the length of the log record in the buffer
846     * @param data a <code>Data</code> structure to be filled out with the
847     * information extracted from the log record.
848     */

849    static void getData(ByteBuffer JavaDoc buffer, int recLen, Data data)
850    {
851       short gidLength;
852       short countOfDirEntries;
853       
854       if (recLen > buffer.limit())
855          return; // TODO: throw an exception in this case
856

857       int checksumField = buffer.getInt(recLen - CHKSUM_LEN);
858
859       Checksum JavaDoc checksum = new Adler32 JavaDoc();
860       checksum.update(buffer.array(), 0, recLen - CHKSUM_LEN);
861       
862       if ((int) checksum.getValue() != checksumField)
863       {
864          throw new CorruptedLogRecordException("Wrong checksum.");
865       }
866
867       data.recordType = buffer.get();
868       
869       switch (data.recordType)
870       {
871       case MULTI_TM_TX_COMMITTED:
872          data.localTransactionId = buffer.getLong();
873          countOfDirEntries = buffer.getShort();
874
875          // Get varField 0, ..., varField N (where N = countOfDirEntries - 1)
876
data.resources = new String JavaDoc[countOfDirEntries];
877          for (int i = 0; i < countOfDirEntries; i++)
878          {
879             short length = buffer.getShort(recLen - CHKSUM_LEN
880                                                   - SIZEOF_SHORT
881                                                   - SIZEOF_DIR_ENTRY * i);
882             short offset = buffer.getShort(recLen - CHKSUM_LEN
883                                                   - SIZEOF_DIR_ENTRY
884                                                   - SIZEOF_DIR_ENTRY * i);
885             offset -= FULL_HEADER_LEN; // Our buffer starts after the header!
886
data.resources[i] = new String JavaDoc(buffer.array(), 0, offset, length);
887          }
888
889          // Nullify unused data fields
890
data.globalTransactionId = null;
891          data.inboundFormatId = -1;
892          data.recoveryCoordinator = null;
893          data.inboundBranchQualifier = null;
894          break;
895          
896       case TX_PREPARED:
897       case JCA_TX_PREPARED:
898          data.localTransactionId = buffer.getLong();
899          countOfDirEntries = buffer.getShort();
900          data.inboundFormatId = buffer.getInt();
901          gidLength = buffer.getShort();
902          data.globalTransactionId = new byte[gidLength];
903          buffer.get(data.globalTransactionId);
904          
905          // Get lentgh and offset of varField 0:
906
short length = buffer.getShort(recLen - CHKSUM_LEN - SIZEOF_SHORT);
907          short offset = buffer.getShort(recLen - CHKSUM_LEN - SIZEOF_DIR_ENTRY);
908          offset -= FULL_HEADER_LEN; // Our buffer starts after the header!
909
// (Alternatively we could have set offset to buffer.position(),
910
// as varField 0 cames immediately after the globalTransactionId.)
911

912          if (data.recordType == TX_PREPARED)
913          {
914             // varField 0 is the recovery coordinator
915
data.recoveryCoordinator =
916                new String JavaDoc(buffer.array(), 0, offset, length);
917             data.inboundBranchQualifier = null;
918          }
919          else
920          {
921             // varField 0 is the inbound branch qualifier
922
data.recoveryCoordinator = null;
923             data.inboundBranchQualifier = new byte[length];
924             // At this point (offset == buffer.position),
925
// so the line below is commented out:
926
// buffer.position(offset);
927
buffer.get(data.inboundBranchQualifier);
928          }
929          
930          // Get varField 1, ... varField N (where N == countOfDirEntries - 1)
931
data.resources = new String JavaDoc[countOfDirEntries - 1];
932          for (int i = 1; i < countOfDirEntries; i++)
933          {
934             length = buffer.getShort(recLen - CHKSUM_LEN
935                                             - SIZEOF_SHORT
936                                             - SIZEOF_DIR_ENTRY * i);
937             offset = buffer.getShort(recLen - CHKSUM_LEN
938                                             - SIZEOF_DIR_ENTRY
939                                             - SIZEOF_DIR_ENTRY * i);
940             offset -= FULL_HEADER_LEN; // Our buffer starts after the header!
941
data.resources[i - 1] =
942                new String JavaDoc(buffer.array(), 0, offset, length);
943          }
944          break;
945          
946       case TX_COMMITTED:
947       case TX_END:
948          data.localTransactionId = buffer.getLong();
949          // Nullify the remaining data fields
950
data.globalTransactionId = null;
951          data.inboundFormatId = -1;
952          data.recoveryCoordinator = null;
953          data.inboundBranchQualifier = null;
954          data.resources = null;
955          break;
956          
957       case HEUR_STATUS:
958       case HEUR_FORGOTTEN:
959          throw new RuntimeException JavaDoc("Log record with unexpected type");
960          
961       default:
962          throw new RuntimeException JavaDoc("Log record with invalid type");
963       }
964    }
965
966    /**
967     * Fills out a <code>HeurData</code> structure with the information taken
968     * from the <code>HEUR_STATUS</code> or <code>HEUR_FORGOTTEN</code> log
969     * record in a given <code>ByteBuffer</code>. The log record starts at the
970     * beginning of the buffer. Its length is known in advance and may be
971     * smaller than the number of bytes in the buffer. The magic header and
972     * record length fields are not included in the <code>ByteBuffer</code>,
973     * whose first byte is the record type of the log record.
974     *
975     * @param buffer a <code>ByteBuffer</code> containing the part of a
976     * <code>HEUR_STATUS</code> or <code>HEUR_FORGOTTEN</code> log
977     * record that starts at the record type field (which is the
978     * first byte of the <code>ByteBuffer</code> and goes until
979     * the checksum field (which is included in the
980     * <code>ByteBuffer</code>
981     * @param recLen the length of the log record in the buffer
982     * @param data a <code>HeurData</code> structure to be filled out with the
983     * information extracted from the log record.
984     */

985    static void getHeurData(ByteBuffer JavaDoc buffer, int recLen, HeurData data)
986    {
987       if (recLen > buffer.limit())
988          return; // TODO: throw an exception in this case
989

990       int checksumField = buffer.getInt(recLen - CHKSUM_LEN);
991
992       Checksum JavaDoc checksum = new Adler32 JavaDoc();
993       checksum.update(buffer.array(), 0, recLen - CHKSUM_LEN);
994       
995       if ((int) checksum.getValue() != checksumField)
996       {
997          throw new CorruptedLogRecordException("Wrong checksum.");
998       }
999
1000      data.recordType = buffer.get();
1001      
1002      switch (data.recordType)
1003      {
1004      case HEUR_STATUS:
1005         data.localTransactionId = buffer.getLong();
1006         short countOfDirEntries = buffer.getShort();
1007         data.transactionStatus = buffer.get();
1008         data.heuristicStatusCode = buffer.get();
1009         data.locallyDetectedHeuristicHazard = (buffer.get() != 0);
1010         data.foreignTx = (buffer.get() != 0);
1011         data.formatId = buffer.getInt();
1012         int gidLength = buffer.getShort();
1013         data.globalTransactionId = new byte[gidLength];
1014         buffer.get(data.globalTransactionId);
1015         
1016         short length, offset;
1017         
1018         // Get lentgh and offset of varField 0:
1019
length = buffer.getShort(recLen - CHKSUM_LEN - SIZEOF_SHORT);
1020         offset = buffer.getShort(recLen - CHKSUM_LEN - SIZEOF_DIR_ENTRY);
1021         offset -= FULL_HEADER_LEN; // Our buffer starts after the header!
1022
// (Alternatively we could have set offset to buffer.position(),
1023
// as varField 0 cames immediately after the globalTransactionId.)
1024

1025         if (length == 0)
1026            data.inboundBranchQualifier = null;
1027         else
1028         {
1029            data.inboundBranchQualifier = new byte[length];
1030            // At this point (offset == buffer.position),
1031
// so the line below is commented out:
1032
// buffer.position(offset);
1033
buffer.get(data.inboundBranchQualifier);
1034         }
1035         
1036         // Get lentgh and offset of varField 1:
1037
length = buffer.getShort(recLen - CHKSUM_LEN - SIZEOF_SHORT - SIZEOF_DIR_ENTRY);
1038         offset = buffer.getShort(recLen - CHKSUM_LEN - SIZEOF_DIR_ENTRY - SIZEOF_DIR_ENTRY);
1039         offset -= FULL_HEADER_LEN; // Our buffer starts after the header!
1040

1041         if (length == 0)
1042            data.xaResourceHeuristics = null;
1043         else
1044         {
1045            byte[] xaResHeurCodes = new byte[length];
1046            buffer.position(offset);
1047            buffer.get(xaResHeurCodes);
1048            data.xaResourceHeuristics = new int[length];
1049            for (int i = 0; i < length; i++)
1050               data.xaResourceHeuristics[i] = xaResHeurCodes[i];
1051         }
1052         
1053         if (countOfDirEntries > 2)
1054            data.remoteResourceHeuristics =
1055               new HeuristicStatus[countOfDirEntries - 2];
1056         else
1057            data.remoteResourceHeuristics = null;
1058         
1059         // Get varField 2, ... varField N (where N == countOfDirEntries - 1)
1060
for (int i = 2; i < countOfDirEntries; i++)
1061         {
1062            length = buffer.getShort(recLen - CHKSUM_LEN
1063                                            - SIZEOF_SHORT
1064                                            - SIZEOF_DIR_ENTRY * i);
1065            offset = buffer.getShort(recLen - CHKSUM_LEN
1066                                            - SIZEOF_DIR_ENTRY
1067                                            - SIZEOF_DIR_ENTRY * i);
1068            offset -= FULL_HEADER_LEN; // Our buffer starts after the header!
1069

1070            byte remoteResourceHeurCode = buffer.get(offset);
1071            String JavaDoc remoteResourceRef =
1072               new String JavaDoc(buffer.array(), 0, offset + 1, length - 1);
1073            data.remoteResourceHeuristics[i - 2] =
1074               new HeuristicStatus(remoteResourceHeurCode, remoteResourceRef);
1075         }
1076         break;
1077         
1078      case HEUR_FORGOTTEN:
1079         data.localTransactionId = buffer.getLong();
1080         data.heuristicStatusCode = 0;
1081         data.xaResourceHeuristics = null;
1082         data.remoteResourceHeuristics = null;
1083         break;
1084         
1085      case MULTI_TM_TX_COMMITTED:
1086      case TX_PREPARED:
1087      case JCA_TX_PREPARED:
1088      case TX_COMMITTED:
1089      case TX_END:
1090         throw new RuntimeException JavaDoc("Log record with unexpected type");
1091         
1092      default:
1093         throw new RuntimeException JavaDoc("Log record with invalid type");
1094         
1095      }
1096   }
1097   
1098   /**
1099    * Gets the lenght of the log record that follows the one at the beginning
1100    * of a given buffer. This method assumes that the header, record lenght,
1101    * and record length check of the next record follows the current record
1102    * in the buffer.
1103    *
1104    * @param buffer a <code>ByteBuffer</code> containing the header, record
1105    * lenght, and record length check of the next log record
1106    * @param currentRecordLen the buffer position at which the header starts.
1107    * @return the next record length, a short value read from the absolute
1108    * buffer position <code>currentRecordLength + HEADER_LEN</code>
1109    *
1110    */

1111   static int getNextRecordLength(ByteBuffer JavaDoc buffer, int currentRecordLength)
1112   {
1113      buffer.position(currentRecordLength);
1114      if (buffer.remaining() < FULL_HEADER_LEN)
1115         return 0;
1116      else
1117      {
1118         byte[] header = new byte[HEADER_LEN];
1119         buffer.get(header);
1120         if (!Arrays.equals(header, HEADER))
1121         {
1122            if (Arrays.equals(header, NULL_HEADER))
1123               return 0;
1124            else
1125               throw new CorruptedLogRecordException("Invalid header.");
1126         }
1127         else
1128         {
1129            short recLen = buffer.getShort();
1130            short recLenCheck = buffer.getShort();
1131            if (recLenCheck != -recLen)
1132               throw new CorruptedLogRecordException("Record lenght check failed.");
1133            return recLen;
1134            
1135         }
1136      }
1137   }
1138   
1139   /**
1140    * Receives a byte array containing the part of a log record that follows
1141    * the header and verifies if the record has a valid checksum.
1142    *
1143    * @param buf a byte array containing the part of a log record that starts
1144    * with the record type and ends with the checksum, which is
1145    * included in the array.
1146    * @return true if the checksum is valid, false otherwise.
1147    */

1148   static boolean hasValidChecksum(byte[] buf)
1149   {
1150      int bufLen = buf.length;
1151      int checksumField = ByteBuffer.wrap(buf).getInt(bufLen - CHKSUM_LEN);
1152      Checksum JavaDoc checksum = new Adler32 JavaDoc();
1153      checksum.update(buf, 0, bufLen - CHKSUM_LEN);
1154      return ((int) checksum.getValue() == checksumField);
1155   }
1156
1157   /**
1158    * Returs the string representation of the log record in a buffer.
1159    *
1160    * @param buffer a <code>ByteBuffer</code> containing a log record,
1161    * with the buffer position set to zero and the buffer limit
1162    * set to the number of bytes in the commit record.
1163    * @return a string that describes the log record.
1164    */

1165   static String JavaDoc toString(ByteBuffer JavaDoc buffer)
1166   {
1167      short countOfDirEntries;
1168      short offset;
1169      short length;
1170      short gidLen;
1171      byte[] gid;
1172      
1173      int recLen = buffer.limit();
1174      buffer.position(FULL_HEADER_LEN);
1175      
1176      StringBuffer JavaDoc sb = new StringBuffer JavaDoc("Record Info:\n Type: ");
1177      byte recordType = buffer.get();
1178      
1179      switch (recordType)
1180      {
1181         case MULTI_TM_TX_COMMITTED:
1182            sb.append("MULTI_TM_TX_COMMITTED\n");
1183            sb.append(" Local transaction id: ");
1184            sb.append(buffer.getLong());
1185            sb.append("\n");
1186            countOfDirEntries = buffer.getShort();
1187            if (countOfDirEntries > 0)
1188            {
1189               sb.append(" Resources:\n");
1190               // Get varField 0, ..., varField N (where N = countOfDirEntries - 1)
1191
for (int i = 0; i < countOfDirEntries; i++)
1192               {
1193                  length = buffer.getShort(recLen - CHKSUM_LEN
1194                                                  - SIZEOF_SHORT
1195                                                  - SIZEOF_DIR_ENTRY * i);
1196                  offset = buffer.getShort(recLen - CHKSUM_LEN
1197                                                  - SIZEOF_DIR_ENTRY
1198                                                  - SIZEOF_DIR_ENTRY * i);
1199                  sb.append(" ");
1200                  sb.append(new String JavaDoc(buffer.array(), 0, offset, length));
1201                  sb.append("\n");
1202               }
1203            }
1204            break;
1205         case TX_PREPARED:
1206            sb.append("TX_PREPARED\n");
1207            sb.append(" Local transaction id: ");
1208            sb.append(buffer.getLong());
1209            sb.append("\n");
1210            countOfDirEntries = buffer.getShort();
1211            sb.append(" Inbound format id: ");
1212            sb.append(buffer.getInt());
1213            sb.append("\n");
1214            gidLen = buffer.getShort();
1215            sb.append(" Global transaction id: ");
1216            gid = new byte[gidLen];
1217            buffer.get(gid);
1218            sb.append(new String JavaDoc(gid, 0));
1219            sb.append("\n");
1220            sb.append(" Recovery coordinator: ");
1221            // Get varField 0
1222
length = buffer.getShort(recLen - CHKSUM_LEN - SIZEOF_SHORT);
1223            offset = buffer.getShort(recLen - CHKSUM_LEN - SIZEOF_DIR_ENTRY);
1224            sb.append(new String JavaDoc(buffer.array(), 0, offset, length));
1225            sb.append("\n");
1226            if (countOfDirEntries > 1)
1227            {
1228               sb.append(" Resources:\n");
1229               // Get varField 1, ..., varField N (where N = countOfDirEntries - 1)
1230
for (int i = 1; i < countOfDirEntries; i++)
1231               {
1232                  length = buffer.getShort(recLen - CHKSUM_LEN
1233                                                  - SIZEOF_SHORT
1234                                                  - SIZEOF_DIR_ENTRY * i);
1235                  offset = buffer.getShort(recLen - CHKSUM_LEN
1236                                                  - SIZEOF_DIR_ENTRY
1237                                                  - SIZEOF_DIR_ENTRY * i);
1238                  sb.append(" ");
1239                  sb.append(new String JavaDoc(buffer.array(), 0, offset, length));
1240               }
1241            }
1242            break;
1243         case JCA_TX_PREPARED:
1244            sb.append("JCA_TX_PREPARED\n");
1245            sb.append(" Local transaction id: ");
1246            sb.append(buffer.getLong());
1247            sb.append("\n");
1248            countOfDirEntries = buffer.getShort();
1249            sb.append(" Inbound format id: ");
1250            sb.append(buffer.getInt());
1251            sb.append("\n");
1252            gidLen = buffer.getShort();
1253            sb.append(" Global transaction id: ");
1254            gid = new byte[gidLen];
1255            buffer.get(gid);
1256            sb.append(new String JavaDoc(gid, 0));
1257            sb.append("\n");
1258            sb.append(" Inbound branch qualifier: ");
1259            // Get varField 0
1260
length = buffer.getShort(recLen - CHKSUM_LEN - SIZEOF_SHORT);
1261            offset = buffer.getShort(recLen - CHKSUM_LEN - SIZEOF_DIR_ENTRY);
1262            sb.append(new String JavaDoc(buffer.array(), 0, offset, length));
1263            sb.append("\n");
1264            if (countOfDirEntries > 1)
1265            {
1266               sb.append(" Resources:\n");
1267               // Get varField 1, ..., varField N (where N = countOfDirEntries - 1)
1268
for (int i = 1; i < countOfDirEntries; i++)
1269               {
1270                  length = buffer.getShort(recLen - CHKSUM_LEN
1271                                                  - SIZEOF_SHORT
1272                                                  - SIZEOF_DIR_ENTRY * i);
1273                  offset = buffer.getShort(recLen - CHKSUM_LEN
1274                                                  - SIZEOF_DIR_ENTRY
1275                                                  - SIZEOF_DIR_ENTRY * i);
1276                  sb.append(" ");
1277                  sb.append(new String JavaDoc(buffer.array(), 0, offset, length));
1278               }
1279            }
1280            break;
1281         case TX_COMMITTED:
1282            sb.append("TX_COMMITTED\n");
1283            sb.append(" Local transaction id: ");
1284            sb.append(buffer.getLong());
1285            sb.append("\n");
1286            break;
1287         case TX_END:
1288            sb.append("TX_END\n");
1289            sb.append(" Local transaction id: ");
1290            sb.append(buffer.getLong());
1291            sb.append("\n");
1292            break;
1293         case HEUR_STATUS:
1294            sb.append("HEUR_STATUS\n");
1295            sb.append(" Local transaction id: ");
1296            sb.append(buffer.getLong());
1297            sb.append("\n");
1298            countOfDirEntries = buffer.getShort();
1299            sb.append(" Transaction status: ");
1300            byte status = buffer.get();
1301            sb.append(TxUtils.getStatusAsString(status));
1302            sb.append("\n");
1303            sb.append(" Heuristic status: ");
1304            byte heurCode = buffer.get();
1305            if (heurCode != 0)
1306               sb.append(TxUtils.getXAErrorCodeAsString(heurCode));
1307            else
1308               sb.append("NONE");
1309            sb.append("\n");
1310            sb.append(" Locally-detected heuristic hazard: ");
1311            sb.append((buffer.get() != 0) ? "yes" : "no");
1312            sb.append("\n");
1313            sb.append(" Foreign transaction: ");
1314            boolean foreignTransaction = (buffer.get() != 0);
1315            sb.append((foreignTransaction) ? "yes" : "no");
1316            sb.append("\n");
1317            sb.append((foreignTransaction) ? " Inbound format id: "
1318                                           : " Format id: ");
1319            sb.append(buffer.getInt());
1320            sb.append("\n");
1321            gidLen = buffer.getShort();
1322            sb.append(" Global transaction id: ");
1323            gid = new byte[gidLen];
1324            buffer.get(gid);
1325            sb.append(new String JavaDoc(gid, 0));
1326            sb.append("\n");
1327            // Get varField 0
1328
length = buffer.getShort(recLen - CHKSUM_LEN - SIZEOF_SHORT);
1329            if (length > 0)
1330            {
1331               sb.append(" Inbound branch qualifier: ");
1332               offset = buffer.getShort(recLen - CHKSUM_LEN - SIZEOF_DIR_ENTRY);
1333               sb.append(new String JavaDoc(buffer.array(), 0, offset, length));
1334               sb.append("\n");
1335            }
1336            // Get varField 1
1337
length = buffer.getShort(recLen - CHKSUM_LEN
1338                                            - SIZEOF_SHORT
1339                                            - SIZEOF_DIR_ENTRY);
1340            if (length > 0)
1341            {
1342               offset = buffer.getShort(recLen - CHKSUM_LEN
1343                                               - SIZEOF_DIR_ENTRY
1344                                               - SIZEOF_DIR_ENTRY);
1345               sb.append(" XAResource heuristics:\n");
1346               for (int i = 0; i < length; i++)
1347               {
1348                  sb.append(" ");
1349                  heurCode = buffer.get(offset + i);
1350                  sb.append(TxUtils.getXAErrorCodeAsString(heurCode));
1351                  sb.append("\n");
1352               }
1353            }
1354            // Get varField 2, ..., varField N (where N = countOfDirEntries - 1)
1355
if (countOfDirEntries > 2)
1356            {
1357               sb.append(" Remote resource heuristics:\n");
1358               for (int i = 2; i < countOfDirEntries; i++)
1359               {
1360                  length = buffer.getShort(recLen - CHKSUM_LEN
1361                                                  - SIZEOF_SHORT
1362                                                  - SIZEOF_DIR_ENTRY * i);
1363                  offset = buffer.getShort(recLen - CHKSUM_LEN
1364                                                  - SIZEOF_DIR_ENTRY
1365                                                  - SIZEOF_DIR_ENTRY * i);
1366                  heurCode = buffer.get(offset);
1367                  sb.append(" ");
1368                  sb.append(TxUtils.getXAErrorCodeAsString(heurCode));
1369                  sb.append(" - ");
1370                  sb.append(new String JavaDoc(buffer.array(), 0,
1371                                       offset + 1, length - 1));
1372                  sb.append("\n");
1373               }
1374            }
1375            break;
1376         case HEUR_FORGOTTEN:
1377            sb.append("HEUR_FORGOTTEN\n");
1378            sb.append("Local transaction id: ");
1379            sb.append(buffer.getLong(FULL_HEADER_LEN + SIZEOF_BYTE));
1380            sb.append("\n");
1381            break;
1382         default:
1383            sb.append("INVALID\n");
1384            break;
1385      }
1386      buffer.position(0);
1387      return sb.toString();
1388   }
1389}
1390
Popular Tags