/*
* 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.commands.tx;
import org.infinispan.commands.remote.BaseRpcCommand;
import org.infinispan.config.Configuration;
import org.infinispan.context.InvocationContext;
import org.infinispan.context.InvocationContextContainer;
import org.infinispan.context.impl.RemoteTxInvocationContext;
import org.infinispan.interceptors.InterceptorChain;
import org.infinispan.lifecycle.ComponentStatus;
import org.infinispan.reconfigurableprotocol.exception.NoSuchReconfigurableProtocolException;
import org.infinispan.reconfigurableprotocol.manager.ReconfigurableReplicationManager;
import org.infinispan.remoting.responses.ResponseGenerator;
import org.infinispan.remoting.transport.Address;
import org.infinispan.transaction.RemoteTransaction;
import org.infinispan.transaction.TransactionTable;
import org.infinispan.transaction.xa.GlobalTransaction;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import org.jgroups.blocks.MessageRequest;
/**
* An abstract transaction boundary command that holds a reference to a {@link org.infinispan.transaction.xa.GlobalTransaction}
*
* @author Manik Surtani (<a href="mailto:manik@jboss.org">manik@jboss.org</a>)
* @author Mircea.Markus@jboss.com
* @author Pedro Ruivo
* @since 4.0
*/
public abstract class AbstractTransactionBoundaryCommand implements TransactionBoundaryCommand {
private static final Log log = LogFactory.getLog(AbstractTransactionBoundaryCommand.class);
private static boolean trace = log.isTraceEnabled();
protected GlobalTransaction globalTx;
protected final String cacheName;
protected InterceptorChain invoker;
protected InvocationContextContainer icc;
protected TransactionTable txTable;
protected Configuration configuration;
private Address origin;
private MessageRequest messageRequest;
private ResponseGenerator responseGenerator;
protected ReconfigurableReplicationManager reconfigurableReplicationManager;
public AbstractTransactionBoundaryCommand(String cacheName) {
this.cacheName = cacheName;
}
public void injectComponents(Configuration configuration) {
this.configuration = configuration;
}
public Configuration getConfiguration() {
return configuration;
}
public void init(InterceptorChain chain, InvocationContextContainer icc, TransactionTable txTable, Configuration configuration,
ReconfigurableReplicationManager reconfigurableReplicationManager) {
this.invoker = chain;
this.icc = icc;
this.txTable = txTable;
this.configuration = configuration;
this.reconfigurableReplicationManager = reconfigurableReplicationManager;
reconfigurableReplicationManager.initGlobalTransactionIfNeeded(globalTx);
}
@Override
public String getCacheName() {
return cacheName;
}
@Override
public GlobalTransaction getGlobalTransaction() {
return globalTx;
}
@Override
public void markTransactionAsRemote(boolean isRemote) {
globalTx.setRemote(isRemote);
}
/**
* This is what is returned to remote callers when an invalid RemoteTransaction is encountered. Can happen if a
* remote node propagates a transactional call to the current node, and the current node has no idea of the transaction
* in question. Can happen during rehashing, when ownerships are reassigned during a transactions.
*
* Returning a null usually means the transactional command succeeded.
* @return return value to respond to a remote caller with if the transaction context is invalid.
*/
protected Object invalidRemoteTxReturnValue() {
return null;
}
@Override
public Object perform(InvocationContext ctx) throws Throwable {
if (ctx != null) throw new IllegalStateException("Expected null context!");
markGtxAsRemote();
RemoteTransaction transaction = txTable.getRemoteTransaction(globalTx);
if (transaction == null || transaction.isMissingModifications()) {
if (trace) log.tracef("Did not find a RemoteTransaction for %s", globalTx);
return invalidRemoteTxReturnValue();
}
visitRemoteTransaction(transaction);
RemoteTxInvocationContext ctxt = icc.createRemoteTxInvocationContext(
transaction, getOrigin());
if (trace) log.tracef("About to execute tx command %s", this);
return invoker.invoke(ctxt, this);
}
/**
* Note: Used by total order protocol.
*
* The commit or the rollback command can be received before the prepare message
* we let the commands be invoked in the interceptor chain. They will be block in TotalOrderInterceptor while the
* prepare command doesn't arrives
*
* @param ctx the same as {@link #perform(org.infinispan.context.InvocationContext)}
* @return the same as {@link #perform(org.infinispan.context.InvocationContext)}
* @throws Throwable the same as {@link #perform(org.infinispan.context.InvocationContext)}
*/
protected Object performIgnoringUnexistingTransaction(InvocationContext ctx) throws Throwable {
if (ctx != null) {
throw new IllegalStateException("Expected null context!");
}
markGtxAsRemote();
RemoteTransaction transaction = txTable.getOrCreateIfAbsentRemoteTransaction(globalTx);
visitRemoteTransaction(transaction);
if (transaction.isMissingModifications()) {
log.tracef("Will execute tx command %s without the remote transaction [%s]", this,
globalTx.prettyPrint());
}
RemoteTxInvocationContext ctxt = icc.createRemoteTxInvocationContext(
transaction, getOrigin());
if (trace) {
log.tracef("About to execute tx command %s", this);
}
return invoker.invoke(ctxt, this);
}
protected void visitRemoteTransaction(RemoteTransaction tx) throws InterruptedException, NoSuchReconfigurableProtocolException {
reconfigurableReplicationManager.notifyRemoteTransaction(tx.getGlobalTransaction(), null);
}
@Override
public Object[] getParameters() {
return new Object[]{globalTx};
}
@Override
public void setParameters(int commandId, Object[] args) {
globalTx = (GlobalTransaction) args[0];
}
@Override
public boolean shouldInvoke(InvocationContext ctx) {
return true;
}
@Override
public boolean ignoreCommandOnStatus(ComponentStatus status) {
return false;
}
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AbstractTransactionBoundaryCommand that = (AbstractTransactionBoundaryCommand) o;
return this.globalTx.equals(that.globalTx);
}
public int hashCode() {
return globalTx.hashCode();
}
@Override
public String toString() {
return "gtx=" + globalTx.prettyPrint() +
", cacheName='" + cacheName + '\'' +
'}';
}
private void markGtxAsRemote() {
globalTx.setRemote(true);
}
@Override
public Address getOrigin() {
return origin;
}
@Override
public void setOrigin(Address origin) {
this.origin = origin;
}
@Override
public boolean isReturnValueExpected() {
return true;
}
@Override
public void sendReply(Object reply, boolean threwException) {
BaseRpcCommand.sendReply(messageRequest, responseGenerator, this, reply, threwException);
}
@Override
public void setResponseGenerator(ResponseGenerator responseGenerator) {
this.responseGenerator = responseGenerator;
}
@Override
public void setMessageRequest(MessageRequest request) {
this.messageRequest = request;
}
protected final boolean isTotalOrder() {
return globalTx.getReconfigurableProtocol().useTotalOrder();
}
}