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 */ 017package org.apache.activemq.command; 018 019import java.io.IOException; 020import java.util.ArrayList; 021import java.util.Arrays; 022import javax.transaction.xa.Xid; 023import org.apache.activemq.util.DataByteArrayInputStream; 024import org.apache.activemq.util.DataByteArrayOutputStream; 025 026/** 027 * @openwire:marshaller code="112" 028 * 029 */ 030public 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) throws IOException { 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() throws IOException { 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 s.append(stringForm(formatId, globalTransactionId)); 106 s.append(",branchId="); 107 s.append(stringForm(formatId, branchQualifier)); 108 s.append("]"); 109 transactionKey = s.toString(); 110 } 111 return transactionKey; 112 } 113 114 private String stringForm(int format, byte[] uid) { 115 StringBuffer s = new StringBuffer(); 116 switch (format) { 117 case 131077: // arjuna 118 stringFormArj(s, uid); 119 break; 120 default: // aries 121 stringFormDefault(s, uid); 122 } 123 return s.toString(); 124 } 125 126 private void stringFormDefault(StringBuffer s, byte[] uid) { 127 for (int i = 0; i < uid.length; i++) { 128 s.append(Integer.toHexString(uid[i])); 129 } 130 } 131 132 private void stringFormArj(StringBuffer s, byte[] uid) { 133 try { 134 DataByteArrayInputStream byteArrayInputStream = new DataByteArrayInputStream(uid); 135 s.append(Long.toString(byteArrayInputStream.readLong(), 16)); 136 s.append(':'); 137 s.append(Long.toString(byteArrayInputStream.readLong(), 16)); 138 s.append(':'); 139 140 s.append(Integer.toString(byteArrayInputStream.readInt(), 16)); 141 s.append(':'); 142 s.append(Integer.toString(byteArrayInputStream.readInt(), 16)); 143 s.append(':'); 144 s.append(Integer.toString(byteArrayInputStream.readInt(), 16)); 145 146 } catch (Exception ignored) { 147 stringFormDefault(s, uid); 148 } 149 } 150 151 public String toString() { 152 return getTransactionKey(); 153 } 154 155 public boolean isXATransaction() { 156 return true; 157 } 158 159 public boolean isLocalTransaction() { 160 return false; 161 } 162 163 /** 164 * @openwire:property version=1 165 */ 166 public int getFormatId() { 167 return formatId; 168 } 169 170 /** 171 * @openwire:property version=1 172 */ 173 public byte[] getGlobalTransactionId() { 174 return globalTransactionId; 175 } 176 177 /** 178 * @openwire:property version=1 179 */ 180 public byte[] getBranchQualifier() { 181 return branchQualifier; 182 } 183 184 public void setBranchQualifier(byte[] branchQualifier) { 185 this.branchQualifier = branchQualifier; 186 this.hash = 0; 187 } 188 189 public void setFormatId(int formatId) { 190 this.formatId = formatId; 191 this.hash = 0; 192 } 193 194 public void setGlobalTransactionId(byte[] globalTransactionId) { 195 this.globalTransactionId = globalTransactionId; 196 this.hash = 0; 197 } 198 199 public int hashCode() { 200 if (hash == 0) { 201 hash = formatId; 202 hash = hash(globalTransactionId, hash); 203 hash = hash(branchQualifier, hash); 204 if (hash == 0) { 205 hash = 0xaceace; 206 } 207 } 208 return hash; 209 } 210 211 private static int hash(byte[] bytes, int hash) { 212 int size = bytes.length; 213 for (int i = 0; i < size; i++) { 214 hash ^= bytes[i] << ((i % 4) * 8); 215 } 216 return hash; 217 } 218 219 public boolean equals(Object o) { 220 if (o == null || o.getClass() != XATransactionId.class) { 221 return false; 222 } 223 XATransactionId xid = (XATransactionId)o; 224 return xid.formatId == formatId && Arrays.equals(xid.globalTransactionId, globalTransactionId) 225 && Arrays.equals(xid.branchQualifier, branchQualifier); 226 } 227 228 public int compareTo(Object o) { 229 if (o == null || o.getClass() != XATransactionId.class) { 230 return -1; 231 } 232 XATransactionId xid = (XATransactionId)o; 233 return getTransactionKey().compareTo(xid.getTransactionKey()); 234 } 235 236 public void setPreparedAcks(ArrayList<MessageAck> preparedAcks) { 237 this.preparedAcks = preparedAcks; 238 } 239 240 public ArrayList<MessageAck> getPreparedAcks() { 241 return preparedAcks; 242 } 243}