/*
* JBoss, Home of Professional Open Source
* Copyright 2006, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* 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,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* (C) 2005-2006,
* @author JBoss Inc.
*/
/*
* Copyright (C) 1998, 1999, 2000,
*
* Arjuna Solutions Limited,
* Newcastle upon Tyne,
* Tyne and Wear,
* UK.
*
* $Id: RecoveryRecord.java 2342 2006-03-30 13:06:17Z $
*/
package com.arjuna.ats.internal.arjuna.abstractrecords;
import java.io.PrintWriter;
import com.arjuna.ats.arjuna.ObjectType;
import com.arjuna.ats.arjuna.StateManager;
import com.arjuna.ats.arjuna.coordinator.AbstractRecord;
import com.arjuna.ats.arjuna.coordinator.BasicAction;
import com.arjuna.ats.arjuna.coordinator.RecordType;
import com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome;
import com.arjuna.ats.arjuna.logging.tsLogger;
import com.arjuna.ats.arjuna.state.InputObjectState;
import com.arjuna.ats.arjuna.state.OutputObjectState;
public class RecoveryRecord extends AbstractRecord
{
/**
* This constructor is used to create a new instance of a RecoveryRecord.
*/
public RecoveryRecord(OutputObjectState os, StateManager sm)
{
super(sm.get_uid(), sm.type(), ObjectType.ANDPERSISTENT);
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("RecoveryRecord::RecoveryRecord(" + os + ", "
+ sm.get_uid() + ")");
}
objectAddr = sm;
state = os;
actionHandle = BasicAction.Current();
}
public int typeIs ()
{
return RecordType.RECOVERY;
}
public Object value ()
{
return state;
}
public void setValue (Object newState)
{
if (newState instanceof OutputObjectState)
state = (OutputObjectState) newState;
else {
tsLogger.i18NLogger.warn_RecoveryRecord_1();
}
}
/**
* nestedAbort causes the restore_state function of the object to be invoked
* passing it the saved ObjectState.
*/
public int nestedAbort ()
{
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("RecoveryRecord::nestedAbort() for " + order());
}
/*
* First check that we have a state. We won't have for records created
* by crash recovery.
*/
forgetAction(false);
if (state != null)
{
if (state.notempty()) /* anything to restore ? */
{
InputObjectState oldState = new InputObjectState(state);
int result = objectAddr.restore_state(oldState,
ObjectType.RECOVERABLE) ? TwoPhaseOutcome.FINISH_OK
: TwoPhaseOutcome.FINISH_ERROR;
if (result == TwoPhaseOutcome.FINISH_ERROR) {
tsLogger.i18NLogger.warn_RecoveryRecord_2();
}
return result;
}
}
return TwoPhaseOutcome.FINISH_OK;
}
/**
* nestedCommit does nothing since the passing of the state up to the parent
* action is handled by the record list merging system. In fact since
* nestedPrepare returns PREPARE_READONLY this function should never
* actually be called
*/
public int nestedCommit ()
{
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("RecoveryRecord::nestedCommit() for " + order());
}
return TwoPhaseOutcome.FINISH_OK;
}
public int nestedPrepare ()
{
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("RecoveryRecord::nestedPrepare() for " + order());
}
forgetAction(true);
return TwoPhaseOutcome.PREPARE_READONLY;
}
/**
* topLevelAbort for Recovery records implies the object state should be
* restored to the saved state exactly like a nested abort.
*/
public int topLevelAbort ()
{
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("RecoveryRecord::topLevelAbort() for " + order());
}
return nestedAbort(); /* i.e., same as nested case */
}
/**
* topLevelCommit has nothing to do for RecoveryRecords as no changes have
* been made in the object store. In fact since topLevelPrepare returns
* PREPARE_READONLY this function should never actually be called
*/
public int topLevelCommit ()
{
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("RecoveryRecord::topLevelCommit() for " + order());
}
forgetAction(true);
return TwoPhaseOutcome.FINISH_OK;
}
/**
* topLevelPrepare can return PREPARE_READONLY to avoid topLevelCommit being
* called in the action commit case
*/
public int topLevelPrepare ()
{
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("RecoveryRecord::topLevelPrepare() for " + order());
}
return TwoPhaseOutcome.PREPARE_READONLY;
}
/*
* Saving of RecoveryRecords is only undertaken during the Prepare phase of
* the top level 2PC. Since the managed objects are only recoverable (not
* persistent) there is no need to save any information (or restore any
* either). However, persistence records (derived from recovery records)
* need to be saved for crash recovery purposes.
*/
public boolean doSave ()
{
return false;
}
public boolean restore_state (InputObjectState os, int ot)
{
return super.restore_state(os, ot);
}
public boolean save_state (OutputObjectState os, int ot)
{
return super.save_state(os, ot);
}
public void print (PrintWriter strm)
{
super.print(strm);
strm.println("RecoveryRecord with state:\n" + state);
}
public String type ()
{
return "/StateManager/AbstractRecord/RecoveryRecord";
}
public void merge (AbstractRecord a)
{
}
public void alter (AbstractRecord a)
{
}
/*
* should_merge and should_replace are invoked by the record list manager to
* determine if two records should be merged togethor or if the 'newer'
* should replace the older. shouldAdd determines if the new record should
* be added in addition to the existing record and is currently only invoked
* if both of should_merge and should_replace return false Default
* implementations here always return false - ie new records do not override
* old.
*/
public boolean shouldAdd (AbstractRecord a)
{
return false;
}
public boolean shouldAlter (AbstractRecord a)
{
return false;
}
public boolean shouldMerge (AbstractRecord a)
{
return false;
}
public boolean shouldReplace (AbstractRecord a)
{
return false;
}
/*
* Creates a 'blank' recovery record. This is used during crash recovery
* when recreating the prepared list of a server atomic action.
*/
public RecoveryRecord()
{
super();
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("RecoveryRecord::RecoveryRecord()"
+ " - crash recovery constructor");
}
objectAddr = null;
state = null;
actionHandle = null;
}
/*
* Can we use this to force our parent to "remember" us when we commit, and
* prevent the system from creating another record in that action?
*/
protected final void forgetAction (boolean commit)
{
if ((actionHandle != null) && (objectAddr != null))
{
StateManagerFriend.forgetAction(objectAddr, actionHandle, commit, RecordType.RECOVERY);
actionHandle = null; // only do this once!
}
}
protected StateManager objectAddr;
protected OutputObjectState state;
private BasicAction actionHandle;
}