package org.infinispan.transaction.xa;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import org.infinispan.commons.marshall.AbstractExternalizer;
import org.infinispan.commons.util.Util;
import org.infinispan.marshall.core.Ids;
import org.infinispan.remoting.transport.Address;
/**
* 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 AtomicLong sid = new AtomicLong(0);
protected long id = -1;
protected Address addr = null;
private int hash_code = -1; // in the worst case, hashCode() returns 0, then increases, so we're safe here
private boolean remote = false;
/**
* 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;
}
@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() {
return "GlobalTx:" + Objects.toString(addr, "local") + ":" + id;
}
/**
* Returns a simplified representation of the transaction.
*/
public final String globalId() {
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);
}
/**
* 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();
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();
}
}
}