| 1 22 package org.jboss.tm.recovery; 23 24 import java.nio.ByteBuffer ; 25 import java.util.Arrays ; 26 import java.util.zip.Adler32 ; 27 import java.util.zip.Checksum ; 28 29 import javax.transaction.xa.Xid ; 30 31 import org.jboss.tm.TxUtils; 32 33 209 public class LogRecord 210 { 211 212 public static final byte[] HEADER = "Log".getBytes(); 213 214 215 public static final int HEADER_LEN = HEADER.length; 216 217 218 private static final byte[] NULL_HEADER = {0, 0, 0}; 219 220 221 private static final int SIZEOF_BYTE = 1; 222 223 224 static final int SIZEOF_SHORT = 2; 225 226 227 private static final int SIZEOF_LONG = 8; 228 229 230 private static final int FORMAT_ID_LEN = 4; 231 232 233 private static final int CHKSUM_LEN = 4; 234 235 236 static final int FULL_HEADER_LEN = HEADER_LEN + 2 * SIZEOF_SHORT; 237 238 239 static final byte TX_COMMITTED = (byte) 'C'; 240 241 242 static final byte MULTI_TM_TX_COMMITTED = (byte) 'M'; 243 244 245 static final byte TX_PREPARED = (byte) 'P'; 246 247 248 static final byte JCA_TX_PREPARED = (byte) 'R'; 249 250 251 static final byte TX_END = (byte) 'E'; 252 253 254 static final byte HEUR_STATUS = (byte) 'H'; 255 256 257 static final byte HEUR_FORGOTTEN = (byte) 'F'; 258 259 260 private static final int SIZEOF_DIR_ENTRY = 261 SIZEOF_SHORT 262 + SIZEOF_SHORT; 263 264 268 private static final int MIN_MULTI_TM_TX_COMMITTED_LEN = 269 HEADER_LEN 270 + SIZEOF_SHORT 271 + SIZEOF_SHORT 272 + SIZEOF_BYTE 273 + SIZEOF_LONG 274 + SIZEOF_SHORT 275 + CHKSUM_LEN; 276 277 281 private static final int MIN_TX_PREPARED_LEN = 282 HEADER_LEN 283 + SIZEOF_SHORT 284 + SIZEOF_SHORT 285 + SIZEOF_BYTE 286 + SIZEOF_LONG 287 + SIZEOF_SHORT 288 + FORMAT_ID_LEN 289 + SIZEOF_SHORT 290 + CHKSUM_LEN; 291 292 296 private static final int TX_COMMITED_LEN = 297 HEADER_LEN 298 + SIZEOF_SHORT 299 + SIZEOF_SHORT 300 + SIZEOF_BYTE 301 + SIZEOF_LONG 302 + CHKSUM_LEN; 303 304 307 static final int TX_END_LEN = 308 HEADER_LEN 309 + SIZEOF_SHORT 310 + SIZEOF_SHORT 311 + SIZEOF_BYTE 312 + SIZEOF_LONG 313 + CHKSUM_LEN; 314 315 318 private static final int MIN_HEUR_STATUS_LEN = 319 HEADER_LEN 320 + SIZEOF_SHORT 321 + SIZEOF_SHORT 322 + SIZEOF_BYTE 323 + SIZEOF_LONG 324 + SIZEOF_SHORT 325 + SIZEOF_BYTE 326 + SIZEOF_BYTE 327 + SIZEOF_BYTE 328 + SIZEOF_BYTE 329 + FORMAT_ID_LEN 330 + SIZEOF_SHORT 331 + SIZEOF_DIR_ENTRY 332 + SIZEOF_DIR_ENTRY 333 + CHKSUM_LEN; 334 335 338 static final int HEUR_FORGOTTEN_LEN = 339 HEADER_LEN 340 + SIZEOF_SHORT 341 + SIZEOF_SHORT 342 + SIZEOF_BYTE 343 + SIZEOF_LONG 344 + CHKSUM_LEN; 345 346 347 348 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 recoveryCoordinator; 358 public byte[] inboundBranchQualifier; 359 public String [] resources; 360 } 361 362 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 383 private LogRecord() 384 { 385 } 386 387 396 static ByteBuffer createTxCommittedRecord(long localTransactionId) 397 { 398 ByteBuffer 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 checksum = new Adler32 (); 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 ) buffer.position(0); 413 } 414 415 426 static ByteBuffer createTxCommittedRecord(long localTransactionId, 427 String [] 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 437 + resources[i].length(); 438 } 439 } 440 else 441 throw new RuntimeException ("No remote resources were specified"); 442 443 ByteBuffer 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 checksum = new Adler32 (); 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 ) buffer.position(0); 475 } 476 477 498 private static ByteBuffer createTxPreparedRecord( 499 long localTransactionId, 500 int inboundFormatId, 501 byte[] globalTransactionId, 502 boolean jcaInboundTransaction, 503 byte[] recoveryCoordOrInboundBranchQual, 504 String [] 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 ("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 524 + resources[i].length(); 525 } 526 } 527 528 recordLen += SIZEOF_DIR_ENTRY 529 + recoveryCoordOrInboundBranchQual.length; 530 531 ByteBuffer 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++; 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 checksum = new Adler32 (); 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 ) buffer.position(0); 576 } 577 578 595 static ByteBuffer createTxPreparedRecord(long localTransactionId, 596 int inboundFormatId, 597 byte[] globalTransactionId, 598 String recoveryCoordinator, 599 String [] 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 , 610 coordinatorByteArray, 611 resources); 612 } 613 614 626 static ByteBuffer createJcaTxPreparedRecord(long localTransactionId, 627 Xid inboundXid, 628 String [] resources) 629 { 630 return createTxPreparedRecord(localTransactionId, 631 inboundXid.getFormatId(), 632 inboundXid.getGlobalTransactionId(), 633 true , 634 inboundXid.getBranchQualifier(), 635 resources); 636 } 637 638 646 static ByteBuffer createTxEndRecord(long localTransactionId) 647 { 648 ByteBuffer 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 checksum = new Adler32 (); 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 ) buffer.position(0); 663 } 664 665 697 static ByteBuffer 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 725 + SIZEOF_BYTE 726 + remoteResourceHeuristics[i].resourceRef.length(); 727 } 728 } 729 730 ByteBuffer 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 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 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 for (int i = 0 ; i < remoteResourceHeuristicsCount; i++) 778 { 779 String 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 checksum = new Adler32 (); 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 ) buffer.position(0); 801 } 802 803 811 static ByteBuffer createHeurForgottenRecord(long localTransactionId) 812 { 813 ByteBuffer 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 checksum = new Adler32 (); 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 ) buffer.position(0); 828 } 829 830 849 static void getData(ByteBuffer buffer, int recLen, Data data) 850 { 851 short gidLength; 852 short countOfDirEntries; 853 854 if (recLen > buffer.limit()) 855
|