All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.arjuna.ats.arjuna.coordinator.AbstractRecord Maven / Gradle / Ivy

There is a newer version: 4.17.37.Final
Show newest version
/*
 * 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, 2001,
 *
 * Hewlett Packard Arjuna Labs,
 * Newcastle upon Tyne,
 * Tyne and Wear,
 * UK.
 *
 * $Id: AbstractRecord.java 2342 2006-03-30 13:06:17Z  $
 */

package com.arjuna.ats.arjuna.coordinator;

import java.io.IOException;
import java.io.PrintWriter;

import com.arjuna.ats.arjuna.StateManager;
import com.arjuna.ats.arjuna.common.Uid;
import com.arjuna.ats.arjuna.common.arjPropertyManager;
import com.arjuna.ats.arjuna.logging.tsLogger;
import com.arjuna.ats.arjuna.state.InputObjectState;
import com.arjuna.ats.arjuna.state.OutputObjectState;
import com.arjuna.ats.internal.arjuna.common.UidHelper;

/**
 * Abstract Record Class
 *
 * This class provides an abstract template that defines the interface that the
 * atomic action system uses to notify objects that various state transitions
 * have occurred as the 2PC protocol executes. Record types derived from this
 * class manage certain properties of objects such as recovery information,
 * concurrency control information etc, and all must redefine the operations
 * defined here as abstract to take appropriate action.
 *
 * Many functions are declared pure virtual to force a definition to occur in
 * any derived class. These are currently all functions dealing with atomic
 * action coordination as well as the following list management functions:
 * typeIs: returns the record type of the instance. This is one of the values of
 * the enumerated type Record_type value: Some arbitrary value associated with
 * the record instance merge: Used when two records need to merge together.
 * Currently this is only used by CadaverRecords to merge information from
 * PersistenceRecords shouldAdd: returns TRUE is the record should be added to
 * the list FALSE if it should be discarded shouldMerge: returns TRUE is the two
 * records should be merged into a single record, FALSE if it should be
 * discarded shouldReplace: returns TRUE if the record should replace an
 * existing one, FALSE otherwise.
 *
 * @author Mark Little ([email protected])
 * @version $Id: AbstractRecord.java 2342 2006-03-30 13:06:17Z  $
 * @since 1.0.
 */

public abstract class AbstractRecord extends StateManager
{

	/**
	 * @return RecordType value.
	 */

	public abstract int typeIs ();

	/**
	 * If this abstract record caused a heuristic then it should return an
	 * object which implements HeuristicInformation
	 *
	 * @return Object to be used to order.
	 */

	public abstract Object value ();

	public abstract void setValue (Object o);

	/**
	 * Atomic action interface - one operation per two-phase commit state.
	 */

	/**
	 * A rollback of a nested transaction has occurred.
	 *
	 * @return TwoPhaseOutcome to indicate success/failure.
	 * @see com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome
	 */

	public abstract int nestedAbort ();

	/**
	 * A commit of a nested transaction has occurred.
	 *
	 * @return TwoPhaseOutcome to indicate success/failure.
	 * @see com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome
	 */

	public abstract int nestedCommit ();

	/**
	 * A prepare for a nested transaction has occurred.
	 *
	 * @return TwoPhaseOutcome to indicate success/failure.
	 * @see com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome
	 */

	public abstract int nestedPrepare ();

	/**
	 * A rollback of a top-level transaction has occurred.
	 *
	 * @return TwoPhaseOutcome to indicate success/failure.
	 * @see com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome
	 */

	public abstract int topLevelAbort ();

	/**
	 * A commit of a top-level transaction has occurred.
	 *
	 * @return TwoPhaseOutcome to indicate success/failure.
	 * @see com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome
	 */

	public abstract int topLevelCommit ();

	/**
	 * A prepare for a top-level transaction has occurred.
	 *
	 * @return TwoPhaseOutcome to indicate success/failure.
	 * @see com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome
	 */

	public abstract int topLevelPrepare ();

	/**
	 * Return the Uid of this abstract record so that it can be ordered in the
	 * intentions list. This is also the Uid that the record was saved with in
	 * the object store.
	 *
	 * @return Uid for this instance.
	 * @see com.arjuna.ats.arjuna.common.Uid
	 */

