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 }