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    
018    package org.apache.geronimo.transaction.log;
019    
020    import java.io.Serializable;
021    import java.util.Arrays;
022    
023    import javax.transaction.xa.Xid;
024    
025    /**
026     * Unique id for a transaction.  This implementation is backed by a single byte buffer
027     * so can do less copying than one backed by several byte buffers for the different components.
028     *
029     * @version $Rev: 476049 $ $Date: 2006-11-16 23:35:17 -0500 (Thu, 16 Nov 2006) $
030     */
031    public class XidImpl2 implements Xid, Serializable {
032        private static int HEADER_SIZE = 4;
033        private static int ACTION_POS = 0;
034        private static int GLOBALID_SIZE_POS = 1;
035        private static int BRANCHID_SIZE_POS = 2;
036        //3 unused
037        private static int FORMAT_ID = 0x4765526f;  // Gero
038        private static int FORMAT_SIZE = 4;
039    
040        private static byte[] FORMAT_ID_BYTES = "Gero".getBytes();
041    
042        private final byte[] buffer = new byte[HEADER_SIZE + FORMAT_SIZE + Xid.MAXGTRIDSIZE + Xid.MAXBQUALSIZE];
043        private int hash;
044        private Object key;
045    
046        /**
047         * Constructor taking a global id (for the main transaction)
048         * @param globalId the global transaction id
049         */
050        public XidImpl2(byte[] globalId) {
051            System.arraycopy(FORMAT_ID_BYTES, 0, buffer, HEADER_SIZE, FORMAT_SIZE);
052            buffer[GLOBALID_SIZE_POS] = (byte) globalId.length;
053            System.arraycopy(globalId, 0, buffer, HEADER_SIZE + FORMAT_SIZE, Xid.MAXGTRIDSIZE);
054    
055            //this.hash = hash(buffer);
056        }
057    
058        /**
059         * Constructor for a branch id
060         * @param global the xid of the global transaction this branch belongs to
061         * @param branch the branch id
062         */
063        public XidImpl2(Xid global, byte[] branch) {
064            if (global instanceof XidImpl2) {
065                System.arraycopy(((XidImpl2) global).buffer, 0, buffer, 0, HEADER_SIZE + FORMAT_SIZE + Xid.MAXGTRIDSIZE);
066            } else {
067                System.arraycopy(FORMAT_ID_BYTES, 0, buffer, HEADER_SIZE, FORMAT_SIZE);
068                byte[] globalId = global.getGlobalTransactionId();
069                System.arraycopy(globalId, 0, buffer, HEADER_SIZE + FORMAT_SIZE, globalId.length);
070            }
071            buffer[BRANCHID_SIZE_POS] = (byte) branch.length;
072            System.arraycopy(branch, 0, buffer, HEADER_SIZE + FORMAT_SIZE + Xid.MAXGTRIDSIZE, Xid.MAXBQUALSIZE);
073            //hash = hash(buffer);
074        }
075    
076        public XidImpl2(int formatId, byte[] globalId, byte[] branch) {
077            //todo this is wrong, it ignores formatId supplied.  Maybe this is ok?
078            System.arraycopy(FORMAT_ID_BYTES, 0, buffer, HEADER_SIZE, FORMAT_SIZE);
079            System.arraycopy(globalId, 0, buffer, HEADER_SIZE + FORMAT_SIZE, globalId.length);
080            buffer[BRANCHID_SIZE_POS] = (byte) branch.length;
081            System.arraycopy(branch, 0, buffer, HEADER_SIZE + FORMAT_SIZE + Xid.MAXGTRIDSIZE, Xid.MAXBQUALSIZE);
082            //hash = hash(buffer);
083        }
084    
085        private int hash(byte[] id) {
086            int hash = 0;
087            for (int i = 0; i < id.length; i++) {
088                hash = (hash * 37) + id[i];
089            }
090            return hash;
091        }
092    
093        public int getFormatId() {
094            return FORMAT_ID;
095        }
096    
097        public byte[] getGlobalTransactionId() {
098            byte[] globalId = new byte[buffer[GLOBALID_SIZE_POS]];
099            System.arraycopy(buffer, HEADER_SIZE + FORMAT_SIZE, globalId, 0, buffer[GLOBALID_SIZE_POS]);
100            return globalId;
101        }
102    
103        public byte[] getBranchQualifier() {
104            byte[] branchId = new byte[buffer[BRANCHID_SIZE_POS]];
105            System.arraycopy(buffer, HEADER_SIZE + FORMAT_SIZE + Xid.MAXGTRIDSIZE, branchId, 0, buffer[BRANCHID_SIZE_POS]);
106            return branchId;
107        }
108    
109        public boolean equals(Object obj) {
110            if (obj instanceof XidImpl2 == false) {
111                return false;
112            }
113            XidImpl2 other = (XidImpl2) obj;
114            return Arrays.equals(buffer, other.buffer);
115        }
116    
117        public int hashCode() {
118            if (hash == 0) {
119                hash = hash(buffer);
120            }
121            return hash;
122        }
123    
124        public String toString() {
125            StringBuffer s = new StringBuffer("[formatId=Gero,");
126            s.append("globalId=");
127            for (int i = FORMAT_SIZE; i < FORMAT_SIZE + Xid.MAXGTRIDSIZE; i++) {
128                s.append(Integer.toHexString(buffer[i]));
129            }
130            s.append(",branchId=");
131            for (int i = FORMAT_SIZE + Xid.MAXGTRIDSIZE; i < buffer.length; i++) {
132                s.append(Integer.toHexString(buffer[i]));
133            }
134            s.append("]");
135            return s.toString();
136        }
137    
138        byte[] getBuffer(byte action) {
139            buffer[ACTION_POS] = action;
140            return buffer;
141        }
142    
143        public void setKey(Object key) {
144            this.key = key;
145        }
146    
147        public Object getKey() {
148            return key;
149        }
150    }