/* * JBoss, Home of Professional Open Source * Copyright 2009 Red Hat Inc. and/or its affiliates and other * contributors as indicated by the @author tags. All rights reserved. * See the copyright.txt in the distribution for a full listing of * individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.infinispan.transaction.xa; import org.infinispan.marshall.AbstractExternalizer; import org.infinispan.marshall.Ids; import org.infinispan.reconfigurableprotocol.ReconfigurableProtocol; import org.infinispan.remoting.transport.Address; import org.infinispan.util.Util; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; /** * Uniquely identifies a transaction that spans all JVMs in a cluster. This is used when replicating all modifications * in a transaction; the PREPARE and COMMIT (or ROLLBACK) messages have to have a unique identifier to associate the * changes with<br>. GlobalTransaction should be instantiated thorough {@link TransactionFactory} class, * as their type depends on the runtime configuration. * * @author <a href="mailto:bela@jboss.org">Bela Ban</a> Apr 12, 2003 * @author <a href="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a> * @author Mircea.Markus@jboss.com * @since 4.0 */ public class GlobalTransaction implements Cloneable { private static final long serialVersionUID = 8011434781266976149L; private static AtomicLong sid = new AtomicLong(0); long id = -1; protected transient Address addr = null; private transient int hash_code = -1; // in the worst case, hashCode() returns 0, then increases, so we're safe here private transient boolean remote = false; private String protocolId; private long epochId; private transient ReconfigurableProtocol reconfigurableProtocol; private int viewId; /** * empty ctor used by externalization. */ protected GlobalTransaction() { } protected GlobalTransaction(Address addr, boolean remote) { this.id = sid.incrementAndGet(); this.addr = addr; this.remote = remote; } public Address getAddress() { return addr; } public long getId() { return id; } public boolean isRemote() { return remote; } public void setRemote(boolean remote) { this.remote = remote; } public int getViewId() { return viewId; } public void setViewId(int viewId) { this.viewId = viewId; } @Override public int hashCode() { if (hash_code == -1) { hash_code = (addr != null ? addr.hashCode() : 0) + (int) id; } return hash_code; } @Override public boolean equals(Object other) { if (this == other) return true; if (!(other instanceof GlobalTransaction)) return false; GlobalTransaction otherGtx = (GlobalTransaction) other; boolean aeq = (addr == null) ? (otherGtx.addr == null) : addr.equals(otherGtx.addr); return aeq && (id == otherGtx.id); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("GlobalTransaction:<").append(addr).append(">:").append(id).append(isRemote() ? ":remote" : ":local"); return sb.toString(); } /** * Returns a simplified representation of the transaction. */ public final String prettyPrint() { return getAddress() + ":" + getId(); } public void setId(long id) { this.id = id; } public void setAddress(Address address) { this.addr = address; } @Override public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { throw new IllegalStateException("Impossible!"); } } protected abstract static class AbstractGlobalTxExternalizer<T extends GlobalTransaction> extends AbstractExternalizer<T> { @Override public void writeObject(ObjectOutput output, T gtx) throws IOException { output.writeLong(gtx.id); output.writeObject(gtx.addr); output.writeInt(gtx.viewId); output.writeLong(gtx.epochId); output.writeUTF(gtx.protocolId); } /** * Factory method for GlobalTransactions * @return a newly constructed instance of GlobalTransaction or one of its subclasses **/ protected abstract T createGlobalTransaction(); @Override public T readObject(ObjectInput input) throws IOException, ClassNotFoundException { T gtx = createGlobalTransaction(); gtx.id = input.readLong(); gtx.addr = (Address) input.readObject(); gtx.viewId = input.readInt(); gtx.epochId = input.readLong(); gtx.protocolId = input.readUTF(); return gtx; } } public static class Externalizer extends AbstractGlobalTxExternalizer<GlobalTransaction> { @Override @SuppressWarnings("unchecked") public Set<Class<? extends GlobalTransaction>> getTypeClasses() { return Util.<Class<? extends GlobalTransaction>>asSet(GlobalTransaction.class); } @Override public Integer getId() { return Ids.GLOBAL_TRANSACTION; } @Override protected GlobalTransaction createGlobalTransaction() { return TransactionFactory.TxFactoryEnum.NODLD_NORECOVERY_XA.newGlobalTransaction(); } } public String getProtocolId() { return protocolId; } public void setProtocolId(String protocolId) { this.protocolId = protocolId; } public long getEpochId() { return epochId; } public void setEpochId(long epochId) { this.epochId = epochId; } public ReconfigurableProtocol getReconfigurableProtocol() { return reconfigurableProtocol; } public void setReconfigurableProtocol(ReconfigurableProtocol reconfigurableProtocol) { this.reconfigurableProtocol = reconfigurableProtocol; } }