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