	/*
	 * Now that StateManager actually maintains state, we could save this in
	 * StateManager. However, it would affect all StateManager instances (more
	 * state to save).
	 */

	public Uid order ()
	{
		return uidOfObject;
	}

	/**
	 * Return the type of the abstract record. Used in ordering the instances in
	 * the intentions list. This is also the type that the record was saved with
	 * in the object store.
	 *
	 * @return String representing type.
	 */

	public String getTypeOfObject ()
	{
		return typeOfObject;
	}

	/**
	 * Determine if records are discarded on action abort or must be propagated
	 * to parents.
	 *
	 * @return true if the record should be propagated to the
	 *         parent transaction if the current transaction rolls back,
	 *         false otherwise. The default is false.
	 */

	public boolean propagateOnAbort ()
	{
		return false;
	}

	/**
	 * Determine if records are discarded on action commit or must be propagated
	 * to parents.
	 *
	 * @return true if the record should be propagated to the
	 *         parent transaction if the current transaction commits,
	 *         false otherwise. The default is true.
	 */

	public boolean propagateOnCommit ()
	{
		return true;
	}

	/**
	 * Operators for comparing and sequencing instances of classes derived from
	 * AbstractRecords. Records are ordered primarily based upon the value of
	 * 'order', followed by 'typeIs'.
	 */

	/**
	 * Determine if two records are equal in that both are the same type and
	 * have the same order value (determined via 'order()').
	 *
	 * @return true if equal, false otherwise.
	 */

	public final boolean equals (AbstractRecord ar)
	{
		return (useAlternativeOrdering ? typeEquals(ar) : orderEquals(ar));
	}

	/**
	 * Determine if two records are less than in that both are the same type and
	 * their Uids are less than.
	 *
	 * @return true if equal, false otherwise.
	 */

	public final boolean lessThan (AbstractRecord ar)
	{
		return (useAlternativeOrdering ? typeLessThan(ar) : orderLessThan(ar));
	}

	/**
	 * Determine if two records are greater than in that both are the same type
	 * and their Uids are greater than.
	 *
	 * @return true if equal, false otherwise.
	 */

	public final boolean greaterThan (AbstractRecord ar)
	{
		return (useAlternativeOrdering ? typeGreaterThan(ar) : orderGreaterThan(ar));
	}

	/**
	 * Cleanup is called if a top-level action is detected to be an orphan.
	 *
	 * NOTE nested actions are never orphans since their parents would be
	 * aborted we may as well abort them as well.
	 *
	 * @return TwoPhaseOutcome as default is the same as
	 *         topLevelAbort.
	 */

	public int topLevelCleanup ()
	{
		return topLevelAbort();
	}

	/**
	 * Cleanup is called if a nested is detected to be an orphan.
	 *
	 * NOTE nested actions are never orphans since their parents would be
	 * aborted we may as well abort them as well.
	 *
	 * @return TwoPhaseOutcome as default is the same as
	 *         nestedAbort.
	 */

	public int nestedCleanup ()
	{
		return nestedAbort();
	}

	/**
	 * Should this record be saved in the intentions list? If the record is
	 * saved, then it may be recovered later in the event of a failure. Note,
	 * however, that the size of the intentions list on disk is critical to the
	 * performance of the system (disk I/O is a bottleneck).
	 *
	 * @return true if it should be saved, false
	 *         otherwise. false is the default.
	 */

	public boolean doSave ()
	{
		return false;
	}

	/**
	 * Re-implementation of abstract methods inherited from base class.
	 */

	public String type ()
	{
		return "/StateManager/AbstractRecord";
	}

	/**
	 * Write information about this specific instance to the specified stream.
	 *
	 * @param strm the stream on which to output.
	 */

	public void print (PrintWriter strm)
	{
		strm.println("Uid of Managed Object: " + uidOfObject);
		strm.println("Type of Managed Object: " + typeOfObject);
		super.print(strm);
	}

	/**
	 * When the transaction is required to make the intentions list persistent,
	 * it scans the list and asks each record whether or not it requires state
	 * to be saved (by calling doSave). If the answer is yes, then save_state is
	 * called and the record instance must save enough information to enable it
	 * to be restored from that state later. The basic AbstractRecord save_state
	 * will save common data that is required by the base class during recovery.
	 *
	 * @return true if successful, false
	 *         otherwise.
	 */

