001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.activemq.command;
018
019 import java.io.IOException;
020 import java.util.ArrayList;
021 import java.util.Arrays;
022 import javax.transaction.xa.Xid;
023 import org.apache.activemq.util.DataByteArrayInputStream;
024 import org.apache.activemq.util.DataByteArrayOutputStream;
025
026 /**
027 * @openwire:marshaller code="112"
028 *
029 */
030 public class XATransactionId extends TransactionId implements Xid, Comparable {
031
032 public static final byte DATA_STRUCTURE_TYPE = CommandTypes.ACTIVEMQ_XA_TRANSACTION_ID;
033
034 private int formatId;
035 private byte[] branchQualifier;
036 private byte[] globalTransactionId;
037 private transient DataByteArrayOutputStream outputStream;
038 private transient byte[] encodedXidBytes;
039
040 private transient int hash;
041 private transient String transactionKey;
042 private transient ArrayList<MessageAck> preparedAcks;
043
044 public XATransactionId() {
045 }
046
047 public XATransactionId(Xid xid) {
048 this.formatId = xid.getFormatId();
049 this.globalTransactionId = xid.getGlobalTransactionId();
050 this.branchQualifier = xid.getBranchQualifier();
051 }
052
053 public XATransactionId(byte[] encodedBytes) {
054 encodedXidBytes = encodedBytes;
055 initFromEncodedBytes();
056 }
057
058 public byte getDataStructureType() {
059 return DATA_STRUCTURE_TYPE;
060 }
061
062 final int XID_PREFIX_SIZE = 16;
063 //+|-,(long)lastAck,(byte)priority,(int)formatid,(short)globalLength....
064 private void initFromEncodedBytes() {
065 DataByteArrayInputStream inputStream = new DataByteArrayInputStream(encodedXidBytes);
066 inputStream.skipBytes(10);
067 formatId = inputStream.readInt();
068 int globalLength = inputStream.readShort();
069 globalTransactionId = new byte[globalLength];
070 try {
071 inputStream.read(globalTransactionId);
072 branchQualifier = new byte[inputStream.available()];
073 inputStream.read(branchQualifier);
074 } catch (IOException fatal) {
075 throw new RuntimeException(this + ", failed to decode:", fatal);
076 }
077 }
078
079 public synchronized byte[] getEncodedXidBytes() {
080 if (encodedXidBytes == null) {
081 outputStream = new DataByteArrayOutputStream(XID_PREFIX_SIZE + globalTransactionId.length + branchQualifier.length);
082 outputStream.position(10);
083 outputStream.writeInt(formatId);
084 // global length
085 outputStream.writeShort(globalTransactionId.length);
086 try {
087 outputStream.write(globalTransactionId);
088 outputStream.write(branchQualifier);
089 } catch (IOException fatal) {
090 throw new RuntimeException(this + ", failed to encode:", fatal);
091 }
092 encodedXidBytes = outputStream.getData();
093 }
094 return encodedXidBytes;
095 }
096
097 public DataByteArrayOutputStream internalOutputStream() {
098 return outputStream;
099 }
100
101 public synchronized String getTransactionKey() {
102 if (transactionKey == null) {
103 StringBuffer s = new StringBuffer();
104 s.append("XID:[" + formatId + ",globalId=");
105 for (int i = 0; i < globalTransactionId.length; i++) {
106 s.append(Integer.toHexString(globalTransactionId[i]));
107 }
108 s.append(",branchId=");
109 for (int i = 0; i < branchQualifier.length; i++) {
110 s.append(Integer.toHexString(branchQualifier[i]));
111 }
112 s.append("]");
113 transactionKey = s.toString();
114 }
115 return transactionKey;
116 }
117
118 public String toString() {
119 return getTransactionKey();
120 }
121
122 public boolean isXATransaction() {
123 return true;
124 }
125
126 public boolean isLocalTransaction() {
127 return false;
128 }
129
130 /**
131 * @openwire:property version=1
132 */
133 public int getFormatId() {
134 return formatId;
135 }
136
137 /**
138 * @openwire:property version=1
139 */
140 public byte[] getGlobalTransactionId() {
141 return globalTransactionId;
142 }
143
144 /**
145 * @openwire:property version=1
146 */
147 public byte[] getBranchQualifier() {
148 return branchQualifier;
149 }
150
151 public void setBranchQualifier(byte[] branchQualifier) {
152 this.branchQualifier = branchQualifier;
153 this.hash = 0;
154 }
155
156 public void setFormatId(int formatId) {
157 this.formatId = formatId;
158 this.hash = 0;
159 }
160
161 public void setGlobalTransactionId(byte[] globalTransactionId) {
162 this.globalTransactionId = globalTransactionId;
163 this.hash = 0;
164 }
165
166 public int hashCode() {
167 if (hash == 0) {
168 hash = formatId;
169 hash = hash(globalTransactionId, hash);
170 hash = hash(branchQualifier, hash);
171 if (hash == 0) {
172 hash = 0xaceace;
173 }
174 }
175 return hash;
176 }
177
178 private static int hash(byte[] bytes, int hash) {
179 int size = bytes.length;
180 for (int i = 0; i < size; i++) {
181 hash ^= bytes[i] << ((i % 4) * 8);
182 }
183 return hash;
184 }
185
186 public boolean equals(Object o) {
187 if (o == null || o.getClass() != XATransactionId.class) {
188 return false;
189 }
190 XATransactionId xid = (XATransactionId)o;
191 return xid.formatId == formatId && Arrays.equals(xid.globalTransactionId, globalTransactionId)
192 && Arrays.equals(xid.branchQualifier, branchQualifier);
193 }
194
195 public int compareTo(Object o) {
196 if (o == null || o.getClass() != XATransactionId.class) {
197 return -1;
198 }
199 XATransactionId xid = (XATransactionId)o;
200 return getTransactionKey().compareTo(xid.getTransactionKey());
201 }
202
203 public void setPreparedAcks(ArrayList<MessageAck> preparedAcks) {
204 this.preparedAcks = preparedAcks;
205 }
206
207 public ArrayList<MessageAck> getPreparedAcks() {
208 return preparedAcks;
209 }
210 }