/* * INESC-ID, Instituto de Engenharia de Sistemas e Computadores Investigação e Desevolvimento em Lisboa * Copyright 2013 INESC-ID 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 3.0 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.reconfigurableprotocol.protocol; import org.infinispan.commands.write.WriteCommand; import org.infinispan.interceptors.base.CommandInterceptor; import org.infinispan.reconfigurableprotocol.ReconfigurableProtocol; import org.infinispan.remoting.transport.Address; import org.infinispan.transaction.LocalTransaction; import org.infinispan.transaction.RemoteTransaction; import org.infinispan.transaction.TransactionTable; import org.infinispan.transaction.xa.GlobalTransaction; import java.util.EnumMap; import static org.infinispan.interceptors.InterceptorChain.InterceptorType; /** * Represents the switch protocol when Two Phase Commit is in use * * @author Pedro Ruivo * @since 5.2 */ public class TwoPhaseCommitProtocol extends ReconfigurableProtocol { public static final String UID = "2PC"; private static final String TO_UID = TotalOrderCommitProtocol.UID; private static final String PB_UID = PassiveReplicationCommitProtocol.UID; private static final String ACK = "_ACK_"; private final AckCollector ackCollector = new AckCollector(); private TransactionTable transactionTable; @Override public final String getUniqueProtocolName() { return UID; } @Override public final boolean canSwitchTo(ReconfigurableProtocol protocol) { return PB_UID.equals(protocol.getUniqueProtocolName()) || TO_UID.endsWith(protocol.getUniqueProtocolName()); } @Override public final void switchTo(ReconfigurableProtocol protocol) { if (TO_UID.equals(protocol.getUniqueProtocolName())) { try { awaitUntilLocalCommittingTransactionsFinished(); } catch (InterruptedException e) { //no-op } } manager.unsafeSwitch(protocol); new SendAckThread().start(); } @Override public final void stopProtocol(boolean abortOnStop) throws InterruptedException { globalStopProtocol(false, abortOnStop); } @Override public final void bootProtocol() { //no-op } @Override public final void processTransaction(GlobalTransaction globalTransaction, WriteCommand[] writeSet) { logProcessTransaction(globalTransaction); } @Override public final void processOldTransaction(GlobalTransaction globalTransaction, WriteCommand[] writeSet, ReconfigurableProtocol currentProtocol) { logProcessOldTransaction(globalTransaction, currentProtocol); if (PB_UID.equals(currentProtocol.getUniqueProtocolName())) { return; } else if (TO_UID.equals(currentProtocol.getUniqueProtocolName())) { if (writeSet == null) { //commit or rollback return; } } RemoteTransaction remoteTransaction = transactionTable.getRemoteTransaction(globalTransaction); if (remoteTransaction.check2ndPhaseAndPrepare()) { transactionTable.remoteTransactionRollback(globalTransaction); } throwOldTxException(globalTransaction); } @Override public final void processSpeculativeTransaction(GlobalTransaction globalTransaction, WriteCommand[] writeSet, ReconfigurableProtocol oldProtocol) { logProcessSpeculativeTransaction(globalTransaction, oldProtocol); if (PB_UID.equals(oldProtocol.getUniqueProtocolName())) { return; } RemoteTransaction remoteTransaction = transactionTable.getRemoteTransaction(globalTransaction); if (remoteTransaction.check2ndPhaseAndPrepare()) { transactionTable.remoteTransactionRollback(globalTransaction); } throwSpeculativeTxException(globalTransaction); } @Override public final void bootstrapProtocol() { this.transactionTable = getComponent(TransactionTable.class); } @Override public final EnumMap<InterceptorType, CommandInterceptor> buildInterceptorChain() { return buildDefaultInterceptorChain(); } @Override public final boolean use1PC(LocalTransaction localTransaction) { return configuration.transaction().use1PcForAutoCommitTransactions() && localTransaction.isImplicitTransaction(); } @Override public final boolean useTotalOrder() { return false; } @Override protected final void internalHandleData(Object data, Address from) { if (ACK.equals(data)) { ackCollector.ack(from); } } /** * Asynchronously sends the Ack when all local transactions are finished */ private class SendAckThread extends Thread { public SendAckThread() { super("2PC-Send-Ack-Thread"); } @Override public void run() { broadcastData(ACK, false); try { ackCollector.awaitAllAck(); awaitUntilRemoteTransactionsFinished(); } catch (InterruptedException e) { //no-op } manager.safeSwitch(null); } } }