	public boolean save_state (OutputObjectState os, int i)
	{
		try
		{
		    UidHelper.packInto(uidOfObject, os);
			os.packString(typeOfObject);

			return true;
		}
		catch (IOException e)
		{
			return false;
		}
	}

	/**
	 * During recovery, the transaction log is given to the recovery system and
	 * it will recreate a transaction instance to perform necessary recovery
	 * actions. This transaction will recreate the intentions list and give each
	 * recreated AbstractRecord the state that that was saved during transaction
	 * persistence. The base class will restore information that it needs from
	 * the log.
	 *
	 * @return true if successful, false
	 *         otherwise.
	 */

	public boolean restore_state (InputObjectState os, int i)
	{
		typeOfObject = null;

		try
		{
		    uidOfObject = UidHelper.unpackFrom(os);
			typeOfObject = os.unpackString();

			return true;
		}
		catch (IOException e)
		{
			return false;
		}
	}

	/**
	 * Forget any heuristic outcome which this implementation may have produced.
	 *
	 * @return true by default. If false is
	 *         returned then the instance must be remembered by the transaction
	 *         (in the log) in order for recovery to retry later or for a system
	 *         administrator to be able to determine which resources have not
	 *         been successfully completed.
	 */

	public boolean forgetHeuristic ()
	{
		return true;
	}

	/**
	 * Perform a nested one phase commit.
	 *
	 * @return TwoPhaseOutcome to indicate success/failure.
	 * @see com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome
	 */

	public int nestedOnePhaseCommit ()
	{
		int res = nestedPrepare();

		switch (res)
		{
		case TwoPhaseOutcome.PREPARE_OK:
			return nestedCommit();
		case TwoPhaseOutcome.PREPARE_READONLY:
			return TwoPhaseOutcome.FINISH_OK;
		default:
			return TwoPhaseOutcome.FINISH_ERROR;
		}
	}

	/**
	 * Perform a top-level one phase commit.
	 *
	 * @return TwoPhaseOutcome to indicate success/failure.
	 * @see com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome
	 */

	public int topLevelOnePhaseCommit ()
	{
		int res = topLevelPrepare();

		switch (res)
		{
		case TwoPhaseOutcome.PREPARE_OK:
			return topLevelCommit();
		case TwoPhaseOutcome.PREPARE_READONLY:
			return TwoPhaseOutcome.FINISH_OK;
		case TwoPhaseOutcome.ONE_PHASE_ERROR:
		    return TwoPhaseOutcome.ONE_PHASE_ERROR;
		default:
			return TwoPhaseOutcome.FINISH_ERROR;
		}
	}

	
	@SuppressWarnings("unchecked")
        public static AbstractRecord create (int type)
	{
	    try
	    {
        	    Class recordClass = RecordType.typeToClass(type);
        
        	    return (AbstractRecord) recordClass.newInstance();
	    }
	    catch (final NullPointerException ex) {
            tsLogger.i18NLogger.warn_coordinator_AbstractRecord_npe(Integer.toString(type));

            return null;
        }
	    catch (final Throwable ex)
	    {
	        ex.printStackTrace();
	        
	        return null;
	    }
	}

	/**
	 * Merge the current record with the one presented.
	 *
	 * @param a the record with which to merge.
	 */

	public abstract void merge (AbstractRecord a);

	/**
	 * Alter the current record with the one presented.
	 *
	 * @param a the record with which to alter.
	 */

	public abstract void alter (AbstractRecord a);

	/**
	 * Should we add the record presented to the intentions list?
	 *
	 * @param a The record to try to add.
	 * @return true if the record should be added,
	 *         false otherwise.
	 */

	public abstract boolean shouldAdd (AbstractRecord a);

	/**
	 * Should we alter the current record with the one presented?
	 *
	 * @param a The record to try to alter.
	 * @return true if the record should be altered,
	 *         false otherwise.
	 */

	public abstract boolean shouldAlter (AbstractRecord a);

	/**
	 * Should we merge the current record with the one presented?
	 *
	 * @param a The record to try to merge.
	 * @return true if the record should be merged,
	 *         false otherwise.
	 */

	public abstract boolean shouldMerge (AbstractRecord a);

	/**
	 * Should we replace the record presented with the current record?
	 *
	 * @param a The record to try to replace.
	 * @return true if the record should be replaced,
	 *         false otherwise.
	 */

