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 }