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 return; 857 int checksumField = buffer.getInt(recLen - CHKSUM_LEN); 858 859 Checksum checksum = new Adler32 (); 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 data.resources = new String [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; data.resources[i] = new String (buffer.array(), 0, offset, length); 887 } 888 889 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 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; 912 if (data.recordType == TX_PREPARED) 913 { 914 data.recoveryCoordinator = 916 new String (buffer.array(), 0, offset, length); 917 data.inboundBranchQualifier = null; 918 } 919 else 920 { 921 data.recoveryCoordinator = null; 923 data.inboundBranchQualifier = new byte[length]; 924 buffer.get(data.inboundBranchQualifier); 928 } 929 930 data.resources = new String [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; data.resources[i - 1] = 942 new String (buffer.array(), 0, offset, length); 943 } 944 break; 945 946 case TX_COMMITTED: 947 case TX_END: 948 data.localTransactionId = buffer.getLong(); 949 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 ("Log record with unexpected type"); 960 961 default: 962 throw new RuntimeException ("Log record with invalid type"); 963 } 964 } 965 966 985 static void getHeurData(ByteBuffer buffer, int recLen, HeurData data) 986 { 987 if (recLen > buffer.limit()) 988 return; 990 int checksumField = buffer.getInt(recLen - CHKSUM_LEN); 991 992 Checksum checksum = new Adler32 (); 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 length = buffer.getShort(recLen - CHKSUM_LEN - SIZEOF_SHORT); 1020 offset = buffer.getShort(recLen - CHKSUM_LEN - SIZEOF_DIR_ENTRY); 1021 offset -= FULL_HEADER_LEN; 1025 if (length == 0) 1026 data.inboundBranchQualifier = null; 1027 else 1028 { 1029 data.inboundBranchQualifier = new byte[length]; 1030 buffer.get(data.inboundBranchQualifier); 1034 } 1035 1036 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; 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 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; 1070 byte remoteResourceHeurCode = buffer.get(offset); 1071 String remoteResourceRef = 1072 new String (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 ("Log record with unexpected type"); 1091 1092 default: 1093 throw new RuntimeException ("Log record with invalid type"); 1094 1095 } 1096 } 1097 1098 1111 static int getNextRecordLength(ByteBuffer 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 1148 static boolean hasValidChecksum(byte[] buf) 1149 { 1150 int bufLen = buf.length; 1151 int checksumField = ByteBuffer.wrap(buf).getInt(bufLen - CHKSUM_LEN); 1152 Checksum checksum = new Adler32 (); 1153 checksum.update(buf, 0, bufLen - CHKSUM_LEN); 1154 return ((int) checksum.getValue() == checksumField); 1155 } 1156 1157 1165 static String toString(ByteBuffer 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 sb = new StringBuffer ("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 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 (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 (gid, 0)); 1219 sb.append("\n"); 1220 sb.append(" Recovery coordinator: "); 1221 length = buffer.getShort(recLen - CHKSUM_LEN - SIZEOF_SHORT); 1223 offset = buffer.getShort(recLen - CHKSUM_LEN - SIZEOF_DIR_ENTRY); 1224 sb.append(new String (buffer.array(), 0, offset, length)); 1225 sb.append("\n"); 1226 if (countOfDirEntries > 1) 1227 { 1228 sb.append(" Resources:\n"); 1229 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 (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 (gid, 0)); 1257 sb.append("\n"); 1258 sb.append(" Inbound branch qualifier: "); 1259 length = buffer.getShort(recLen - CHKSUM_LEN - SIZEOF_SHORT); 1261 offset = buffer.getShort(recLen - CHKSUM_LEN - SIZEOF_DIR_ENTRY); 1262 sb.append(new String (buffer.array(), 0, offset, length)); 1263 sb.append("\n"); 1264 if (countOfDirEntries > 1) 1265 { 1266 sb.append(" Resources:\n"); 1267 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 (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 (gid, 0)); 1326 sb.append("\n"); 1327 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 (buffer.array(), 0, offset, length)); 1334 sb.append("\n"); 1335 } 1336 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 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 (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 |