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}