//$Id: ActiveStateHandler.java,v 1.2 2006/09/19 08:03:51 guy Exp $
//$Log: ActiveStateHandler.java,v $
//Revision 1.2 2006/09/19 08:03:51 guy
//FIXED 10050
//
//Revision 1.1.1.1 2006/08/29 10:01:05 guy
//Import of 3.0 essentials edition.
//
//Revision 1.1.1.1 2006/04/29 08:55:37 guy
//Initial import.
//
//Revision 1.1.1.1 2006/03/29 13:21:27 guy
//Imported.
//
//Revision 1.1.1.1 2006/03/23 16:25:27 guy
//Imported.
//
//Revision 1.1.1.1 2006/03/22 13:46:53 guy
//Import.
//
//Revision 1.4 2006/03/21 16:13:01 guy
//Added active recovery as a setter.
//
//Revision 1.3 2006/03/21 13:22:55 guy
//Introduced active recovery and CompTx properties as meta-tags.
//
//Revision 1.2 2006/03/15 10:31:39 guy
//Formatted code.
//
//Revision 1.1.1.1 2006/03/09 14:59:08 guy
//Imported 3.0 development into CVS repository.
//
//Revision 1.9 2005/09/01 10:01:58 guy
//Modified: getHeuristicMessages return messages, not strings.
//Otherwise, activities can't throw heuristic errors.
//
//Revision 1.8 2005/08/14 21:29:50 guy
//Corrected BUG: commit onephase with orphan check did not work:
//no sibling count was set.
//
//Revision 1.7 2005/08/05 15:03:27 guy
//Merged-in changes/additions of redesign-5-2004 (SOAP development branch).
//
//Revision 1.6 2004/11/24 10:20:15 guy
//Updated error messages.
//
//Revision 1.5 2004/10/12 13:03:25 guy
//Updated docs (changed Atomikos to Atomikos in many places).
//
//Revision 1.4 2004/09/07 14:51:57 guy
//Improved recovery of 1PC committing txs.
//Added rollback() of pending subtxs upon timeout of coordinator.
//Improved behaviour of addParticipant methods for terminated/marked abort txs.
//
//Revision 1.3 2004/09/06 09:26:37 guy
//Redesigned recovery: can now be done at any time.
//Resources can now be added after init() and will be
//recovered immediately rather than on the next restart.
//
//Revision 1.2 2004/03/22 15:36:52 guy
//Merged-in changes from branch redesign-4-2003.
//
//Revision 1.1.2.4 2003/06/20 16:31:31 guy
//*** empty log message ***
//
//Revision 1.1.2.3 2003/05/15 08:05:16 guy
//Changed to make tests pass with the new state mechanism.
//
//Revision 1.1.2.2 2003/05/12 07:00:07 guy
//Redesigned Coordinator with STATE PATTERN.
//
/*
* Copyright 2000-2008, Atomikos (http://www.atomikos.com)
*
* 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.Enumeration;
import java.util.Stack;
import java.util.Vector;
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.RollbackException;
import com.atomikos.icatch.SysException;
import com.atomikos.icatch.TxState;
import com.atomikos.icatch.system.Configuration;
/**
*
*
* A state handler for the active coordinator state.
*/
class ActiveStateHandler extends CoordinatorStateHandler
{
private long rollbackTicks_;
// how many timeout events have happened?
// if max allowed -> rollback on timeout
private int globalSiblingCount_;
ActiveStateHandler ( CoordinatorImp coordinator )
{
super ( coordinator );
rollbackTicks_ = 0;
}
// ActiveStateHandler ( CoordinatorStateHandler previous )
// {
// super ( previous );
// rollbackTicks_ = 0;
// }
protected long getRollbackTicks ()
{
return rollbackTicks_;
}
protected Object getState ()
{
return TxState.ACTIVE;
}
protected void onTimeout ()
{
try {
if ( rollbackTicks_ < getCoordinator ().getMaxRollbackTicks () )
rollbackTicks_++;
else {
// first check if we are still the current state!
// otherwise, a COMMITTING tx could be rolled back
// in case of 1PC!!!
if ( getCoordinator ().getState ().equals ( getState () ) ) {
printMsg ( "Rollback of timedout ACTIVE coordinator !" );
boolean indoubt = getCoordinator().isRecoverableWhileActive().booleanValue();
//treat activities (recoverable) as indoubts to make sure that anomalies
//with early prepare etc. are treated as heuristics
super.rollback ( indoubt , false );
}
}
} catch ( Exception e ) {
printMsg ( "Error in timeout of ACTIVE state: " + e.getMessage ()
+ " for coordinator " + getCoordinator ().getCoordinatorId () );
}
}
protected void setGlobalSiblingCount ( int count )
{
globalSiblingCount_ = count;
}
protected int prepare () throws RollbackException,
java.lang.IllegalStateException, HeurHazardException,
HeurMixedException, SysException
{
Stack errors = new Stack (); // error propagation
int count = 0; // number of participants
PrepareResult result = null; // synchronization
boolean allReadOnly = true; // if still true at end-> readonly vote
int ret = 0; // return value
Vector participants = getCoordinator ().getParticipants ();
CoordinatorStateHandler nextStateHandler = null;
if ( getCoordinator ().checkSiblings ()
&& globalSiblingCount_ != getCoordinator ()
.getLocalSiblingCount () ) {
// NO COMMIT ALLOWED: ORPHANS!!!!
try {
Configuration.logDebug ( "Orphans detected: "
+ getCoordinator ().getLocalSiblingCount () + " vs "
+ globalSiblingCount_ + " - forcing rollback." );
super.rollback ( getCoordinator().isRecoverableWhileActive().booleanValue() , false );
} catch ( HeurCommitException hc ) {
throw new HeurMixedException ( hc.getHeuristicMessages() );
}
throw new RollbackException ( "Orphans detected." );
}
try {
try {
getCoordinator ().setState ( TxState.PREPARING );
} catch ( RuntimeException error ) {
//See case 23334
String msg = "Error in preparing: " + error.getMessage() + " - rolling back instead";
Configuration.logWarning ( msg , error );
try {
super.rollback ( getCoordinator().isRecoverableWhileActive().booleanValue() , false );
throw new RollbackException ( msg );
} catch ( HeurCommitException e ) {
Configuration.logWarning ( "Illegal heuristic commit during rollback before prepare:" + e );
throw new HeurMixedException ( e.getHeuristicMessages() );
}
}
count = participants.size ();
result = new PrepareResult ( count );
Enumeration enumm = participants.elements ();
while ( enumm.hasMoreElements () ) {
Participant p = (Participant) enumm.nextElement ();
PrepareMessage pm = new PrepareMessage ( p, result );
if ( getCascadeList () != null && p.getURI () != null ) { // null
// for
// OTS
// case?
Integer sibnum = (Integer) getCascadeList ().get (
p.getURI () );
if ( sibnum != null ) // null for local participant!
p.setGlobalSiblingCount ( sibnum.intValue () );
p.setCascadeList ( getCascadeList () );
}
getPropagator ().submitPropagationMessage ( pm );
// this will trigger sending the message
} // while
// now wait for all replies and act accordingly
result.waitForReplies ();
boolean voteOK = result.allYes ();
setReadOnlyTable ( result.getReadOnlyTable () );
// indoubttable_ = result.getIndoubtTable();
allReadOnly = result.allReadOnly ();
if ( !voteOK ) {
// addErrorMessages(result.getMessages());
int res = result.getResult ();
// FOLLOWING CODE REMOVED:
// DURING PREPARE, NO CONTRACTS ARE BROKEN
// IF PREPARE FAILS. the only danger is that
// some servers vote yes and remain indoubt,
// but that is a heuristic case at these
// remote sites, but not locally.
// indeed, locally there is no uncertainty
// about the outcome, because we will
// roll back and vote no!
// THEREFORE, WE CAN FORGET ABOUT ANY
// INDOUBT SERVERS; IF THEY INQUIRE, WE WILL
// SAY ABORTED.
// if (res == Result.HEUR_MIXED) {
// rollback ( true );
// throw new HeurMixedException(getHeuristicMessages());
// }
// else if (res == Result.HEUR_COMMIT) {
// commit ( true, false );
// throw new HeurCommitException(getHeuristicMessages());
// }
// else if (res == Result.HEUR_HAZARD) {
// rollback ( true );
// throw new HeurMixedException(getHeuristicMessages());
// }
// else {
// resolve indoubt situations and return NO vote.
try {
rollback ( true, false );
} catch ( HeurCommitException hc ) {
// can not happen:
// heuristic commit means that ALL subordinate work
// as been committed heuristically.
// this is impossible since it assumes that ALL
// participants voted YES in the first place,
// which contradicts the fact that we are dealing with
// !voteOK
errors.push ( hc );
throw new SysException ( "Unexpected heuristic: "
+ hc.getMessage (), errors );
}
throw new RollbackException ( "Prepare: " + "NO vote" );
// } //else
} // if (!voteOK)
} catch ( RuntimeException runerr ) {
errors.push ( runerr );
throw new SysException ( "Error in prepare: "
+ runerr.getMessage (), errors );
} catch ( InterruptedException err ) {
errors.push ( err );
throw new SysException ( "Error in prepare: " + err.getMessage (),
errors );
}
// here we are if all yes.
if ( allReadOnly ) {
nextStateHandler = new TerminatedStateHandler ( this );
getCoordinator ().setStateHandler ( nextStateHandler );
ret = Participant.READ_ONLY;
// System.err.println ( "Prepare: returning readonly" );
} else {
nextStateHandler = new IndoubtStateHandler ( this );
getCoordinator ().setStateHandler ( nextStateHandler );
ret = Participant.READ_ONLY + 1;
// System.err.println ( "Prepare: returning not readonly" );
}
return ret;
}
protected HeuristicMessage[] commit ( boolean onePhase )
throws HeurRollbackException, HeurMixedException,
HeurHazardException, java.lang.IllegalStateException,
RollbackException, SysException
{
HeuristicMessage[] result = new HeuristicMessage[0];
if ( !onePhase )
throw new IllegalStateException (
"Illegal state for commit: ACTIVE!" );
if ( getCoordinator ().getParticipants ().size () > 1 ) {
int prepareResult = Participant.READ_ONLY + 1;
// happens if client has one remote participant
// and hence decides for 1PC, but the remote
// instance (this one) has more participants registered
// in that case: first prepare and then do normal
// 2PC commit
setGlobalSiblingCount ( 1 );
prepareResult = prepare ();
// make sure to only do 2PC commit if NOT read only
if ( prepareResult == Participant.READ_ONLY )
result = getHeuristicMessages ();
else
result = commit ( false, false );
} else {
result = commit ( false, true );
}
return result;
}
protected HeuristicMessage[] rollback ()
throws HeurCommitException, HeurMixedException, SysException,
HeurHazardException, java.lang.IllegalStateException
{
return rollback ( getCoordinator().isRecoverableWhileActive().booleanValue() , false );
}
protected Boolean replayCompletion ( Participant participant )
throws IllegalStateException
{
throw new IllegalStateException ( "No prepares sent yet." );
}
}