View Javadoc

    1   /***
    2    *
    3    * Copyright 2004 Hiram Chirino
    4    *
    5    * Licensed under the Apache License, Version 2.0 (the "License");
    6    * you may not use this file except in compliance with the License.
    7    * You may obtain a copy of the License at
    8    *
    9    * http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   **/
    18  package org.codehaus.activemq.message;
    19
    20  import org.apache.commons.logging.Log;
    21  import org.apache.commons.logging.LogFactory;
    22
    23  import javax.jms.JMSException;
    24  import javax.transaction.xa.Xid;
    25  import java.io.*;
    26
    27  /***
    28   * <P>
    29   * A <CODE>ActiveMQXid</CODE> object holds the distributed
    30   * transaction id that is passed around in an ActiveMQ system.
    31   * <P>
    32   * Eventhough a Transaction Manager (TM) has his own Xid implementation
    33   * that he uses when he talks to the our ActiveMQXAResource, we need to
    34   * send the Xid data down to the server in our format.
    35   * <P>
    36   * ActiveMQ uses Strings as the transaction id.  This class coverts an
    37   * Xid to and from a string.
    38   * <p/>
    39   * <P>
    40   *
    41   * @version $Revision: 1.11 $
    42   * @see javax.transaction.xa.Xid
    43   */
    44  public class ActiveMQXid implements Xid, Externalizable, Comparable {
    45      private static final long serialVersionUID = -5754338187296859149L;
    46      private static final Log log = LogFactory.getLog(ActiveMQXid.class);
    47
    48      private int formatId;
    49      private byte[] branchQualifier;
    50      private byte[] globalTransactionId;
    51      private transient int hash;
    52
    53      private static final String[] HEX_TABLE = new String[]{
    54          "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a", "0b", "0c", "0d", "0e", "0f",
    55          "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f",
    56          "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2a", "2b", "2c", "2d", "2e", "2f",
    57          "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3a", "3b", "3c", "3d", "3e", "3f",
    58          "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4a", "4b", "4c", "4d", "4e", "4f",
    59          "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5a", "5b", "5c", "5d", "5e", "5f",
    60          "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6a", "6b", "6c", "6d", "6e", "6f",
    61          "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7a", "7b", "7c", "7d", "7e", "7f",
    62          "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8a", "8b", "8c", "8d", "8e", "8f",
    63          "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9a", "9b", "9c", "9d", "9e", "9f",
    64          "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af",
    65          "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf",
    66          "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf",
    67          "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "da", "db", "dc", "dd", "de", "df",
    68          "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef",
    69          "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff",
    70      };
    71
    72
    73      /***
    74       * Deserializes the data into an Xid
    75       *
    76       * @param data
    77       * @return
    78       */
    79      public static ActiveMQXid fromBytes(byte[] data) throws IOException {
    80          return read(new DataInputStream(new ByteArrayInputStream(data)));
    81      }
    82
    83      /***
    84       * This constructor is only used for serialization
    85       */
    86      public ActiveMQXid() {
    87      }
    88
    89      /***
    90       * Creates a new ActiveMQXid object with the Xid data
    91       * contained in <code>xid</code>
    92       */
    93      public ActiveMQXid(Xid xid) {
    94          this.formatId = xid.getFormatId();
    95          this.branchQualifier = xid.getBranchQualifier();
    96          this.globalTransactionId = xid.getGlobalTransactionId();
    97      }
    98
    99      public ActiveMQXid(int formatId, byte[] branchQualifier, byte[] globalTransactionId) {
    100         this.formatId = formatId;
    101         this.branchQualifier = branchQualifier;
    102         this.globalTransactionId = globalTransactionId;
    103     }
    104
    105     /***
    106      * Creates a new ActiveMQXid object.
    107      */
    108     public ActiveMQXid(String txid) throws JMSException {
    109         String parts[] = txid.split(":", 3);
    110         if (parts.length != 3) {
    111             throw new JMSException("Invalid XID: " + txid);
    112         }
    113         formatId = Integer.parseInt(parts[0]);
    114
    115         if (log.isDebugEnabled()) {
    116             log.debug("parts:" + parts[0]);
    117             log.debug("parts:" + parts[1]);
    118             log.debug("parts:" + parts[2]);
    119         }
    120         globalTransactionId = toBytesFromHex(parts[1]);
    121         branchQualifier = toBytesFromHex(parts[2]);
    122     }
    123
    124     public int hashCode() {
    125         if (hash == 0) {
    126             hash = formatId;
    127             hash = hash(branchQualifier, hash);
    128             hash = hash(globalTransactionId, hash);
    129         }
    130         if (hash == 0) {
    131             hash = 0xaceace;
    132         }
    133         return hash;
    134     }
    135
    136     public boolean equals(Object that) {
    137         if (this == that) {
    138             return true;
    139         }
    140         else if (hashCode() == that.hashCode() && that instanceof Xid) {
    141             return equals(this, (Xid)that);
    142         }
    143         return false;
    144     }
    145
    146     /***
    147      * Test for equivlance between two Xid
    148      * @param tis
    149      * @param that
    150      * @return
    151      */
    152     public static boolean equals(Xid tis,Xid that) {
    153         if ( tis == that){
    154             return true;
    155         }else if (tis == null || that == null){
    156             return false;
    157         }
    158         return tis.getFormatId() == that.getFormatId() && equals(tis.getBranchQualifier(), that.getBranchQualifier()) && equals(tis.getGlobalTransactionId(), that.getGlobalTransactionId());
    159     }
    160
    161     public int compareTo(Object object) {
    162         if (this == object) {
    163             return 0;
    164         }
    165         else {
    166             if (object instanceof ActiveMQXid) {
    167                 ActiveMQXid that = (ActiveMQXid) object;
    168                 int diff = this.formatId - that.formatId;
    169                 if (diff == 0) {
    170                     diff = compareTo(this.branchQualifier, that.branchQualifier);
    171                     if (diff == 0) {
    172                         diff = compareTo(this.globalTransactionId, that.globalTransactionId);
    173                     }
    174                 }
    175                 return diff;
    176             }
    177             else {
    178                 return -1;
    179             }
    180         }
    181     }
    182
    183     public String toLocalTransactionId() {
    184         StringBuffer rc = new StringBuffer(13 + globalTransactionId.length * 2 + branchQualifier.length * 2);
    185         rc.append(formatId);
    186         rc.append(":");
    187         rc.append(toHexFromBytes(globalTransactionId));
    188         rc.append(":");
    189         rc.append(toHexFromBytes(branchQualifier));
    190         return rc.toString();
    191     }
    192
    193     /***
    194      * @see javax.transaction.xa.Xid#getBranchQualifier()
    195      */
    196     public byte[] getBranchQualifier() {
    197         return branchQualifier;
    198     }
    199
    200     /***
    201      * @see javax.transaction.xa.Xid#getFormatId()
    202      */
    203     public int getFormatId() {
    204         return formatId;
    205     }
    206
    207     /***
    208      * @see javax.transaction.xa.Xid#getGlobalTransactionId()
    209      */
    210     public byte[] getGlobalTransactionId() {
    211         return globalTransactionId;
    212     }
    213
    214     /***
    215      * @see java.lang.Object#toString()
    216      */
    217     public String toString() {
    218         return "XID:" + toLocalTransactionId();
    219     }
    220
    221
    222     public void writeExternal(ObjectOutput out) throws IOException {
    223         write(out);
    224     }
    225
    226     public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    227         readState(in);
    228     }
    229
    230     public void readState(DataInput dataIn) throws IOException {
    231         formatId = dataIn.readInt();
    232         branchQualifier = readBytes(dataIn);
    233         globalTransactionId = readBytes(dataIn);
    234     }
    235
    236     /***
    237      * Reads the Xid from a stream
    238      *
    239      * @param dataIn
    240      * @return
    241      */
    242     public static ActiveMQXid read(DataInput dataIn) throws IOException {
    243         ActiveMQXid answer = new ActiveMQXid();
    244         answer.readState(dataIn);
    245         return answer;
    246     }
    247
    248     public byte[] toBytes() throws IOException {
    249         ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    250         write(new DataOutputStream(buffer));
    251         return buffer.toByteArray();
    252     }
    253
    254     /***
    255      * Writes the Xid to a stream
    256      *
    257      * @param dataOut
    258      */
    259     public void write(DataOutput dataOut) throws IOException {
    260         dataOut.writeInt(formatId);
    261         writeBytes(dataOut, branchQualifier);
    262         writeBytes(dataOut, globalTransactionId);
    263     }
    264
    265     protected void writeBytes(DataOutput dataOut, byte[] data) throws IOException {
    266         dataOut.writeInt(data.length);
    267         dataOut.write(data);
    268     }
    269
    270     protected static byte[] readBytes(DataInput dataIn) throws IOException {
    271         int size = dataIn.readInt();
    272         byte[] data = new byte[size];
    273         dataIn.readFully(data);
    274         return data;
    275     }
    276
    277
    278     public static boolean equals(byte[] left, byte[] right) {
    279         if (left == right) {
    280             return true;
    281         }
    282         int size = left.length;
    283         if (size != right.length) {
    284             return false;
    285         }
    286         for (int i = 0; i < size; i++) {
    287             if (left[i] != right[i]) {
    288                 return false;
    289             }
    290         }
    291         return true;
    292     }
    293
    294     protected int compareTo(byte[] left, byte[] right) {
    295         if (left == right) {
    296             return 0;
    297         }
    298         int size = left.length;
    299         int answer = size - right.length;
    300         if (answer == 0) {
    301             for (int i = 0; i < size; i++) {
    302                 answer = left[i] - right[i];
    303                 if (answer != 0) {
    304                     break;
    305                 }
    306             }
    307         }
    308         return answer;
    309     }
    310
    311     protected int hash(byte[] bytes, int hash) {
    312         for (int i = 0, size = bytes.length; i < size; i++) {
    313             hash ^= bytes[i] << ((i % 4) * 8);
    314         }
    315         return hash;
    316     }
    317
    318     /***
    319      * @param hex
    320      * @return
    321      */
    322     private byte[] toBytesFromHex(String hex) {
    323         byte rc[] = new byte[hex.length() / 2];
    324         for (int i = 0; i < rc.length; i++) {
    325             String h = hex.substring(i * 2, i * 2 + 2);
    326             int x = Integer.parseInt(h, 16);
    327             rc[i] = (byte) x;
    328         }
    329         return rc;
    330     }
    331
    332     /***
    333      * @param bytes
    334      * @return
    335      */
    336     private String toHexFromBytes(byte[] bytes) {
    337         StringBuffer rc = new StringBuffer(bytes.length * 2);
    338         for (int i = 0; i < bytes.length; i++) {
    339             rc.append(HEX_TABLE[0xFF & bytes[i]]);
    340         }
    341         return rc.toString();
    342     }
    343 }