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.
/**
* Copyright (C) 2000-2012 Atomikos
*
* This code ("Atomikos TransactionsEssentials"), by itself,
* is being distributed under the
* Apache License, Version 2.0 ("License"), a copy of which may be found at
* http://www.atomikos.com/licenses/apache-license-2.0.txt .
* You may not use this file except in compliance with the License.
*
* While the License grants certain patent license rights,
* those patent license rights only extend to the use of
* Atomikos TransactionsEssentials by itself.
*
* This code (Atomikos TransactionsEssentials) contains certain interfaces
* in package (namespace) com.atomikos.icatch
* (including com.atomikos.icatch.Participant) which, if implemented, may
* infringe one or more patents held by Atomikos.
* It should be appreciated that you may NOT implement such interfaces;
* licensing to implement these interfaces must be obtained separately from Atomikos.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*/
package com.atomikos.icatch.imp;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import com.atomikos.finitestates.FSM;
import com.atomikos.finitestates.FSMEnterEvent;
import com.atomikos.finitestates.FSMEnterListener;
import com.atomikos.finitestates.FSMImp;
import com.atomikos.finitestates.FSMPreEnterListener;
import com.atomikos.finitestates.FSMTransitionEvent;
import com.atomikos.finitestates.FSMTransitionListener;
import com.atomikos.finitestates.Stateful;
import com.atomikos.icatch.CompositeCoordinator;
import com.atomikos.icatch.HeurCommitException;
import com.atomikos.icatch.HeurHazardException;
import com.atomikos.icatch.HeurMixedException;
import com.atomikos.icatch.HeurRollbackException;
import com.atomikos.icatch.HeuristicMessage;
import com.atomikos.icatch.Participant;
import com.atomikos.icatch.RecoveryCoordinator;
import com.atomikos.icatch.RollbackException;
import com.atomikos.icatch.StringHeuristicMessage;
import com.atomikos.icatch.Synchronization;
import com.atomikos.icatch.SysException;
import com.atomikos.icatch.TxState;
import com.atomikos.icatch.event.Event;
import com.atomikos.icatch.event.transaction.TransactionAbortedEvent;
import com.atomikos.icatch.event.transaction.TransactionCommittedEvent;
import com.atomikos.icatch.event.transaction.TransactionCreatedEvent;
import com.atomikos.icatch.event.transaction.TransactionHeuristicEvent;
import com.atomikos.icatch.event.transaction.TransactionReadOnlyEvent;
import com.atomikos.logging.Logger;
import com.atomikos.logging.LoggerFactory;
import com.atomikos.persistence.ObjectImage;
import com.atomikos.persistence.StateRecoverable;
import com.atomikos.publish.EventPublisher;
import com.atomikos.thread.TaskManager;
import com.atomikos.timing.AlarmTimer;
import com.atomikos.timing.AlarmTimerListener;
import com.atomikos.timing.PooledAlarmTimer;
/**
*
* All things related to termination logic.
*
*/
public class CoordinatorImp implements CompositeCoordinator, Participant,
RecoveryCoordinator, StateRecoverable, AlarmTimerListener, Stateful,
FSMPreEnterListener, FSMTransitionListener, FSMEnterListener
{
private static final Logger LOGGER = LoggerFactory.createLogger(CoordinatorImp.class);
static long DEFAULT_MILLIS_BETWEEN_TIMER_WAKEUPS = 150;
// SHOULD NOT BE BIG, otherwise lots of sleeping threads -> OUT OF MEMORY!
private static final int MAX_NUMBER_OF_TIMEOUT_TICKS_FOR_INDOUBTS = 30;
private static final int MAX_NUMBER_OF_TIMEOUT_TICKS_BEFORE_ROLLBACK_OF_ACTIVES = 30;
private int localSiblingCount_ = 0;
private AlarmTimer timer_ = null;
private boolean checkSiblings_ = true;
private long maxNumberOfTimeoutTicksBeforeHeuristicDecision_ = MAX_NUMBER_OF_TIMEOUT_TICKS_FOR_INDOUBTS;
private long maxNumberOfTimeoutTicksBeforeRollback_ = MAX_NUMBER_OF_TIMEOUT_TICKS_BEFORE_ROLLBACK_OF_ACTIVES;
private String root_ = null;
private FSM fsm_ = null;
private boolean recoverableWhileActive_;
private boolean heuristicMeansCommit_ = true;
private Vector participants_ = new Vector();
private RecoveryCoordinator superiorCoordinator_ = null;
private Vector tags_ = new Vector();
// the tags of all incoming txs
// does NOT have to be logged: the contents are
// retrieved BEFORE prepare (in Participant proxy),
// at return time of the call.
private CoordinatorStateHandler stateHandler_;
private boolean single_threaded_2pc_;
private transient List synchronizations;
/**
* Constructor for testing only.
*/
protected CoordinatorImp ( String root , boolean heuristic_commit ,
boolean checkorphans )
{
root_ = root;
initFsm(TxState.ACTIVE);
heuristicMeansCommit_ = heuristic_commit;
setStateHandler ( new ActiveStateHandler ( this ) );
startThreads ( DEFAULT_MILLIS_BETWEEN_TIMER_WAKEUPS );
checkSiblings_ = checkorphans;
single_threaded_2pc_ = false;
synchronizations = new ArrayList();
}
private void initFsm(TxState initialState) {
fsm_ = new FSMImp ( this, new TransactionTransitionTable (),
initialState );
fsm_.addFSMPreEnterListener ( this, TxState.TERMINATED );
fsm_.addFSMPreEnterListener ( this, TxState.HEUR_COMMITTED );
fsm_.addFSMPreEnterListener ( this, TxState.HEUR_ABORTED );
fsm_.addFSMPreEnterListener ( this, TxState.HEUR_MIXED );
fsm_.addFSMPreEnterListener ( this, TxState.HEUR_HAZARD );
fsm_.addFSMTransitionListener ( this, TxState.COMMITTING, TxState.TERMINATED );
fsm_.addFSMTransitionListener ( this, TxState.ABORTING, TxState.TERMINATED );
fsm_.addFSMTransitionListener ( this, TxState.PREPARING, TxState.TERMINATED);
fsm_.addFSMEnterListener(this, TxState.HEUR_COMMITTED);
fsm_.addFSMEnterListener(this, TxState.HEUR_ABORTED);
fsm_.addFSMEnterListener(this, TxState.HEUR_HAZARD);
fsm_.addFSMEnterListener(this, TxState.HEUR_MIXED);
}
/**
* Constructor.
*
* @param root
* The root tid.
* @param coord
* The RecoverCoordinator, null if root.
* @param console
* The console to log to, or null if none.
* @param heuristic_commit
* Whether to do commit on heuristic.
* @param timeout
* The timeout in milliseconds for indoubts before a heuristic
* decision is made.
* @param checkorphans
* If true, orphan checks are made on prepare. For OTS, this is
* false.
* @param single_threaded_2pc
* If true then commit is done in the same thread as the one that
* started the tx.
*/
protected CoordinatorImp ( String root , RecoveryCoordinator coord ,
boolean heuristic_commit ,
long timeout , boolean checkorphans , boolean single_threaded_2pc )
{
root_ = root;
single_threaded_2pc_ = single_threaded_2pc;
initFsm(TxState.ACTIVE );
heuristicMeansCommit_ = heuristic_commit;
recoverableWhileActive_ = false;
superiorCoordinator_ = coord;
if ( timeout > DEFAULT_MILLIS_BETWEEN_TIMER_WAKEUPS ) {
// If timeout is smaller than the default timeout, then
// there is no need to re-adjust the next two fields
// since the defaults will be used.
maxNumberOfTimeoutTicksBeforeHeuristicDecision_ = timeout / DEFAULT_MILLIS_BETWEEN_TIMER_WAKEUPS;
maxNumberOfTimeoutTicksBeforeRollback_ = maxNumberOfTimeoutTicksBeforeHeuristicDecision_;
}
setStateHandler ( new ActiveStateHandler ( this ) );
startThreads ( DEFAULT_MILLIS_BETWEEN_TIMER_WAKEUPS );
checkSiblings_ = checkorphans;
synchronizations = new ArrayList();
publishDomainEvent(new TransactionCreatedEvent(root_));
}
/**
* Constructor.
*
* @param root
* The root String for this one.
* @param console
* The console to log to, or null if none.
* @param coord
* The recovery coordinator for indoubt resolution.
* @param heuristic_commit
* If true, heuristic decision is commit.
* @param checkorphans
* If true, orphan checking is done at prepare.
*/
public CoordinatorImp ( String root , RecoveryCoordinator coord ,
boolean heuristic_commit , boolean checkorphans )
{
this ( root , coord , heuristic_commit ,
DEFAULT_MILLIS_BETWEEN_TIMER_WAKEUPS , checkorphans , false );
}
/**
* No argument constructor as required by Recoverable interface.
*/
public CoordinatorImp ()
{
initFsm(TxState.ACTIVE );
heuristicMeansCommit_ = false;
checkSiblings_ = true;
recoverableWhileActive_ = false;
single_threaded_2pc_ = false;
synchronizations = new ArrayList();
}
boolean prefersSingleThreaded2PC()
{
return single_threaded_2pc_;
}
/**
* Mark the tx as committed. Needed for testing.
*/
void setCommitted ()
{
stateHandler_.setCommitted ();
}
void addTag ( HeuristicMessage tag )
{
synchronized ( fsm_ ) {
if ( tag != null )
tags_.addElement ( tag );
}
}
/**
* Set the state handler. This method should always be preferred over
* calling setState directly.
*
* @param stateHandler
* The next state handler.
*/
void setStateHandler ( CoordinatorStateHandler stateHandler )
{
// NB: if this method is synchronized then deadlock happens on heuristic mixed!
TxState state = stateHandler.getState ();
stateHandler_ = stateHandler;
setState ( state );
}
RecoveryCoordinator getSuperiorRecoveryCoordinator ()
{
return superiorCoordinator_;
}
Vector getParticipants ()
{
return participants_;
}
boolean prefersHeuristicCommit ()
{
return heuristicMeansCommit_;
}
int getLocalSiblingCount ()
{
return localSiblingCount_;
}
long getMaxIndoubtTicks ()
{
return maxNumberOfTimeoutTicksBeforeHeuristicDecision_;
}
long getMaxRollbackTicks ()
{
return maxNumberOfTimeoutTicksBeforeRollback_;
}
boolean checkSiblings ()
{
return checkSiblings_;
}
public Boolean isRecoverableWhileActive()
{
return new Boolean ( recoverableWhileActive_ );
}
/**
* Gets the heuristic messages for all participants that are in the given
* heuristic state
*
* @param heuristicState
* The heuristic state, or the terminated state.
* @return HeuristicMessage[] The heuristic messages of all participants in
* the given state, or an empty array if none.
*/
public HeuristicMessage[] getHeuristicMessages (
Object heuristicState )
{
//NB: don't synchronize, to avoid blocks in recursive 2PC
return stateHandler_.getHeuristicMessages ( heuristicState );
}
/**
* Tests if the transaction was committed or not.
*
* @return boolean True iff committed.
*/
public boolean isCommitted ()
{
return stateHandler_.isCommitted ();
}
/**
* Get the heuristic info for the message round.
*
* @return HeuristicMessages[] The heuristic messages, or an empty array if
* none.
*/
public HeuristicMessage[] getHeuristicMessages ()
{
// NB: don't synchronize, to avoid blocks in recursive 2PC
return stateHandler_.getHeuristicMessages ();
}
/**
* Get the heuristic tags for this coordinator. This info is returned to
* remote client TMs by a Participant proxy.
*/
public HeuristicMessage[] getTags ()
{
HeuristicMessage[] template = null;
synchronized ( fsm_ ) {
template = new HeuristicMessage[tags_.size ()];
for ( int i = 0; i < template.length; i++ ) {
template[i] = (HeuristicMessage) tags_.elementAt ( i );
}
}
return template;
}
/**
* Start threads, propagator and timer logic. Needed on construction AND by
* replay request events: timers have stopped by then!
*
* @param timeout
* The timeout for the thread wakeup interval.
* @param console
* The console, null if none.
*/
protected void startThreads ( long timeout )
{
synchronized ( fsm_ ) {
if ( timer_ == null ) { //not null for repeated recovery
stateHandler_.activate ();
timer_ = new PooledAlarmTimer(timeout);
timer_.addAlarmTimerListener(this);
submitTimer(timer_);
}
}
}
private void submitTimer(AlarmTimer timer) {
TaskManager.getInstance().executeTask (timer);
}
protected long getTimeOut ()
{
return (maxNumberOfTimeoutTicksBeforeRollback_ - stateHandler_.getRollbackTicks ())
* DEFAULT_MILLIS_BETWEEN_TIMER_WAKEUPS;
}
void setState ( TxState state ) throws IllegalStateException
{
if ( LOGGER.isDebugEnabled() ) LOGGER.logDebug ( "Coordinator " + getCoordinatorId ()
+ " entering state: " + state.toString () );
fsm_.setState ( state );
}
/**
* @see Stateful
*/
public TxState getState ()
{
// this method should NOT be synchronized to avoid
// recursive 2PC deadlocks!
return fsm_.getState ();
}
/**
* @see FSMEnterEventSource.
*/
public void addFSMEnterListener ( FSMEnterListener l ,
TxState state )
{
fsm_.addFSMEnterListener ( l, state );
}
/*
* @see FSMPreEnterEventSource.
*/
public void addFSMPreEnterListener ( FSMPreEnterListener l ,
TxState state )
{
fsm_.addFSMPreEnterListener ( l, (TxState)state );
}
/**
* @see CompositeCoordinator.
*/
public RecoveryCoordinator getRecoveryCoordinator ()
{
return this;
}
/**
* @see CompositeCoordinator.
*/
public Participant getParticipant () throws UnsupportedOperationException
{
return this;
}
/**
* @see com.atomikos.icatch.CompositeCoordinator.
*/
public String getCoordinatorId ()
{
return root_;
}
public RecoveryCoordinator addParticipant (
Participant participant ) throws SysException,
java.lang.IllegalStateException, RollbackException
{
synchronized ( fsm_ ) {
if ( !getState ().equals ( TxState.ACTIVE ) )
throw new IllegalStateException (
getCoordinatorId() +
" is no longer active but in state " +
getState ().toString () );
//FIRST add participant, THEN set state to support active recovery
if ( !participants_.contains ( participant ) ) {
participants_.add ( participant );
}
//make sure that aftercompletion notification is done.
setState ( TxState.ACTIVE );
}
return this;
}
/**
* Called when a tx import is being done.
*/
protected void incLocalSiblingCount ()
{
synchronized ( fsm_ ) {
localSiblingCount_++;
}
}
void registerSynchronization ( Synchronization sync )
throws RollbackException, IllegalStateException,
UnsupportedOperationException, SysException
{
synchronized ( fsm_ ) {
if ( !getState ().equals ( TxState.ACTIVE ) )
throw new IllegalStateException ( "wrong state: " + getState () );
rememberSychronizationForAfterCompletion(sync);
}
}
private void rememberSychronizationForAfterCompletion(Synchronization sync) {
getSynchronizations().add(sync);
}
private List getSynchronizations() {
synchronized(fsm_) {
if (synchronizations == null) synchronizations = new ArrayList();
return synchronizations;
}
}
void notifySynchronizationsAfterCompletion(TxState... successiveStates) {
for ( TxState state : successiveStates ) {
for (Synchronization s : getSynchronizations()) {
try {
s.afterCompletion(state);
} catch (Throwable t) {
LOGGER.logWarning("Unexpected error in afterCompletion", t);
}
}
}
}
/**
* @see FSMPreEnterListener.
*/
public void preEnter ( FSMEnterEvent event ) throws IllegalStateException
{
TxState state = event.getState ();
if ( state.equals ( TxState.TERMINATED )
|| state.equals ( TxState.HEUR_ABORTED )
|| state.equals ( TxState.HEUR_COMMITTED )
|| state.equals ( TxState.HEUR_HAZARD )
|| state.equals ( TxState.HEUR_MIXED ) ) {
if ( !state.equals ( TxState.TERMINATED ) )
LOGGER.logWarning ( "Local heuristic termination of coordinator "
+ root_ + " with state " + getState () );
else
dispose ();
}
}
/**
* @see Participant
*/
public String getURI ()
{
return getCoordinatorId ();
}
/**
* @see Participant
*/
public boolean recover () throws SysException
{
boolean allOK = true;
boolean ret;
if ( LOGGER.isDebugEnabled() ){
LOGGER.logDebug ( "starting recover() for coordinator: " + getCoordinatorId () );
}
synchronized ( fsm_ ) {
// cf case 61686 and case 62217: avoid concurrent enlists while recovering
Iterator parts = getParticipants().iterator();
while (parts.hasNext()) {
Participant next = (Participant) parts.next();
boolean recoveredParticipant = false;
try {
recoveredParticipant = next.recover();
if ( LOGGER.isDebugEnabled() ) LOGGER.logDebug ( "coordinator: " + getCoordinatorId()
+ "recovered participant: " + next );
} catch (Exception e) {
// happens if XA connection could not be gotten or other problems
LOGGER.logWarning("Error in recovering participant");
StackTraceElement[] infos = e.getStackTrace();
for (int i = 0; i < infos.length; i++) {
LOGGER.logWarning(infos[i].toString());
}
// do NOT throw any exception: tolerate this to let the coordinator do the rest
}
allOK = allOK && recoveredParticipant;
}
stateHandler_.recover(this);
ret = !(!allOK && getState().equals(TxState.IN_DOUBT));
} // synchronized
// ONLY NOW start threads and so on
startThreads ( DEFAULT_MILLIS_BETWEEN_TIMER_WAKEUPS );
if ( LOGGER.isDebugEnabled() ) LOGGER.logDebug ( "recover() done for coordinator: " + getCoordinatorId () );
return ret;
}
/**
* @see Participant.
*/
public void forget ()
{
stateHandler_.forget ();
}
/**
* @see Participant.
*/
public void setCascadeList ( java.util.Dictionary allParticipants )
throws SysException
{
stateHandler_.setCascadeList ( allParticipants );
}
/**
* @see Participant.
*/
public void setGlobalSiblingCount ( int count )
{
stateHandler_.setGlobalSiblingCount ( count );
}
/**
* @see Participant.
*/
public int prepare () throws RollbackException,
java.lang.IllegalStateException, HeurHazardException,
HeurMixedException, SysException
{
// FIRST, TAKE CARE OF DUPLICATE PREPARES
// Recursive prepare-calls should be avoided for not deadlocking rollback/commit methods
// If a recursive prepare re-enters, then it will see a voting state -> reject.
// Note that this may also avoid some legal prepares, but only rarely
if ( getState ().equals ( TxState.PREPARING ) )
throw new RollbackException ( "Recursion detected" );
int ret = Participant.READ_ONLY + 1;
synchronized ( fsm_ ) {
ret = stateHandler_.prepare ();
if ( ret == Participant.READ_ONLY ) {
if ( LOGGER.isDebugEnabled() ) LOGGER.logDebug ( "prepare() of Coordinator " + getCoordinatorId ()
+ " returning READONLY" );
} else {
if ( LOGGER.isDebugEnabled() ) LOGGER.logDebug ( "prepare() of Coordinator " + getCoordinatorId ()
+ " returning YES vote");
}
}
return ret;
}
/**
* @see Participant.
*/
public HeuristicMessage[] commit ( boolean onePhase )
throws HeurRollbackException, HeurMixedException,
HeurHazardException, java.lang.IllegalStateException,
RollbackException, SysException
{
HeuristicMessage[] ret = null;
synchronized ( fsm_ ) {
ret = stateHandler_.commit(onePhase);
}
return ret;
}
/**
* @see Participant.
*/
public HeuristicMessage[] rollback () throws HeurCommitException,
HeurMixedException, SysException, HeurHazardException,
java.lang.IllegalStateException
{
HeuristicMessage[] ret = null;
if ( getState ().equals ( TxState.ABORTING ) ) {
// this method is ONLY called for EXTERNAL events -> by remote coordinators
// therefore, state aborting means either a recursive
// call or a concurrent rollback by two different coordinators.
// Recursion can be detected by this state, because the
// original call will still be in its propagation phase,
// where the state is set to ABORTING.
// Returning immediately will make sure no
// deadlock happens during 2PC, especially for recursion!.
// NOTE that if heuristic problems arise, the first call will always
// return the heuristic info and set the heuristic state.
return getHeuristicMessages ();
}
// here, we are certain that no RECURSIVE call is going on,
// so we can safely lock this instance.
synchronized ( fsm_ ) {
ret = stateHandler_.rollback();
}
return ret;
}
public HeuristicMessage[] rollbackHeuristically ()
throws HeurCommitException, HeurMixedException, SysException,
HeurHazardException, java.lang.IllegalStateException
{
HeuristicMessage[] ret = null;
synchronized ( fsm_ ) {
ret = stateHandler_.rollbackHeuristically();
}
return ret;
}
public HeuristicMessage[] commitHeuristically () throws HeurMixedException,
SysException, HeurRollbackException, HeurHazardException,
java.lang.IllegalStateException, RollbackException
{
HeuristicMessage[] ret = null;
synchronized ( fsm_ ) {
ret = stateHandler_.commitHeuristically();
}
return ret;
}
/**
* @see RecoveryCoordinator.
*/
public Boolean replayCompletion ( Participant participant )
throws IllegalStateException
{
if(LOGGER.isInfoEnabled()){
LOGGER.logInfo("replayCompletion ( " + participant
+ " ) received by coordinator " + getCoordinatorId ()
+ " for participant " + participant.toString ());
}
Boolean ret = null;
synchronized ( fsm_ ) {
ret = stateHandler_.replayCompletion ( participant );
}
return ret;
}
/**
* Help function for restoration.
*/
protected void restore ( ObjectImage image )
{
CoordinatorLogImage img = (CoordinatorLogImage) image;
root_ = img.root_;
participants_ = img.participants_;
superiorCoordinator_ = img.coordinator_;
heuristicMeansCommit_ = img.heuristicCommit_;
maxNumberOfTimeoutTicksBeforeHeuristicDecision_ = img.maxInquiries_;
maxNumberOfTimeoutTicksBeforeRollback_ = img.maxInquiries_;
recoverableWhileActive_ = img.activity_;
if ( recoverableWhileActive_ ) {
checkSiblings_ = img.checkSiblings_;
localSiblingCount_ = img.localSiblingCount_;
}
initFsm(img.state_);
stateHandler_ = img.stateHandler_;
if ( img.state_.equals ( TxState.COMMITTING )
&& stateHandler_.getState ().equals ( TxState.ACTIVE ) ) {
// this is a recovered coordinator that was committing
// ONE-PHASE; make this a heuristic hazard so it can
// be terminated manually if desired (LogAdministrator)
CoordinatorStateHandler stateHandler = new HeurHazardStateHandler (
stateHandler_, img.participants_ );
stateHandler.recover ( this );
setStateHandler ( stateHandler );
}
single_threaded_2pc_ = img.single_threaded_2pc_;
}
/**
* @see com.atomikos.persistence.Recoverable
*/
public ObjectImage getObjectImage ()
{
synchronized ( fsm_ ) {
return getObjectImage ( getState () );
}
}
/**
* @see com.atomikos.persistence.StateRecoverable
*/
public ObjectImage getObjectImage ( TxState state )
{
// IF VOTING: RETURN LIST OF ALL PARTICIPANTS
// IF INDOUBT: RETURN LIST OF INDOUBTS AND NOT READONLY
// IF COMMIT/ABORT: RETURN LIST OF REMAINING ACK
CoordinatorLogImage ret = null;
synchronized ( fsm_ ) {
if ( excludedFromLogging(state)) {
//merely return null to avoid logging overhead
ret = null;
}
else {
TxState imgstate = state;
if ( recoverableWhileActive_ ) {
ret = new CoordinatorLogImage ( root_, imgstate, participants_,
superiorCoordinator_, heuristicMeansCommit_, maxNumberOfTimeoutTicksBeforeHeuristicDecision_,
stateHandler_, localSiblingCount_, checkSiblings_ , single_threaded_2pc_);
} else {
ret = new CoordinatorLogImage ( root_, imgstate, participants_,
superiorCoordinator_, heuristicMeansCommit_, maxNumberOfTimeoutTicksBeforeHeuristicDecision_,
stateHandler_ , single_threaded_2pc_ );
}
}
}
return ret;
}
private boolean excludedFromLogging(Object state) {
boolean ret = false;
if (state.equals ( TxState.ACTIVE ) && !recoverableWhileActive_) {
ret = true;
} else if ( superiorCoordinator_ == null) {
if ( state.equals( TxState.IN_DOUBT )) {
ret = true; //see case 23693: don't log prepared state for roots
} else if ( participants_.size() == 0 ) {
ret = true; //see case 84851: avoid logging overhead for empty transactions
}
}
return ret;
}
/**
* @see com.atomikos.persistence.StateRecoverable
*/
public TxState[] getRecoverableStates ()
{
// NOTE: make sure COMMITTING is recoverable as well,
// in order to be able to recover the commit decision!
// This also prevents anomalous notification of participants
// when immediate shutdown leaves active coordinator threads
// behind, because the forced log write on COMMIT will prevent
// pending coordinators' commit decisions if the log service is down!
// NOTE:: active state is recoverable, but if feature is disabled then
// a null image will be returned to avoid log overhead
return new TxState[] { TxState.ACTIVE , TxState.IN_DOUBT, TxState.COMMITTING,
TxState.HEUR_COMMITTED, TxState.HEUR_ABORTED,
TxState.HEUR_HAZARD, TxState.HEUR_MIXED };
}
/**
* @see com.atomikos.persistence.StateRecoverable
*/
public TxState[] getFinalStates ()
{
return new TxState[] { TxState.TERMINATED };
}
/**
* @see com.atomikos.persistence.Recoverable
*/
public Object getId ()
{
return root_;
}
public void alarm ( AlarmTimer timer )
{
try {
stateHandler_.onTimeout ();
} catch ( Exception e ) {
LOGGER.logWarning( "Exception on timeout of coordinator " + root_ , e );
}
}
protected void dispose ()
{
synchronized ( fsm_ ) {
if ( timer_ != null ) {
if ( LOGGER.isDebugEnabled() ) LOGGER.logDebug ( "Coordinator " + getCoordinatorId() + " : stopping timer..." );
timer_.stop ();
}
if ( LOGGER.isDebugEnabled() ) LOGGER.logDebug ( "Coordinator " + getCoordinatorId() + " : disposing statehandler " + stateHandler_.getState() + "..." );
stateHandler_.dispose ();
if ( LOGGER.isDebugEnabled() ) LOGGER.logDebug ( "Coordinator " + getCoordinatorId() + " : disposed." );
}
}
/**
* Terminate the work, on behalf of Terminator.
*
* @param commit
* True iff commit termination is asked.
*/
protected void terminate ( boolean commit ) throws HeurRollbackException,
HeurMixedException, SysException, java.lang.SecurityException,
HeurCommitException, HeurHazardException, RollbackException,
IllegalStateException
{
synchronized ( fsm_ ) {
if ( commit ) {
if ( participants_.size () <= 1 ) {
commit ( true );
} else {
int prepareResult = prepare ();
// make sure to only do commit if NOT read only
if ( prepareResult != Participant.READ_ONLY )
commit ( false );
}
} else {
rollback ();
}
}
}
public void setRecoverableWhileActive () throws UnsupportedOperationException
{
recoverableWhileActive_ = true;
}
void setRollbackOnly() {
StringHeuristicMessage msg = new StringHeuristicMessage (
"setRollbackOnly" );
RollbackOnlyParticipant p = new RollbackOnlyParticipant ( msg );
try {
addParticipant ( p );
} catch ( IllegalStateException alreadyTerminated ) {
//happens in rollback after timeout - see case 27857; ignore but log
if ( LOGGER.isDebugEnabled() ) LOGGER.logDebug ( "Error during setRollbackOnly" , alreadyTerminated );
} catch ( RollbackException e ) {
//ignore: corresponds to desired outcome
if ( LOGGER.isDebugEnabled() ) LOGGER.logDebug ( "Error during setRollbackOnly" , e );
}
}
public TxState getStateWithTwoPhaseCommitDecision() {
TxState ret = getState();
if (TxState.TERMINATED.equals(getState())) {
if (isCommitted()) ret = TxState.COMMITTED;
else ret = TxState.ABORTED;
} else if (TxState.HEUR_ABORTED.equals(getState())) {
ret = TxState.ABORTED;
} else if (TxState.HEUR_COMMITTED.equals(getState())) {
ret = TxState.COMMITTED;
} else if (TxState.HEUR_HAZARD.equals(getState())) {
if (isCommitted()) ret = TxState.COMMITTING;
else ret = TxState.ABORTING;
}
return ret;
}
@Override
public void transitionPerformed(FSMTransitionEvent e) {
TxState fromState = e.fromState();
TxState toState = e.toState();
if (TxState.TERMINATED.equals(toState)) {
if (TxState.COMMITTING.equals(fromState)) {
publishDomainEvent(new TransactionCommittedEvent(root_));
} else if (TxState.ABORTING.equals(fromState)) {
publishDomainEvent(new TransactionAbortedEvent(root_));
} else if (TxState.PREPARING.equals(fromState)) {
publishDomainEvent(new TransactionReadOnlyEvent(root_));
}
}
}
private void publishDomainEvent(Event event) {
EventPublisher.publish(event);
}
@Override
public void entered(FSMEnterEvent e) {
TxState state = e.getState();
if ( TxState.HEUR_ABORTED.equals(state) ||
TxState.HEUR_COMMITTED.equals(state) ||
TxState.HEUR_HAZARD.equals(state) ||
TxState.HEUR_MIXED.equals(state)
) {
publishDomainEvent(new TransactionHeuristicEvent(root_));
}
}
}