Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package com.arjuna.wst11.messaging.engines;
import com.arjuna.webservices.SoapFault;
import com.arjuna.webservices.SoapFaultType;
import com.arjuna.webservices.logging.WSTLogger;
import com.arjuna.webservices.util.TransportTimer;
import com.arjuna.webservices11.wsaddr.AddressingHelper;
import org.jboss.ws.api.addressing.MAP;
import com.arjuna.webservices11.wsarj.ArjunaContext;
import com.arjuna.webservices11.wsarj.InstanceIdentifier;
import com.arjuna.webservices11.wscoor.CoordinationConstants;
import com.arjuna.webservices11.wsat.client.CoordinatorClient;
import com.arjuna.webservices11.wsat.ParticipantInboundEvents;
import com.arjuna.webservices11.wsat.State;
import com.arjuna.webservices11.wsat.AtomicTransactionConstants;
import com.arjuna.webservices11.wsat.processors.ParticipantProcessor;
import com.arjuna.wsc11.messaging.MessageId;
import com.arjuna.wst.*;
import org.oasis_open.docs.ws_tx.wsat._2006._06.Notification;
import org.jboss.jbossts.xts11.recovery.participant.at.ATParticipantRecoveryRecord;
import org.jboss.jbossts.xts.recovery.participant.at.XTSATRecoveryManager;
import javax.xml.namespace.QName;
import javax.xml.ws.wsaddressing.W3CEndpointReference;
import java.util.TimerTask;
/**
* The participant state engine
* @author kevin
*/
public class ParticipantEngine implements ParticipantInboundEvents
{
/**
* The associated participant
*/
private final Participant participant ;
/**
* The participant id.
*/
private final String id ;
/**
* The coordinator endpoint reference.
*/
private final W3CEndpointReference coordinator ;
/**
* The current state.
*/
private State state ;
/**
* The associated timer task or null.
*/
private TimerTask timerTask ;
/**
* the time which will elapse before the next message resend. this is incrementally increased
* until it reaches RESEND_PERIOD_MAX
*/
private long resendPeriod;
/**
* the initial period we will allow between resends.
*/
private long initialResendPeriod;
/**
* the maximum period we will allow between resends. n.b. the coordinator uses the value returned
* by getTransportTimeout as the limit for how long it waits for a response. however, we can still
* employ a max resend period in excess of this value. if a message comes in after the coordinator
* has given up it will catch it on the next retry.
*/
private long maxResendPeriod;
/**
* true if this participant has been recovered otherwise false
*/
private boolean recovered;
/**
* true if this participant's recovery details have been logged to disk otherwise false
*/
private boolean persisted;
/**
* Construct the initial engine for the participant.
* @param participant The participant.
* @param id The participant id.
* @param coordinator The coordinator endpoint reference.
*/
public ParticipantEngine(final Participant participant, final String id, final W3CEndpointReference coordinator)
{
this(participant, id, State.STATE_ACTIVE, coordinator, false) ;
}
/**
* Construct the engine for the participant in a specified state.
* @param participant The participant.
* @param id The participant id.
* @param state The initial state.
* @param coordinator The coordinator endpoint reference.
*/
public ParticipantEngine(final Participant participant, final String id, final State state, final W3CEndpointReference coordinator, final boolean recovered)
{
this.participant = participant ;
this.id = id ;
this.state = state ;
this.coordinator = coordinator ;
this.recovered = recovered;
this.persisted = recovered;
this.initialResendPeriod = TransportTimer.getTransportPeriod();
this.maxResendPeriod = TransportTimer.getMaximumTransportPeriod();
this.resendPeriod = this.initialResendPeriod;
}
/**
* Handle the commit event.
* @param commit The commit notification.
* @param map The addressing context.
* @param arjunaContext The arjuna context.
*
* None -> None (send committed)
* Active -> Aborting (do nothing)
* Preparing -> Aborting (do nothing)
* PreparedSuccess -> Committing (initiate commit)
* Committing -> Committing (do nothing)
* Aborting -> Aborting (do nothing)
*/
public void commit(final Notification commit, final MAP map, final ArjunaContext arjunaContext)
{
final State current ;
synchronized(this)
{
current = state ;
if (current == State.STATE_PREPARED_SUCCESS)
{
state = State.STATE_COMMITTING ;
if (timerTask != null)
{
timerTask.cancel() ;
}
}
else if ((current == State.STATE_ACTIVE) || (current == State.STATE_PREPARING))
{
state = State.STATE_ABORTING ;
}
}
if (current == State.STATE_PREPARED_SUCCESS)
{
executeCommit() ;
}
else if (current == null)
{
sendCommitted() ;
}
}
/**
* Handle the prepare event.
* @param prepare The prepare notification.
* @param map The addressing context.
* @param arjunaContext The arjuna context.
*
* None -> None (send aborted)
* Active -> Preparing (execute prepare)
* Preparing -> Preparing (do nothing)
* PreparedSuccess -> PreparedSuccess (resend prepared)
* Committing -> Committing (ignore)
* Aborting -> Aborting (send aborted and forget)
*/
public void prepare(final Notification prepare, final MAP map, final ArjunaContext arjunaContext)
{
final State current ;
synchronized(this)
{
current = state ;
if (current == State.STATE_ACTIVE)
{
state = State.STATE_PREPARING ;
}
}
if (current == State.STATE_ACTIVE)
{
executePrepare() ;
}
else if (current == State.STATE_PREPARED_SUCCESS)
{
sendPrepared() ;
}
else if ((current == State.STATE_ABORTING) || (current == null))
{
sendAborted() ;
forget() ;
}
}
/**
* Handle the rollback event.
* @param rollback The rollback notification.
* @param map The addressing context.
* @param arjunaContext The arjuna context.
*
* None -> None (send aborted)
* Active -> Aborting (execute rollback, send aborted and forget)
* Preparing -> Aborting (execute rollback, send aborted and forget)
* PreparedSuccess -> Aborting (execute rollback, send aborted and forget)
* Committing -> Committing (ignore)
* Aborting -> Aborting (send aborted and forget)
*
*/
public void rollback(final Notification rollback, final MAP map, final ArjunaContext arjunaContext)
{
final State current ;
synchronized(this)
{
current = state ;
if ((current == State.STATE_ACTIVE) || (current == State.STATE_PREPARING) ||
(current == State.STATE_PREPARED_SUCCESS))
{
state = State.STATE_ABORTING ;
}
}
if ((current == State.STATE_ACTIVE) || (current == State.STATE_PREPARING) ||
(current == State.STATE_PREPARED_SUCCESS))
{
// n.b. if state is PREPARING the participant may still be in the middle
// of prepare or may even be told to prepare after this is called. according
// to the spec that is not our lookout. however, rollback should only get
// called once here.
if (!executeRollback())
{
synchronized (this)
{
state = current;
}
return ;
}
// if the participant managed to persist the log record then we should try
// to delete it. note that persisted can only be set to true by the PREPARING
// thread. if it detects a transtiion to ABORTING while it is doing the log write
// it will clear up itself.
if (persisted && participant instanceof Durable2PCParticipant) {
// if we cannot delete the participant we effectively drop the rollback message
// here in the hope that we have better luck next time..
if (!XTSATRecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
// hmm, could not delete entry -- leave it so we can maybe retry later
WSTLogger.i18NLogger.warn_wst11_messaging_engines_ParticipantEngine_rollback_1(id);
}
}
sendAborted() ;
forget() ;
}
else if (current != State.STATE_ABORTING)
{
sendAborted();
}
}
/**
* Handle the early rollback event.
*
* None -> None
* Active -> Aborting (execute rollback, send aborted and forget)
* Preparing -> Aborting (execute rollback, send aborted and forget)
* PreparedSuccess -> PreparedSuccess
* Committing -> Committing
* Aborting -> Aborting
*/
public void earlyRollback()
{
rollbackDecision() ;
}
/**
* Handle the early readonly event.
*
* None -> None
* Active -> None (send ReadOnly)
* Preparing -> None (send ReadOnly)
* PreparedSuccess -> PreparedSuccess
* Committing -> Committing
* Aborting -> Aborting
*/
public void earlyReadonly()
{
readOnlyDecision() ;
}
/**
* Handle the recovery event.
*
* None -> None
* Active -> Active
* Preparing -> Preparing
* Committing -> Committing
* PreparedSuccess -> PreparedSuccess (resend Prepared)
* Aborting -> Aborting
*/
public void recovery()
{
final State current ;
synchronized(this)
{
current = state ;
}
if (current == State.STATE_PREPARED_SUCCESS)
{
sendPrepared() ;
}
}
/**
* Handle the soap fault event.
* @param soapFault The soap fault.
* @param map The addressing context.
* @param arjunaContext The arjuna context.
*
*/
public void soapFault(final SoapFault soapFault, final MAP map, final ArjunaContext arjunaContext)
{
if (WSTLogger.logger.isTraceEnabled())
{
final InstanceIdentifier instanceIdentifier = arjunaContext.getInstanceIdentifier() ;
final SoapFaultType soapFaultType = soapFault.getSoapFaultType() ;
final QName subCode = soapFault.getSubcode() ;
WSTLogger.logger.tracev("Unexpected SOAP fault for participant {0}: {1} {2}", new Object[] {instanceIdentifier, soapFaultType, subCode}) ;
}
QName subcode = soapFault.getSubcode();
if (CoordinationConstants.WSCOOR_ERROR_CODE_INVALID_STATE_QNAME.equals(subcode) ||
AtomicTransactionConstants.WSAT_ERROR_CODE_INCONSISTENT_INTERNAL_STATE_QNAME.equals(subcode) ||
AtomicTransactionConstants.WSAT_ERROR_CODE_UNKNOWN_TRANSACTION_QNAME.equals(subcode))
{
final SoapFaultType soapFaultType = soapFault.getSoapFaultType() ;
final QName subCode = soapFault.getSubcode() ;
WSTLogger.i18NLogger.error_wst11_messaging_engines_ParticipantEngine_soapFault_2(id, soapFaultType.toString(), subCode);
// unrecoverable error -- forget this participant and delete any persistent
// record of it
final State current ;
synchronized(this)
{
current = state;
state = null;
}
if (current == State.STATE_PREPARED_SUCCESS &&
AtomicTransactionConstants.WSAT_ERROR_CODE_UNKNOWN_TRANSACTION_QNAME.equals(subcode)) {
// we need to tell this participant to roll back
executeRollback();
}
if (persisted && participant instanceof Durable2PCParticipant) {
// remove any durable participant recovery record from the persistent store
Durable2PCParticipant durableParticipant =(Durable2PCParticipant) participant;
// if we cannot delete the participant we record an error here
if (!XTSATRecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
// hmm, could not delete entry -- log an error
WSTLogger.i18NLogger.error_wst11_messaging_engines_ParticipantEngine_soapFault_3(id);
}
}
forget() ;
}
}
/**
* Handle the commit decision event.
*
* Preparing -> PreparedSuccess (send Prepared)
* Committing -> null (send committed and forget)
*/
private void commitDecision()
{
State current ;
boolean rollbackRequired = false;
boolean deleteRequired = false;
synchronized(this)
{
current = state ;
}
if (current == State.STATE_PREPARING)
{
// ok, we need to write the recovery details to log and send prepared.
// if we cannot write the log then we have to rollback the participant
// and send aborted.
if (participant instanceof Durable2PCParticipant) {
// write a durable participant recovery record to the persistent store
Durable2PCParticipant durableParticipant =(Durable2PCParticipant) participant;
ATParticipantRecoveryRecord recoveryRecord = new ATParticipantRecoveryRecord(id, durableParticipant, coordinator);
if (!XTSATRecoveryManager.getRecoveryManager().writeParticipantRecoveryRecord(recoveryRecord)) {
// we need to rollback and send aborted unless some other thread
//gets there first
rollbackRequired = true;
}
}
// recheck state in case a rollback or readonly came in while we were writing the
// log record
synchronized (this) {
current = state;
if (current == State.STATE_PREPARING) {
if (rollbackRequired) {
// if we change state to aborting then we are responsible for
// calling rollback and sending aborted but we have no log record
// to delete
state = State.STATE_ABORTING;
} else {
state = State.STATE_PREPARED_SUCCESS;
// this ensures any subsequent commit or rollback deletes the log record
// so we still have no log record to delete here
persisted = true;
}
} else if (!rollbackRequired) {
// an incoming rollback or readonly changed the state to aborted or null so
// it will already have performed a rollback if required but we need to
// delete the log record since the rollback/readonly thread did not know
// about it
deleteRequired = true;
}
}
if (rollbackRequired)
{
// we need to do the rollback and send aborted
executeRollback();
sendAborted();
forget();
} else if (deleteRequired) {
// just try to delete the log entry -- any required aborted has already been sent
if (!XTSATRecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
// hmm, could not delete entry log warning
WSTLogger.i18NLogger.warn_wst11_messaging_engines_ParticipantEngine_commitDecision_2(id);
}
} else {
// whew got through -- send a prepared
sendPrepared() ;
}
}
else if (current == State.STATE_COMMITTING)
{
if (persisted && participant instanceof Durable2PCParticipant) {
// remove any durable participant recovery record from the persistent store
Durable2PCParticipant durableParticipant =(Durable2PCParticipant) participant;
// if we cannot delete the participant we effectively drop the commit message
// here in the hope that we have better luck next time.
if (!XTSATRecoveryManager.getRecoveryManager().deleteParticipantRecoveryRecord(id)) {
// hmm, could not delete entry -- log a warning
WSTLogger.i18NLogger.warn_wst11_messaging_engines_ParticipantEngine_commitDecision_3(id);
// now revert back to PREPARED_SUCCESS and drop message awaiting a retry
synchronized(this) {
state = State.STATE_PREPARED_SUCCESS;
}
return;
}
}
sendCommitted() ;
forget() ;
}
}
/**
* Handle the readOnly decision event.
*
* Active -> None (send ReadOnly)
* Preparing -> None (send ReadOnly)
*/
private void readOnlyDecision()
{
final State current ;
synchronized(this)
{
current = state ;
if ((current == State.STATE_ACTIVE) || (current == State.STATE_PREPARING))
{
state = null ;
}
}
if ((current == State.STATE_ACTIVE) || (current == State.STATE_PREPARING))
{
sendReadOnly() ;
forget() ;
}
}
/**
* Handle the rollback decision event.
*
* Active -> Aborting (send aborted)
* Preparing -> Aborting (send aborted)
*/
private void rollbackDecision()
{
final State current ;
synchronized(this)
{
current = state ;
if ((current == State.STATE_PREPARING) || (current == State.STATE_ACTIVE))
{
state = State.STATE_ABORTING ;
}
}
if ((current == State.STATE_PREPARING) || (current == State.STATE_ACTIVE))
{
sendAborted() ;
forget() ;
}
}
/**
* Handle the comms timeout event.
*
* PreparedSuccess -> PreparedSuccess (resend Prepared)
*/
private void commsTimeout(TimerTask caller)
{
final State current ;
synchronized(this)
{
if (!timerTask.equals(caller)) {
// the timer was cancelled but it went off before it could be cancelled
return;
}
// double the resend period up to our maximum limit
if (resendPeriod < maxResendPeriod) {
resendPeriod = resendPeriod * 14 / 10; // approximately doubles every two resends
if (resendPeriod > maxResendPeriod) {
resendPeriod = maxResendPeriod;
}
}
current = state ;
}
if (current == State.STATE_PREPARED_SUCCESS)
{
sendPrepared() ;
}
}
/**
* Execute the commit transition.
*
*/
private void executeCommit()
{
try
{
participant.commit() ;
commitDecision() ;
}
catch (final Throwable th)
{
synchronized(this)
{
if (state == State.STATE_COMMITTING)
{
state = State.STATE_PREPARED_SUCCESS ;
}
}
if (WSTLogger.logger.isTraceEnabled())
{
WSTLogger.logger.tracev("Unexpected exception from participant commit", th) ;
}
}
}
/**
* Execute the rollback transition.
*
*/
private boolean executeRollback()
{
try
{
participant.rollback() ;
}
catch (final SystemException se)
{
return false ;
}
catch (final Throwable th)
{
if (WSTLogger.logger.isTraceEnabled())
{
WSTLogger.logger.tracev("Unexpected exception from participant rollback", th) ;
}
}
return true ;
}
/**
* Execute the prepare transition.
*
*/
private void executePrepare()
{
final Vote vote ;
try
{
vote = participant.prepare();
}
catch (final SystemException se)
{
if (WSTLogger.logger.isTraceEnabled())
{
WSTLogger.logger.tracev("Unexpected exception from participant prepare", se) ;
}
return ;
}
catch (final Throwable th)
{
if (WSTLogger.logger.isTraceEnabled())
{
WSTLogger.logger.tracev("Unexpected exception from participant prepare", th) ;
}
rollbackDecision() ;
return ;
}
if (vote instanceof Prepared)
{
commitDecision() ;
}
else if (vote instanceof ReadOnly)
{
readOnlyDecision() ;
}
else if (vote instanceof Aborted)
{
rollbackDecision() ;
}
else
{
if (WSTLogger.logger.isTraceEnabled())
{
WSTLogger.logger.tracev("Unexpected result from participant prepare: {0}", new Object[] {(vote == null ? "null" : vote.getClass().getName())});
}
rollbackDecision() ;
}
}
/**
* Forget the current participant.
*/
private void forget()
{
synchronized(this)
{
state = null ;
}
ParticipantProcessor.getProcessor().deactivateParticipant(this) ;
}
/**
* Send the committed message.
*
*/
private void sendCommitted()
{
final MAP responseAddressingContext = createContext() ;
final InstanceIdentifier instanceIdentifier = new InstanceIdentifier(id) ;
try
{
CoordinatorClient.getClient().sendCommitted(coordinator, responseAddressingContext, instanceIdentifier) ;
}
catch (final Throwable th)
{
if (WSTLogger.logger.isTraceEnabled())
{
WSTLogger.logger.tracev("Unexpected exception while sending Committed", th) ;
}
}
}
/**
* Send the prepared message.
*
*/
private void sendPrepared()
{
sendPrepared(false);
}
/**
* Send the prepared message.
*
* @param timedOut true if this is in response to a comms timeout
*/
private void sendPrepared(boolean timedOut)
{
final MAP responseAddressingContext = createContext() ;
final InstanceIdentifier instanceIdentifier = new InstanceIdentifier(id) ;
try
{
CoordinatorClient.getClient().sendPrepared(coordinator, responseAddressingContext, instanceIdentifier) ;
}
catch (final Throwable th)
{
if (WSTLogger.logger.isTraceEnabled())
{
WSTLogger.logger.tracev("Unexpected exception while sending Prepared", th) ;
}
}
updateResendPeriod(timedOut);
initiateTimer() ;
}
private synchronized void updateResendPeriod(boolean timedOut)
{
// if we timed out then we multiply the resend period by ~= sqrt(2) up to the maximum
// if not we make sure it is reset to the initial period
if (timedOut) {
if (resendPeriod < maxResendPeriod) {
long newPeriod = resendPeriod * 14 / 10; // approximately doubles every two resends
if (newPeriod > maxResendPeriod) {
newPeriod = maxResendPeriod;
}
resendPeriod = newPeriod;
}
} else {
if (resendPeriod > initialResendPeriod) {
resendPeriod = initialResendPeriod;
}
}
}
/**
* Send the aborted message.
*
*/
private void sendAborted()
{
final MAP responseAddressingContext = createContext() ;
final InstanceIdentifier instanceIdentifier = new InstanceIdentifier(id) ;
try
{
CoordinatorClient.getClient().sendAborted(coordinator, responseAddressingContext, instanceIdentifier) ;
}
catch (final Throwable th)
{
if (WSTLogger.logger.isTraceEnabled())
{
WSTLogger.logger.tracev("Unexpected exception while sending Aborted", th) ;
}
}
}
/**
* Send the read only message.
*
*/
private void sendReadOnly()
{
final MAP responseAddressingContext = createContext() ;
final InstanceIdentifier instanceIdentifier = new InstanceIdentifier(id) ;
try
{
CoordinatorClient.getClient().sendReadOnly(coordinator, responseAddressingContext, instanceIdentifier) ;
}
catch (final Throwable th)
{
if (WSTLogger.logger.isTraceEnabled())
{
WSTLogger.logger.tracev("Unexpected exception while sending ReadOnly", th) ;
}
}
}
/**
* Initiate the timer.
*/
private synchronized void initiateTimer()
{
if (timerTask != null)
{
timerTask.cancel() ;
}
if (state == State.STATE_PREPARED_SUCCESS)
{
timerTask = new TimerTask() {
public void run() {
commsTimeout(this) ;
}
} ;
TransportTimer.getTimer().schedule(timerTask, resendPeriod) ;
}
else
{
timerTask = null ;
}
}
/**
* Create a response context from the incoming context.
* @return The addressing context.
*/
private MAP createContext()
{
final String messageId = MessageId.getMessageId() ;
return AddressingHelper.createNotificationContext(messageId) ;
}
/**
* Get the coordinator id.
* @return The coordinator id.
*/
public String getId()
{
return id ;
}
public W3CEndpointReference getCoordinator()
{
return coordinator;
}
/**
* Is the participant persisted to disk?
* @return true if persisted, false otherwise.
*/
public boolean isPersisted()
{
return persisted ;
}
/**
* Is the participant recovered?
* @return true if recovered, false otherwise.
*/
public boolean isRecovered()
{
return recovered ;
}
}