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.8 $
    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 ActiveMQXid) {
    141             return equals((ActiveMQXid) that);
    142         }
    143         return false;
    144     }
    145
    146     public boolean equals(ActiveMQXid that) {
    147         return this.formatId == that.formatId && equals(this.branchQualifier, that.branchQualifier) && equals(this.globalTransactionId, that.globalTransactionId);
    148     }
    149
    150     public int compareTo(Object object) {
    151         if (this == object) {
    152             return 0;
    153         }
    154         else {
    155             if (object instanceof ActiveMQXid) {
    156                 ActiveMQXid that = (ActiveMQXid) object;
    157                 int diff = this.formatId - that.formatId;
    158                 if (diff == 0) {
    159                     diff = compareTo(this.branchQualifier, that.branchQualifier);
    160                     if (diff == 0) {
    161                         diff = compareTo(this.globalTransactionId, that.globalTransactionId);
    162                     }
    163                 }
    164                 return diff;
    165             }
    166             else {
    167                 return -1;
    168             }
    169         }
    170     }
    171
    172     public String toLocalTransactionId() {
    173         StringBuffer rc = new StringBuffer(13 + globalTransactionId.length * 2 + branchQualifier.length * 2);
    174         rc.append(formatId);
    175         rc.append(":");
    176         rc.append(toHexFromBytes(globalTransactionId));
    177         rc.append(":");
    178         rc.append(toHexFromBytes(branchQualifier));
    179         return rc.toString();
    180     }
    181
    182     /***
    183      * @see javax.transaction.xa.Xid#getBranchQualifier()
    184      */
    185     public byte[] getBranchQualifier() {
    186         return branchQualifier;
    187     }
    188
    189     /***
    190      * @see javax.transaction.xa.Xid#getFormatId()
    191      */
    192     public int getFormatId() {
    193         return formatId;
    194     }
    195
    196     /***
    197      * @see javax.transaction.xa.Xid#getGlobalTransactionId()
    198      */
    199     public byte[] getGlobalTransactionId() {
    200         return globalTransactionId;
    201     }
    202
    203     /***
    204      * @see java.lang.Object#toString()
    205      */
    206     public String toString() {
    207         return "XID:" + toLocalTransactionId();
    208     }
    209
    210
    211     public void writeExternal(ObjectOutput out) throws IOException {
    212         write(out);
    213     }
    214
    215     public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    216         readState(in);
    217     }
    218
    219     public void readState(DataInput dataIn) throws IOException {
    220         formatId = dataIn.readInt();
    221         branchQualifier = readBytes(dataIn);
    222         globalTransactionId = readBytes(dataIn);
    223     }
    224
    225     /***
    226      * Reads the Xid from a stream
    227      *
    228      * @param dataIn
    229      * @return
    230      */
    231     public static ActiveMQXid read(DataInput dataIn) throws IOException {
    232         ActiveMQXid answer = new ActiveMQXid();
    233         answer.readState(dataIn);
    234         return answer;
    235     }
    236
    237     public byte[] toBytes() throws IOException {
    238         ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    239         write(new DataOutputStream(buffer));
    240         return buffer.toByteArray();
    241     }
    242
    243     /***
    244      * Writes the Xid to a stream
    245      *
    246      * @param dataOut
    247      */
    248     public void write(DataOutput dataOut) throws IOException {
    249         dataOut.writeInt(formatId);
    250         writeBytes(dataOut, branchQualifier);
    251         writeBytes(dataOut, globalTransactionId);
    252     }
    253
    254     protected void writeBytes(DataOutput dataOut, byte[] data) throws IOException {
    255         dataOut.writeInt(data.length);
    256         dataOut.write(data);
    257     }
    258
    259     protected static byte[] readBytes(DataInput dataIn) throws IOException {
    260         int size = dataIn.readInt();
    261         byte[] data = new byte[size];
    262         dataIn.readFully(data);
    263         return data;
    264     }
    265
    266
    267     protected boolean equals(byte[] left, byte[] right) {
    268         if (left == right) {
    269             return true;
    270         }
    271         int size = left.length;
    272         if (size != right.length) {
    273             return false;
    274         }
    275         for (int i = 0; i < size; i++) {
    276             if (left[i] != right[i]) {
    277                 return false;
    278             }
    279         }
    280         return true;
    281     }
    282
    283     protected int compareTo(byte[] left, byte[] right) {
    284         if (left == right) {
    285             return 0;
    286         }
    287         int size = left.length;
    288         int answer = size - right.length;
    289         if (answer == 0) {
    290             for (int i = 0; i < size; i++) {
    291                 answer = left[i] - right[i];
    292                 if (answer != 0) {
    293                     break;
    294                 }
    295             }
    296         }
    297         return answer;
    298     }
    299
    300     protected int hash(byte[] bytes, int hash) {
    301         for (int i = 0, size = bytes.length; i < size; i++) {
    302             hash ^= bytes[i] << ((i % 4) * 8);
    303         }
    304         return hash;
    305     }
    306
    307     /***
    308      * @param hex
    309      * @return
    310      */
    311     private byte[] toBytesFromHex(String hex) {
    312         byte rc[] = new byte[hex.length() / 2];
    313         for (int i = 0; i < rc.length; i++) {
    314             String h = hex.substring(i * 2, i * 2 + 2);
    315             int x = Integer.parseInt(h, 16);
    316             rc[i] = (byte) x;
    317         }
    318         return rc;
    319     }
    320
    321     /***
    322      * @param bytes
    323      * @return
    324      */
    325     private String toHexFromBytes(byte[] bytes) {
    326         StringBuffer rc = new StringBuffer(bytes.length * 2);
    327         for (int i = 0; i < bytes.length; i++) {
    328             rc.append(HEX_TABLE[0xFF & bytes[i]]);
    329         }
    330         return rc.toString();
    331     }
    332 }