com.arjuna.wst11.stub.SubordinateDurable2PCStub Maven / Gradle / Ivy
package com.arjuna.wst11.stub;
import com.arjuna.wst.*;
import com.arjuna.mwlabs.wscf.model.twophase.arjunacore.subordinate.SubordinateATCoordinator;
import com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome;
import com.arjuna.ats.arjuna.coordinator.ActionStatus;
import com.arjuna.ats.arjuna.state.OutputObjectState;
import com.arjuna.ats.arjuna.state.InputObjectState;
import java.io.IOException;
import org.jboss.jbossts.xts.recovery.participant.at.XTSATRecoveryManager;
import org.jboss.jbossts.xts.recovery.participant.at.PersistableATParticipant;
/**
* A durable participant registered on behalf of an interposed WS-AT coordinator in order to ensure that
* durable participants in the subtransaction prepared, committed and aborted at the right time.
*/
public class SubordinateDurable2PCStub implements Durable2PCParticipant, PersistableParticipant, PersistableATParticipant
{
/**
* normal constructor used when the subordinate coordinator is registered as a durable participant
* with its parent coordinator.
*
* @param coordinator
*/
public SubordinateDurable2PCStub(SubordinateATCoordinator coordinator)
{
this.coordinator = coordinator;
this.coordinatorId = coordinator.get_uid().stringForm();
this.recovered = false;
}
/**
* empty constructor for use only during recovery
*/
public SubordinateDurable2PCStub()
{
this.coordinator = null;
this.coordinatorId = null;
this.recovered = true;
}
/**
* This will be called when the parent coordinator is preparing its durable participants and should ensure
* that the interposed cooordinator does the same.
*
* @return the Vote returned by the subordinate coordinator.
* @throws com.arjuna.wst.WrongStateException if the subordinate coordinator does the same
* @throws com.arjuna.wst.SystemException if the subordinate coordinator does the same
*/
public Vote prepare() throws WrongStateException, SystemException {
switch (coordinator.prepare())
{
case TwoPhaseOutcome.PREPARE_OK:
return new Prepared();
case TwoPhaseOutcome.PREPARE_READONLY:
return new ReadOnly();
case TwoPhaseOutcome.PREPARE_NOTOK:
default:
return new Aborted();
}
}
/**
* this will be called when the parent coordinator commits its durable participants and should ensure
* that the interposed cooordinator does the same
* @throws com.arjuna.wst.WrongStateException
* @throws com.arjuna.wst.SystemException
*/
public void commit() throws WrongStateException, SystemException {
if (!isRecovered()) {
coordinator.commit();
} else {
XTSATRecoveryManager recoveryManager = null;
boolean isRecoveryScanStarted = false;
if (coordinator == null) {
// try fetching coordinator from the recovery manager
recoveryManager = XTSATRecoveryManager.getRecoveryManager();
// check whether recovery has started before we check for the presence
// of the subordinate coordinator
isRecoveryScanStarted = recoveryManager.isSubordinateCoordinatorRecoveryStarted();
coordinator = SubordinateATCoordinator.getRecoveredCoordinator(coordinatorId);
}
if (coordinator == null) {
// hmm, still null -- see if we have finished recovery scanning
if (!isRecoveryScanStarted) {
// the subtransaction may still be waiting to be resolved
// throw an exception causing the commit to be retried later
throw new SystemException();
}
// ok we have no transaction to commit so assume we already committed it and
// return without error
} else if(!coordinator.isActivated()) {
// the transaction was logged but has not yet been recovered successfully
// throw an exception causing the commit to be retried later
throw new SystemException();
} else {
int status = coordinator.status();
if (status == ActionStatus.PREPARED || status == ActionStatus.COMMITTING) {
// ok, the commit process was not previously initiated so start it now
coordinator.commit();
SubordinateATCoordinator.removeActiveProxy(coordinatorId);
status = coordinator.status();
}
// check that we are not still committing because of a comms timeout
if (status == ActionStatus.COMMITTING) {
// throw an exception causing the commit to be retried later
throw new SystemException();
}
}
}
}
/**
* this will be called when the parent coordinator rolls back its durable participants and should ensure
* that the interposed cooordinator does the same
* @throws com.arjuna.wst.WrongStateException
* @throws com.arjuna.wst.SystemException
*/
public void rollback() throws WrongStateException, SystemException {
if (!isRecovered()) {
coordinator.rollback();
} else {
// first check whether crashed coordinators have been recovered
XTSATRecoveryManager recoveryManager = XTSATRecoveryManager.getRecoveryManager();
boolean isRecoveryScanStarted = recoveryManager.isSubordinateCoordinatorRecoveryStarted();
// now look for a subordinate coordinator with the right id
coordinator = SubordinateATCoordinator.getRecoveredCoordinator(coordinatorId);
if (coordinator == null) {
if (!isRecoveryScanStarted) {
// the subtransaction may still be waiting to be resolved
// throw an exception causing the rollback to be retried later
throw new SystemException();
}
} else if(!coordinator.isActivated()) {
// the transaction was logged but has not yet been recovered successfully
// throw an exception causing the rollback to be retried later
throw new SystemException();
} else {
int status = coordinator.status();
if ((status == ActionStatus.ABORTED) ||
(status == ActionStatus.H_ROLLBACK) ||
(status == ActionStatus.ABORTING) ||
(status == ActionStatus.ABORT_ONLY)) {
// ok, the rollback process was not previously initiated so start it now
coordinator.rollback();
SubordinateATCoordinator.removeActiveProxy(coordinatorId);
status = coordinator.status();
}
}
}
}
/**
* this should never get called
* @throws com.arjuna.wst.SystemException
*/
public void unknown() throws SystemException {
coordinator.unknown();
}
/**
* this should never get called
* @throws com.arjuna.wst.SystemException
*/
public void error() throws SystemException {
coordinator.error();
}
/**
* Save the state of the particpant to the specified input object stream.
* @param oos The output output stream.
* @return true if persisted, false otherwise.
*/
public boolean saveState(OutputObjectState oos) {
// we need to save the id of the subordinate coordinator so we can identify it again
// when we are recreated
try {
oos.packString(coordinatorId);
return true;
} catch (IOException e) {
return false;
}
}
/**
* Restore the state of the particpant from the specified input object stream.
* @param ios The Input object stream.
* @return true if restored, false otherwise.
*/
public boolean restoreState(InputObjectState ios) {
// restore the subordinate coordinator id so we can check to ensure it has been committed
try {
coordinatorId = ios.unpackString();
SubordinateATCoordinator.addActiveProxy(coordinatorId);
return true;
} catch (IOException e) {
return false;
}
}
/**
* test if this participant is recovered
*/
public boolean isRecovered()
{
return recovered;
}
/**
* the interposed coordinator
*/
private SubordinateATCoordinator coordinator;
/**
* the interposed coordinator's id
*/
private String coordinatorId;
/**
* a flag indicating whether this participant has been recovered
*/
private boolean recovered;
/**
* this participant implements the PersistableATarticipant interface so it can save its state.
* recovery is managed by an XTS recovery module
* @return
* @throws Exception
*/
public byte[] getRecoveryState() throws Exception {
OutputObjectState oos = new OutputObjectState();
oos.packString(this.getClass().getName());
this.saveState(oos);
return oos.buffer();
}
}