	public abstract boolean shouldReplace (AbstractRecord a);

	/**
	 * The current record is about to replace the one presented. This method is
	 * invoked to give the current record a chance to copy information, for
	 * example, from the record being replaced.
	 *
	 * @param a the record that will replace this instance.
	 */

	public void replace (AbstractRecord a)
	{
	}

	/**
	 * These few functions are link manipulation primitives used by the
	 * RecordList processing software to chain instances together.
	 *
	 * @return the previous element in the intentions list, or null.
	 */

	protected final AbstractRecord getPrevious ()
	{
		return previous;
	}

	/**
	 * @return the next element in the intentions list, or null.
	 */

	protected final AbstractRecord getNext ()
	{
		return next;
	}

	/**
	 * Set the previous element in the list to the specified instance.
	 *
	 * @param ar the instance to become previous.
	 */

	protected final void setPrevious (AbstractRecord ar)
	{
		previous = ar;
	}

	/**
	 * Set the next element in the list to the specified instance.
	 *
	 * @param ar the instance to become next.
	 */

	protected final void setNext (AbstractRecord ar)
	{
		next = ar;
	}

	/**
	 * Create a new instance with the specified parameters.
	 *
	 * @param storeUid the unique id for this instance.
	 * @param objType the type of the instance.
	 * @param otype the ObjectType of the object.
	 * @see com.arjuna.ats.arjuna.ObjectType
	 */

	protected AbstractRecord (Uid storeUid, String objType, int otype)
	{
		super(otype);

		next = null;
		previous = null;
		uidOfObject = storeUid;
		typeOfObject = objType;

		if (tsLogger.logger.isTraceEnabled()) {
            tsLogger.logger.trace("AbstractRecord::AbstractRecord ("
                    + storeUid + ", " + otype + ")");
        }
	}

	/**
	 * Create a new instance with the specified paramaters.
	 *
	 * @param storeUid the unique id for this instance.
	 */

	protected AbstractRecord (Uid storeUid)
	{
		super(storeUid);

		next = null;
		previous = null;
		uidOfObject = storeUid;
		typeOfObject = null;

		if (tsLogger.logger.isTraceEnabled()) {
            tsLogger.logger.trace("AbstractRecord::AbstractRecord ("
                    + storeUid + ")");
        }
	}

	/**
	 * Creates a 'blank' abstract record. This is used during crash recovery
	 * when recreating the prepared list of a server atomic action.
	 */

	public AbstractRecord ()
	{
		super(Uid.nullUid());

		next = null;
		previous = null;
		uidOfObject = new Uid(Uid.nullUid());
		typeOfObject = null;

		if (tsLogger.logger.isTraceEnabled()) {
            tsLogger.logger.trace("AbstractRecord::AbstractRecord () - crash recovery constructor");
        }
	}

	/**
	 * ensure records of the same type are grouped together in the list, rather
	 * than grouping them by object (i.e. uid)
	 */

	private final boolean typeEquals (AbstractRecord ar)
	{
		return ((typeIs() == ar.typeIs()) && (order().equals(ar.order())));
	}

	private final boolean typeLessThan (AbstractRecord ar)
	{
		return ((typeIs() < ar.typeIs()) || ((typeIs() == ar.typeIs()) && (order().lessThan(ar.order()))));
	}

	private final boolean typeGreaterThan (AbstractRecord ar)
	{
		return ((typeIs() > ar.typeIs()) || ((typeIs() == ar.typeIs()) && (order().greaterThan(ar.order()))));
	}

	private final boolean orderEquals (AbstractRecord ar)
	{
		return ((order().equals(ar.order())) && (typeIs() == ar.typeIs()));
	}

	private final boolean orderLessThan (AbstractRecord ar)
	{
		return ((order().lessThan(ar.order())) || ((order().equals(ar.order())) && (typeIs() < ar.typeIs())));
	}

	private final boolean orderGreaterThan (AbstractRecord ar)
	{
		return ((order().greaterThan(ar.order())) || ((order().equals(ar.order())) && (typeIs() > ar.typeIs())));
	}

	private AbstractRecord next;
	private AbstractRecord previous;
	private Uid uidOfObject;
	private String typeOfObject;

    private static final boolean useAlternativeOrdering = arjPropertyManager.getCoordinatorEnvironmentBean().isAlternativeRecordOrdering();
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy