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; 025import org.apache.activemq.util.JenkinsHash; 026 027/** 028 * @openwire:marshaller code="112" 029 * 030 */ 031public class XATransactionId extends TransactionId implements Xid, Comparable { 032 033 public static final byte DATA_STRUCTURE_TYPE = CommandTypes.ACTIVEMQ_XA_TRANSACTION_ID; 034 035 private int formatId; 036 private byte[] branchQualifier; 037 private byte[] globalTransactionId; 038 private transient DataByteArrayOutputStream outputStream; 039 private transient byte[] encodedXidBytes; 040 041 private transient int hash; 042 private transient String transactionKey; 043 private transient ArrayList<MessageAck> preparedAcks; 044 045 public XATransactionId() { 046 } 047 048 public XATransactionId(Xid xid) { 049 this.formatId = xid.getFormatId(); 050 this.globalTransactionId = xid.getGlobalTransactionId(); 051 this.branchQualifier = xid.getBranchQualifier(); 052 } 053 054 public XATransactionId(byte[] encodedBytes) throws IOException { 055 encodedXidBytes = encodedBytes; 056 initFromEncodedBytes(); 057 } 058 059 public byte getDataStructureType() { 060 return DATA_STRUCTURE_TYPE; 061 } 062 063 final int XID_PREFIX_SIZE = 16; 064 //+|-,(long)lastAck,(byte)priority,(int)formatid,(short)globalLength.... 065 private void initFromEncodedBytes() throws IOException { 066 DataByteArrayInputStream inputStream = new DataByteArrayInputStream(encodedXidBytes); 067 inputStream.skipBytes(10); 068 formatId = inputStream.readInt(); 069 int globalLength = inputStream.readShort(); 070 globalTransactionId = new byte[globalLength]; 071 try { 072 inputStream.read(globalTransactionId); 073 branchQualifier = new byte[inputStream.available()]; 074 inputStream.read(branchQualifier); 075 } catch (IOException fatal) { 076 throw new RuntimeException(this + ", failed to decode:", fatal); 077 } 078 } 079 080 public synchronized byte[] getEncodedXidBytes() { 081 if (encodedXidBytes == null) { 082 outputStream = new DataByteArrayOutputStream(XID_PREFIX_SIZE + globalTransactionId.length + branchQualifier.length); 083 outputStream.position(10); 084 outputStream.writeInt(formatId); 085 // global length 086 outputStream.writeShort(globalTransactionId.length); 087 try { 088 outputStream.write(globalTransactionId); 089 outputStream.write(branchQualifier); 090 } catch (IOException fatal) { 091 throw new RuntimeException(this + ", failed to encode:", fatal); 092 } 093 encodedXidBytes = outputStream.getData(); 094 } 095 return encodedXidBytes; 096 } 097 098 public DataByteArrayOutputStream internalOutputStream() { 099 return outputStream; 100 } 101 102 public synchronized String getTransactionKey() { 103 if (transactionKey == null) { 104 StringBuffer s = new StringBuffer(); 105 s.append("XID:[" + formatId + ",globalId="); 106 s.append(stringForm(formatId, globalTransactionId)); 107 s.append(",branchId="); 108 s.append(stringForm(formatId, branchQualifier)); 109 s.append("]"); 110 transactionKey = s.toString(); 111 } 112 return transactionKey; 113 } 114 115 private String stringForm(int format, byte[] uid) { 116 StringBuffer s = new StringBuffer(); 117 switch (format) { 118 case 131077: // arjuna 119 stringFormArj(s, uid); 120 break; 121 default: // aries 122 stringFormDefault(s, uid); 123 } 124 return s.toString(); 125 } 126 127 private void stringFormDefault(StringBuffer s, byte[] uid) { 128 for (int i = 0; i < uid.length; i++) { 129 s.append(Integer.toHexString(uid[i])); 130 } 131 } 132 133 private void stringFormArj(StringBuffer s, byte[] uid) { 134 try { 135 DataByteArrayInputStream byteArrayInputStream = new DataByteArrayInputStream(uid); 136 s.append(Long.toString(byteArrayInputStream.readLong(), 16)); 137 s.append(':'); 138 s.append(Long.toString(byteArrayInputStream.readLong(), 16)); 139 s.append(':'); 140 141 s.append(Integer.toString(byteArrayInputStream.readInt(), 16)); 142 s.append(':'); 143 s.append(Integer.toString(byteArrayInputStream.readInt(), 16)); 144 s.append(':'); 145 s.append(Integer.toString(byteArrayInputStream.readInt(), 16)); 146 147 } catch (Exception ignored) { 148 stringFormDefault(s, uid); 149 } 150 } 151 152 public String toString() { 153 return getTransactionKey(); 154 } 155 156 public boolean isXATransaction() { 157 return true; 158 } 159 160 public boolean isLocalTransaction() { 161 return false; 162 } 163 164 /** 165 * @openwire:property version=1 166 */ 167 public int getFormatId() { 168 return formatId; 169 } 170 171 /** 172 * @openwire:property version=1 173 */ 174 public byte[] getGlobalTransactionId() { 175 return globalTransactionId; 176 } 177 178 /** 179 * @openwire:property version=1 180 */ 181 public byte[] getBranchQualifier() { 182 return branchQualifier; 183 } 184 185 public void setBranchQualifier(byte[] branchQualifier) { 186 this.branchQualifier = branchQualifier; 187 this.hash = 0; 188 } 189 190 public void setFormatId(int formatId) { 191 this.formatId = formatId; 192 this.hash = 0; 193 } 194 195 public void setGlobalTransactionId(byte[] globalTransactionId) { 196 this.globalTransactionId = globalTransactionId; 197 this.hash = 0; 198 } 199 200 public int hashCode() { 201 if (hash == 0) { 202 hash = formatId; 203 JenkinsHash jh = JenkinsHash.getInstance(); 204 hash = jh.hash(globalTransactionId, hash); 205 hash = jh.hash(branchQualifier, hash); 206 if (hash == 0) { 207 hash = 0xaceace; 208 } 209 } 210 return hash; 211 } 212 213 public boolean equals(Object o) { 214 if (o == null || o.getClass() != XATransactionId.class) { 215 return false; 216 } 217 XATransactionId xid = (XATransactionId)o; 218 return xid.formatId == formatId && Arrays.equals(xid.globalTransactionId, globalTransactionId) 219 && Arrays.equals(xid.branchQualifier, branchQualifier); 220 } 221 222 public int compareTo(Object o) { 223 if (o == null || o.getClass() != XATransactionId.class) { 224 return -1; 225 } 226 XATransactionId xid = (XATransactionId)o; 227 return getTransactionKey().compareTo(xid.getTransactionKey()); 228 } 229 230 public void setPreparedAcks(ArrayList<MessageAck> preparedAcks) { 231 this.preparedAcks = preparedAcks; 232 } 233 234 public ArrayList<MessageAck> getPreparedAcks() { 235 return preparedAcks; 236 } 237}