Package com.arjuna.ats.arjuna

Source Code of com.arjuna.ats.arjuna.StateManager

/*
* 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,
*
* Arjuna Solutions Limited,
* Newcastle upon Tyne,
* Tyne and Wear,
* UK.
*
* $Id: StateManager.java 2342 2006-03-30 13:06:17Z  $
*/

package com.arjuna.ats.arjuna;

import com.arjuna.ats.arjuna.coordinator.*;
import com.arjuna.ats.arjuna.objectstore.ObjectStore;
import com.arjuna.ats.arjuna.common.*;
import com.arjuna.ats.arjuna.state.*;
import com.arjuna.ats.arjuna.utils.Utility;
import com.arjuna.ats.arjuna.exceptions.FatalError;
import java.io.PrintWriter;
import java.util.*;

import com.arjuna.common.util.logging.*;

import com.arjuna.ats.arjuna.logging.tsLogger;
import com.arjuna.ats.arjuna.logging.FacilityCode;

import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;
import com.arjuna.ats.internal.arjuna.Header;
import com.arjuna.ats.internal.arjuna.abstractrecords.ActivationRecord;
import com.arjuna.ats.internal.arjuna.abstractrecords.CadaverActivationRecord;
import com.arjuna.ats.internal.arjuna.abstractrecords.CadaverRecord;
import com.arjuna.ats.internal.arjuna.abstractrecords.DisposeRecord;
import com.arjuna.ats.internal.arjuna.abstractrecords.PersistenceRecord;
import com.arjuna.ats.internal.arjuna.abstractrecords.RecoveryRecord;
import com.arjuna.ats.internal.arjuna.common.UidHelper;

import java.io.IOException;

/**
* The root of the Arjuna class hierarchy. This class provides state management
* facilities than can be automatically used by other classes by inheritance.
*
* @author Mark Little (mark@arjuna.com)
* @version $Id: StateManager.java 2342 2006-03-30 13:06:17Z $
* @since JTS 1.0.
* @message com.arjuna.ats.arjuna.StateManager_1
*          [com.arjuna.ats.arjuna.StateManager_1] - StateManager::terminate()
*          should be invoked in every destructor
* @message com.arjuna.ats.arjuna.StateManager_2
*          [com.arjuna.ats.arjuna.StateManager_2] - Activate of object with id
*          = {0} and type {1} unexpectedly failed"
* @message com.arjuna.ats.arjuna.StateManager_3
*          [com.arjuna.ats.arjuna.StateManager_3] - StateManager::deactivate -
*          object store error
* @message com.arjuna.ats.arjuna.StateManager_4
*          [com.arjuna.ats.arjuna.StateManager_4] - StateManager::deactivate -
*          save_state error
* @message com.arjuna.ats.arjuna.StateManager_5
*          [com.arjuna.ats.arjuna.StateManager_5] - StateManager::destroy for
*          object-id {0}
* @message com.arjuna.ats.arjuna.StateManager_6
*          [com.arjuna.ats.arjuna.StateManager_6] - StateManager.destroy -
*          failed to add abstract record.
* @message com.arjuna.ats.arjuna.StateManager_7
*          [com.arjuna.ats.arjuna.StateManager_7] - StateManager.destroy -
*          caught object store exception: {0}
* @message com.arjuna.ats.arjuna.StateManager_8
*          [com.arjuna.ats.arjuna.StateManager_8] - StateManager.destroy -
*          called on non-persistent or new object!
* @message com.arjuna.ats.arjuna.StateManager_9
*          [com.arjuna.ats.arjuna.StateManager_9] - StateManager.restore_state
*          - could not find StateManager state in object state!
* @message com.arjuna.ats.arjuna.StateManager_10
*          [com.arjuna.ats.arjuna.StateManager_10] - StateManager::modified()
*          invocation on an object whose state has not been restored -
*          activating object
* @message com.arjuna.ats.arjuna.StateManager_11
*          [com.arjuna.ats.arjuna.StateManager_11] - Delete called on object
*          with uid {0} and type {1} within atomic action.
* @message com.arjuna.ats.arjuna.StateManager_12
*          [com.arjuna.ats.arjuna.StateManager_12] - StateManager.cleanup -
*          could not save_state from terminate!
* @message com.arjuna.ats.arjuna.StateManager_13
*          [com.arjuna.ats.arjuna.StateManager_13] - Attempt to use volatile
*          store.
* @message com.arjuna.ats.arjuna.StateManager_14
*          [com.arjuna.ats.arjuna.StateManager_14] - Volatile store not
*          implemented!
* @message com.arjuna.ats.arjuna.StateManager_15
*          [com.arjuna.ats.arjuna.StateManager_15] - Invalid object state.
* @message com.arjuna.ats.arjuna.StateManager_16
*          [com.arjuna.ats.arjuna.StateManager_16] - Invalid object store type:
*/

public class StateManager
{

    /**
     * These methods must be used by a derived class. They are responsible for
     * packing and unpacking an object's state to/from a state buffer.
     * StateManager calls them at appropriate times during the lifetime of the
     * object, and may then pass the buffer to a persistent object store for
     * saving.
     *
     * @return <code>true</code> on success, <code>false</code> otherwise.
     */

    public boolean save_state (OutputObjectState os, int ot)
    {
        /*
         * Only pack additional information if this is for a persistent state
         * modification.
         */

        if (ot == ObjectType.ANDPERSISTENT)
        {
            try
            {
                BasicAction action = BasicAction.Current();

                if (action == null)
                    packHeader(os, new Header(null, Utility.getProcessUid()));
                else
                    packHeader(os, new Header(action.get_uid(), Utility.getProcessUid()));
            }
            catch (IOException e)
            {
                return false;
            }
        }

        return true;
    }

    /**
     * These methods must be provided by a derived class. They are responsible
     * for packing and unpacking an object's state to/from a state buffer.
     * StateManager calls them at appropriate times during the lifetime of the
     * object, and may then pass the buffer to a persistent object store for
     * saving.
     *
     * @return <code>true</code> on success, <code>false</code> otherwise.
     */

    public boolean restore_state (InputObjectState os, int ot)
    {
        if (ot == ObjectType.ANDPERSISTENT)
        {
            try
            {
                unpackHeader(os, new Header());
            }
            catch (IOException e)
            {
                return false;
            }
        }

        return true;
    }

    /**
     * Destructor.
     */

    public void finalize () throws Throwable
    {
        if (tsLogger.arjLogger.isDebugEnabled())
        {
            tsLogger.arjLogger.debug(DebugLevel.DESTRUCTORS,
                    VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_STATE_MAN,
                    "StateManager.finalize() for object-id " + get_uid()
                            + " type " + type());
        }

        if (currentStatus == ObjectStatus.ACTIVE_NEW)
        {
            BasicAction action = BasicAction.Current();

            if ((action != null) && (action.status() == ActionStatus.RUNNING))
            {
                if (tsLogger.arjLoggerI18N.isWarnEnabled())
                    tsLogger.arjLoggerI18N
                            .warn("com.arjuna.ats.arjuna.StateManager_1");
                cleanup(false);
            }
        }
    }

    /**
     * This operation activates an object. Activation of an object may entail
     * loading its passive state from the object store and unpacking it into the
     * memory resident form, or it may simply be a no-op. Full activation is
     * only necessary if the object is currently marked as being PASSIVE (that
     * is, the object was constructed as being of type ANDPERSISTENT with an
     * existing uid and has not already been activated). Objects that are not of
     * type ANDPERSISTENT or are persistent but have not yet been saved in an
     * object store (so-called new persistent objects) are unaffected by this
     * function. Returns false if PASSIVE object cannot be loaded from object
     * store, true otherwise. The root of the object store is taken as
     * <code>null</code>.
     *
     * @return <code>true</code> on success, <code>false</code> otherwise.
     * @see com.arjuna.ats.arjuna.objectstore.ObjectStore
     */

    public boolean activate ()
    {
        return activate(null);
    }

    /**
     * This operation activates an object. Activation of an object may entail
     * loading its passive state from the object store and unpacking it into the
     * memory resident form, or it may simply be a no-op. Full activation is
     * only necessary if the object is currently marked as being PASSIVE (that
     * is, the object was constructed as being of type ANDPERSISTENT with an
     * existing uid and has not already been activated). Objects that are not of
     * type ANDPERSISTENT or are persistent but have not yet been saved in an
     * object store (so-called new persistent objects) are unaffected by this
     * function. Returns false if PASSIVE object cannot be loaded from object
     * store, true otherwise.
     *
     * @return <code>true</code> on success, <code>false</code> otherwise.
     * @see com.arjuna.ats.arjuna.objectstore.ObjectStore
     */

    public synchronized boolean activate (String rootName)
    {
        if (tsLogger.arjLogger.isDebugEnabled())
        {
            tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
                    VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_STATE_MAN,
                    "StateManager::activate( "
                            + ((rootName != null) ? rootName : "null")
                            + ") for object-id " + objectUid);
        }

        if (myType == ObjectType.NEITHER)
        {
            return true;
        }

        if (currentStatus == ObjectStatus.DESTROYED)
            return false;

        BasicAction action = null;
        int oldStatus = currentStatus;
        boolean result = true; /* assume 'succeeds' */
        boolean forceAR = false;

        /*
         * Check if this action has logged its presence before. If not we force
         * creation of an ActivationRecord so that each thread/action tree has
         * an ActivationRecord in it. This allows us to passivate the object
         * when the last thread has finished with it, i.e., when the last
         * ActivationRecord is gone.
         */

        action = BasicAction.Current();

        if ((action != null) && (action.status() == ActionStatus.RUNNING))
        {
            /*
             * Only check for top-level action. This is sufficient because
             * activation records are propagated to the parent on nested
             * transaction commit, and dropped when the (nested) action aborts.
             * Thus, an object remains active as long as a single
             * ActivationRecord is being used, and we don't need to create a new
             * record for each transaction in the same hierarchy. Once
             * activated, the object remains active until the action commits or
             * aborts (at which time it may be passivated, and then reactivated
             * later by the creation of a new ActivationRecord.)
             */

            synchronized (usingActions)
            {
                if (usingActions.get(action.topLevelAction().get_uid()) == null)
                {
                    usingActions.put(action.topLevelAction().get_uid(), action
                            .topLevelAction());
                    forceAR = true;
                }
            }
        }

        if (forceAR || (currentStatus == ObjectStatus.PASSIVE)
                || (currentStatus == ObjectStatus.PASSIVE_NEW))
        {
            /*
             * If object is recoverable only, then no need to set up the object
             * store.
             */

            if (loadObjectState())
            {
                setupStore(rootName);
            }

            /* Only really activate if object is PASSIVE */

            if (currentStatus == ObjectStatus.PASSIVE)
            {
                /*
                 * If the object is shared between different processes, then we
                 * must load the state each time a top-level action accesses it.
                 * Otherwise we can continue to use the last state as in
                 * Dharma/ArjunaII.
                 */

                if (loadObjectState())
                {
                    InputObjectState oldState = null;

                    try
                    {
                        oldState = objectStore
                                .read_committed(objectUid, type());
                    }
                    catch (ObjectStoreException e)
                    {
                        oldState = null;
                    }

                    if (oldState != null)
                    {
                        if ((result = restore_state(oldState,
                                ObjectType.ANDPERSISTENT)))
                        {
                            currentStatus = ObjectStatus.ACTIVE;
                        }

                        oldState = null;
                    }
                    else
                    {
                        if (tsLogger.arjLoggerI18N.isWarnEnabled())
                        {
                            tsLogger.arjLoggerI18N.warn(
                                    "com.arjuna.ats.arjuna.StateManager_2",
                                    new Object[]
                                    { objectUid, type() });
                        }

                        return false;
                    }
                }
                else
                {
                    if (currentStatus == ObjectStatus.PASSIVE_NEW)
                        currentStatus = ObjectStatus.ACTIVE_NEW;
                    else
                        currentStatus = ObjectStatus.ACTIVE;
                }
            }
            else
            {
                if (currentStatus == ObjectStatus.PASSIVE_NEW)
                    currentStatus = ObjectStatus.ACTIVE_NEW;
                else
                    currentStatus = ObjectStatus.ACTIVE;
            }

            /*
             * Create ActivationRecord if status changed Passive->Active or if
             * object is a new persistent object.
             */

            if (forceAR
                    || ((currentStatus == ObjectStatus.ACTIVE) || (currentStatus == ObjectStatus.PASSIVE_NEW))
                    && (action != null))
            {
                int arStatus = AddOutcome.AR_ADDED;
                ActivationRecord ar = new ActivationRecord(oldStatus, this,
                        action.topLevelAction());

                if ((arStatus = action.add(ar)) != AddOutcome.AR_ADDED)
                {
                    ar = null;

                    if (forceAR)
                    {
                        synchronized (usingActions)
                        {
                            usingActions.remove(action.topLevelAction()
                                    .get_uid());
                        }
                    }

                    if (arStatus == AddOutcome.AR_REJECTED)
                        result = false;
                }
                else
                {
                    /*
                     * We never reset activated, so we can optimise state
                     * loading/unloading in the case of SINGLE object model
                     */

                    currentlyActivated = activated = true;
                }
            }
        }

        return result;
    }

    /**
     * This operation deactivates a persistent object. It behaves in a similar
     * manner to the activate operation, but has an extra argument which defines
     * whether the object's state should be committed or written as a shadow.
     * The root of the object store is <code>null</code>. It is assumed that
     * this is being called during a transaction commit.
     *
     * @return <code>true</code> on success, <code>false</code> otherwise.
     */

    public boolean deactivate ()
    {
        return deactivate(null, true);
    }

    /**
     * This operation deactivates a persistent object. It behaves in a similar
     * manner to the activate operation, but has an extra argument which defines
     * whether the object's state should be commited now or not. It is assumed
     * that this is being called during a transaction commit.
     *
     * @return <code>true</code> on success, <code>false</code> otherwise.
     */

    public boolean deactivate (String rootName)
    {
        return deactivate(rootName, true);
    }

    /**
     * This operation deactivates a persistent object. It behaves in a similar
     * manner to the activate operation, but has an extra argument which defines
     * whether the object's state should be commited now or not.
     *
     * @return <code>true</code> on success, <code>false</code> otherwise.
     */

    public synchronized boolean deactivate (String rootName, boolean commit)
    {
        if (tsLogger.arjLogger.isDebugEnabled())
        {
            tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
                    VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_STATE_MAN,
                    "StateManager::deactivate("
                            + ((rootName != null) ? rootName : "null") + ", "
                            + commit + ") for object-id " + objectUid);
        }

        boolean result = false;

        if ((currentlyActivated && (myType == ObjectType.ANDPERSISTENT))
                || loadObjectState())
        {
            setupStore(rootName);

            if ((currentStatus == ObjectStatus.ACTIVE_NEW)
                    || (currentStatus == ObjectStatus.ACTIVE))
            {
                String tn = type();
                OutputObjectState newState = new OutputObjectState(objectUid,
                        tn);

                /*
                 * Call save_state again to possibly get a persistent
                 * representation of the object.
                 */

                if (save_state(newState, myType))
                {
                    try
                    {
                        if (commit)
                            result = objectStore.write_committed(objectUid, tn,
                                    newState);
                        else
                            result = objectStore.write_uncommitted(objectUid,
                                    tn, newState);
                    }
                    catch (ObjectStoreException e)
                    {
                        if (tsLogger.arjLoggerI18N.isWarnEnabled())
                            tsLogger.arjLoggerI18N.warn(
                                    "com.arjuna.ats.arjuna.StateManager_3", e);

                        result = false;
                    }
                }
                else
                {
                    if (tsLogger.arjLoggerI18N.isWarnEnabled())
                        tsLogger.arjLoggerI18N
                                .warn("com.arjuna.ats.arjuna.StateManager_4");
                }

                /*
                 * Not needed any more because activation record does this when
                 * all actions are forgotten. if (result) currentStatus =
                 * ObjectStatus.PASSIVE;
                 */
            }
        }
        else
        {
            result = true;
        }

        return result;
    }

    /**
     * @return the object's current status (active, passive, ...)
     */

    public synchronized int status ()
    {
        return currentStatus;
    }

    /**
     * @return the type of the object (persistent, recoverable, ...)
     */

    public synchronized int ObjectType ()
    {
        return myType;
    }

    /**
     * @return the object's unique identifier.
     */

    public final Uid get_uid ()
    {
        return objectUid;
    }

    /**
     * Destroy the object (e.g., remove its state from the persistent store.)
     * Calls to destroy for volatile objects (ones not maintained within the
     * volatile object store) are ignored, and FALSE is returned.
     *
     * @return <code>true</code> on success, <code>false</code> otherwise.
     */

    public synchronized boolean destroy ()
    {
        if (tsLogger.arjLoggerI18N.isDebugEnabled())
        {
            tsLogger.arjLoggerI18N.debug(DebugLevel.FUNCTIONS,
                    VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_STATE_MAN,
                    "com.arjuna.ats.arjuna.StateManager_5", new Object[]
                    { objectUid });
        }

        boolean result = false;

        if (objectStore != null)
        {
            BasicAction action = BasicAction.Current();

            if (action != null) // add will fail if the status is wrong!
            {
                DisposeRecord dr = new DisposeRecord(objectStore, this);

                if (action.add(dr) != AddOutcome.AR_ADDED)
                {
                    dr = null;

                    if (tsLogger.arjLoggerI18N.isWarnEnabled())
                    {
                        tsLogger.arjLoggerI18N
                                .warn("com.arjuna.ats.arjuna.StateManager_6");
                    }
                }
                else
                    result = true;
            }
            else
            {
                try
                {
                    result = objectStore.remove_committed(get_uid(), type());

                    /*
                     * Once destroyed, we can never use the object again.
                     */

                    if (result)
                        destroyed();
                }
                catch (Exception e)
                {
                    if (tsLogger.arjLoggerI18N.isWarnEnabled())
                        tsLogger.arjLoggerI18N.warn(
                                "com.arjuna.ats.arjuna.StateManager_7", e);

                    result = false;
                }
            }
        }
        else
        {
            /*
             * Not a persistent object!
             */

            if (tsLogger.arjLoggerI18N.isWarnEnabled())
            {
                tsLogger.arjLoggerI18N
                        .warn("com.arjuna.ats.arjuna.StateManager_8");
            }
        }

        return result;
    }

    /**
     * The following function disables recovery for an object by setting the
     * ObjectType to NEITHER (RECOVERABLE or ANDPERSISTENT). The value of this
     * variable is checked in the modified operation so that no recovery
     * information is created if myType is set to NEITHER.
     */

    public synchronized void disable ()
    {
        myType = ObjectType.NEITHER;
    }

    /**
     * Print out information about the object.
     */

    public void print (PrintWriter strm)
    {
        strm.println("Uid: " + objectUid);
        strm.println("Type: " + type());
    }

    /**
     * The object's type. Derived classes should override this to reflect their
     * type structure. Typically this string is used for locating the object
     * state in an object store, and reflects the hierarchy structure of the
     * object.
     */

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

    /**
     * @return the root of the object store this instance will use if it has to
     *         save the state of the object to storage.
     */

    public final String getStoreRoot ()
    {
        if (tsLogger.arjLogger.isDebugEnabled())
        {
            tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
                    VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_STATE_MAN,
                    "StateManager::getStoreRoot ()");
        }

        return storeRoot;
    }

    /**
     * @return the object store this instance will used if it has to save the
     *         state of the object to storage.
     */

    public ObjectStore getStore ()
    {
        if (tsLogger.arjLogger.isDebugEnabled())
        {
            tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
                    VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_STATE_MAN,
                    "StateManager::getStore ()");
        }

        if (objectStore == null)
            setupStore();

        return objectStore;
    }

    /**
     * Pack the necessary information for crash recovery.
     *
     * @since JTS 2.1.
     */

    protected void packHeader (OutputObjectState os, Header hdr)
            throws IOException
    {
        /*
         * If there is a transaction present than pack the process Uid of this
         * JVM and the tx id. Otherwise pack a null Uid.
         */

        Uid txId = ((hdr == null) ? null : hdr.getTxId());
        Uid processUid = ((hdr == null) ? null : hdr.getProcessId());
       
        try
        {
            // pack the marker first.

            os.packString(StateManager.marker);

            /*
             * Only pack something if there is a transaction. Otherwise the
             * application is driving this object manually, and all bets are
             * off!
             */

            if (txId != null)
            {
                UidHelper.packInto(txId, os);
                UidHelper.packInto(processUid, os);
            }
            else
                UidHelper.packInto(Uid.nullUid(), os);
           
            long birthDate = System.currentTimeMillis();
           
            if (tsLogger.arjLogger.isDebugEnabled())
            {
                tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
                        VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_STATE_MAN,
                        "StateManager.packHeader for object-id " + get_uid()
                                + " birth-date " + birthDate);
            }
           
            os.packLong(birthDate);
        }
        catch (IOException ex)
        {
            throw ex;
        }
        catch (Exception e)
        {
            IOException ioException = new IOException(e.toString());
            ioException.initCause(e);
            throw ioException;
        }
    }

    /**
     * Unpack the crash recovery state header information and return it.
     *
     * @since JTS 2.1.
     * @param txId
     *            the identity of the transaction that last caused the state to
     *            be written to the object store.
     */

    protected void unpackHeader (InputObjectState os, Header hdr)
            throws IOException
    {
        try
        {
            if (hdr == null)
                throw new NullPointerException();
           
            Uid txId = null;
            Uid processUid = null;
           
            String myState = os.unpackString();

            if (myState.equals(StateManager.marker))
            {
                txId = UidHelper.unpackFrom(os);

                /*
                 * Is there going to be a Uid to unpack?
                 */

                if (!txId.equals(Uid.nullUid()))
                    processUid = UidHelper.unpackFrom(os);
            }
            else
            {
                if (tsLogger.arjLoggerI18N.isWarnEnabled())
                {
                    tsLogger.arjLoggerI18N
                            .warn("com.arjuna.ats.arjuna.StateManager_9");
                }

                throw new IOException(tsLogger.log_mesg
                        .getString("com.arjuna.ats.arjuna.StateManager_15"));
            }
           
            long birthDate = os.unpackLong();
           
            if (tsLogger.arjLogger.isDebugEnabled())
            {
                tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
                        VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_STATE_MAN,
                        "StateManager.unpackHeader for object-id " + get_uid()
                                + " birth-date " + birthDate);
            }
           
            hdr.setTxId(txId);
            hdr.setProcessId(processUid);
        }
        catch (IOException ex)
        {
            throw ex;
        }
        catch (final Throwable e)
        {
            IOException ioException = new IOException(e.toString());
            ioException.initCause(e);
            throw ioException;
        }
    }

    /**
     * The following function checks to see if the object is going out of scope
     * while an action is still running.
     */

    protected void terminate ()
    {
        if (tsLogger.arjLogger.isDebugEnabled())
        {
            tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
                    VisibilityLevel.VIS_PROTECTED, FacilityCode.FAC_STATE_MAN,
                    "StateManager::terminate() for object-id " + get_uid());
        }

        cleanup(true);
    }

    protected final synchronized void setStatus (int s)
    {
        currentStatus = s;
    }

    /**
     * Create object with specific uid. This constructor is primarily used when
     * recreating an existing object. The object type is set to 'ANDPERSISTENT'
     * this is equivalent to invoking persist in the object constructor.
     */

    protected StateManager(Uid objUid)
    {
        this(objUid, ObjectType.ANDPERSISTENT, ObjectModel.SINGLE);
    }

    protected StateManager(Uid objUid, int ot)
    {
        this(objUid, ot, ObjectModel.SINGLE);
    }

    protected StateManager (Uid objUid, int ot, int om)
    {
        objectModel = om;

        if (ot == ObjectType.NEITHER)
        {
            modifyingActions = null;
            usingActions = null;
        }
        else
        {
            modifyingActions = new Hashtable();
            usingActions = new Hashtable();
        }

        activated = false;
        currentlyActivated = false;
        currentStatus = ObjectStatus.PASSIVE;
        initialStatus = ObjectStatus.PASSIVE;
        myType = ot;
        objectStore = null;
        storeRoot = null;

        objectUid = objUid;

        if (tsLogger.arjLogger.isDebugEnabled())
        {
            tsLogger.arjLogger.debug(DebugLevel.CONSTRUCTORS,
                    VisibilityLevel.VIS_PROTECTED, FacilityCode.FAC_STATE_MAN,
                    "StateManager::StateManager( " + get_uid() + " )");
        }
    }

    protected StateManager()
    {
        this(ObjectType.RECOVERABLE);
    }

    protected StateManager(int ot)
    {
        this(ot, ObjectModel.SINGLE);
    }

    protected StateManager (int ot, int om)
    {
        objectModel = om;
       
        if (ot == ObjectType.NEITHER)
        {
            modifyingActions = null;
            usingActions = null;
        }
        else
        {
            modifyingActions = new Hashtable();
            usingActions = new Hashtable();
        }

        activated = false;
        currentlyActivated = false;
        currentStatus = (((objectModel == ObjectModel.SINGLE) && (ot == ObjectType.RECOVERABLE)) ? ObjectStatus.ACTIVE
                : ObjectStatus.PASSIVE_NEW);
        initialStatus = currentStatus;
        myType = ot;
        objectStore = null;
        storeRoot = null;

        objectUid = new Uid();

        if (tsLogger.arjLogger.isDebugEnabled())
        {
            tsLogger.arjLogger.debug(DebugLevel.CONSTRUCTORS,
                    VisibilityLevel.VIS_PROTECTED, FacilityCode.FAC_STATE_MAN,
                    "StateManager::StateManager( " + ot + ", "+om+" )");
        }
    }

    /*
     * Protected non-virtual functions.
     */

    /**
     * The object's state is about to be modified, and StateManager should take
     * a snapshot of the state if the object is being used within a transaction.
     *
     * @return <code>true</code> on success, <code>false</code> otherwise.
     */

    protected synchronized boolean modified ()
    {
        if (tsLogger.arjLogger.isDebugEnabled())
        {
            tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
                    VisibilityLevel.VIS_PROTECTED, FacilityCode.FAC_STATE_MAN,
                    "StateManager::modified() for object-id " + get_uid());
        }

        BasicAction action = BasicAction.Current();
        RecoveryRecord record = null;

        if ((myType == ObjectType.NEITHER)
                || (currentStatus == ObjectStatus.DESTROYED)) /*
                                                               * NEITHER => no
                                                               * recovery info
                                                               */
        {
            return true;
        }

        if (currentStatus == ObjectStatus.PASSIVE)
        {
            if (tsLogger.arjLoggerI18N.isWarnEnabled())
                tsLogger.arjLoggerI18N
                        .warn("com.arjuna.ats.arjuna.StateManager_10");
            activate();
        }

        /*
         * Need not have gone through active if new object.
         */

        if (currentStatus == ObjectStatus.PASSIVE_NEW)
            currentStatus = ObjectStatus.ACTIVE_NEW;

        if (action != null)
        {
            /*
             * Check if this is the first call to modified in this action.
             * BasicList insert returns FALSE if the entry is already present.
             */

            synchronized (modifyingActions)
            {
                if ((modifyingActions.size() > 0)
                        && (modifyingActions.get(action.get_uid()) != null))
                {
                    return true;
                }
                else
                    modifyingActions.put(action.get_uid(), action);
            }

            /* If here then its a new action */

            OutputObjectState state = new OutputObjectState(objectUid, type());
            int rStatus = AddOutcome.AR_ADDED;

            if (save_state(state, ObjectType.RECOVERABLE))
            {
                if ((myType == ObjectType.RECOVERABLE)
                        && (objectModel == ObjectModel.SINGLE))
                {
                    record = new RecoveryRecord(state, this);
                }
                else
                    record = new PersistenceRecord(state, objectStore, this);

                if ((rStatus = action.add(record)) != AddOutcome.AR_ADDED)
                {
                    synchronized (modifyingActions)
                    {
                        modifyingActions.remove(action.get_uid()); // remember
                                                                   // to
                                                                   // unregister
                                                                   // with
                                                                   // action
                    }

                    record = null;

                    return false;
                }
            }
            else
                return false;
        }

        return true;
    }

    /**
     * The persist function changes the type of the object from RECOVERABLE to
     * ANDPERSISTENT. No changes are made unless the status of the object is
     * ACTIVE, so it is not possible to change the type of the object if it has
     * been modified.
     */

    protected final synchronized void persist ()
    {
        if (tsLogger.arjLogger.isDebugEnabled())
        {
            tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
                    VisibilityLevel.VIS_PROTECTED, FacilityCode.FAC_STATE_MAN,
                    "StateManager::persist() for object-id " + get_uid());
        }

        if (currentStatus == ObjectStatus.ACTIVE)
        {
            currentStatus = ObjectStatus.PASSIVE_NEW;
            myType = ObjectType.ANDPERSISTENT;
        }
    }

    /**
     * Object cleanup. Attempt sane cleanup when object is deleted. Handle
     * perverse cases where multiple actions are still active as object dies.
     *
     * @param fromTerminate
     *            indicates whether this method is being called from the
     *            <code>terminate</code> method, or from elsewhere.
     * @see StateManager#terminate
     */

    protected final synchronized void cleanup (boolean fromTerminate)
    {
        if (tsLogger.arjLogger.isDebugEnabled())
        {
            tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
                    VisibilityLevel.VIS_PROTECTED, FacilityCode.FAC_STATE_MAN,
                    "StateManager::cleanup() for object-id " + get_uid());
        }

        if (myType == ObjectType.NEITHER)
            return;

        BasicAction action = null;

        synchronized (usingActions)
        {
            if (usingActions != null)
            {
                Enumeration e = usingActions.keys();

                while (e.hasMoreElements())
                {
                    action = (BasicAction) usingActions.remove(e.nextElement());

                    if (action != null)
                    {
                        /*
                         * Pop actions off using list. Don't check if action is
                         * running below so that cadavers can be created in
                         * commit protocol too.
                         */

                        AbstractRecord record = null;
                        int rStatus = AddOutcome.AR_ADDED;

                        if ((currentStatus == ObjectStatus.ACTIVE_NEW)
                                || (currentStatus == ObjectStatus.ACTIVE))
                        {
                            OutputObjectState state = null;

                            if (tsLogger.arjLoggerI18N.isWarnEnabled())
                            {
                                tsLogger.arjLoggerI18N
                                        .warn(
                                                "com.arjuna.ats.arjuna.StateManager_11",
                                                new Object[]
                                                { objectUid, type() });
                            }

                            /*
                             * If we get here via terminate its ok to do a
                             * save_state.
                             */

                            if (fromTerminate)
                            {
                                state = new OutputObjectState(objectUid, type());

                                if (!save_state(state, myType))
                                {
                                    if (tsLogger.arjLoggerI18N.isWarnEnabled())
                                        tsLogger.arjLoggerI18N
                                                .warn("com.arjuna.ats.arjuna.StateManager_12");
                                    /* force action abort */

                                    action.preventCommit();
                                }
                            }
                            else
                            {
                                /* otherwise force action abort */

                                action.preventCommit();
                            }

                            /*
                             * This should be unnecessary - but just in case.
                             */

                            setupStore(storeRoot);

                            record = new CadaverRecord(state, objectStore, this);

                            if ((rStatus = action.add(record)) != AddOutcome.AR_ADDED)
                                record = null;
                        }

                        if (currentlyActivated
                                && (currentStatus != ObjectStatus.DESTROYED))
                        {
                            record = new CadaverActivationRecord(this);

                            if ((rStatus = action.add(record)) == AddOutcome.AR_ADDED)
                            {
                                currentStatus = ObjectStatus.PASSIVE;
                            }
                            else
                                record = null;
                        }
                    }
                }
            }
        }

        /*
         * Here the object must be either RECOVERABLE or PERSISTENT. Whether or
         * not an action exists we still need to reset the object status to
         * avoid possible later confusion What it gets set to is not important
         * really as long as it gets changed from ACTIVE_NEW which might cause
         * any running action to abort.
         */

        if (currentStatus == ObjectStatus.ACTIVE_NEW)
        {
            if ((myType == ObjectType.RECOVERABLE)
                    && (objectModel == ObjectModel.SINGLE))
            {
                currentStatus = ObjectStatus.ACTIVE;
            }
            else
            {
                currentStatus = ObjectStatus.PASSIVE;
            }
        }

        currentlyActivated = false;
    }

    /**
     * Make sure the object store is set up, if required. The root of the object
     * store is assumed to be <code>null</code>.
     */

    protected final void setupStore ()
    {
        setupStore(null);
    }

    @SuppressWarnings("unchecked")
    protected synchronized void setupStore (String rootName)
    {
        setupStore(rootName, arjPropertyManager.getObjectStoreEnvironmentBean()
                .getObjectStoreType());
    }

    /**
     * Make sure the object store is set up, if required.
     *
     * @param rootName
     *            indicates the root of the object store.
     */

    @SuppressWarnings("unchecked")
    protected synchronized void setupStore (String rootName,
            String objectStoreType)
    {
        if (tsLogger.arjLogger.isDebugEnabled())
        {
            tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
                    VisibilityLevel.VIS_PROTECTED, FacilityCode.FAC_STATE_MAN,
                    "StateManager::setupStore ( "
                            + ((rootName != null) ? rootName : "null") + " )");
        }

        if (!loadObjectState())
            return;

        /*
         * Already setup? Assume type will not change once object is created.
         */

        if (objectStore != null)
            return;

        if (rootName == null)
            rootName = arjPropertyManager.getObjectStoreEnvironmentBean()
                    .getLocalOSRoot();

        /* Check if we have a store */

        if (storeRoot != null)
        {
            /* Attempting to reuse it ? */

            if ((rootName == null) || (rootName.compareTo("") == 0)
                    || (rootName.compareTo(storeRoot) == 0))
            {
                return;
            }

            /* No - destroy old store and create new */

            objectStore = null;
        }

        if (rootName == null)
        {
            rootName = "";
        }

        /* Create store now */

        storeRoot = new String(rootName);

        if ((myType == ObjectType.ANDPERSISTENT)
                || (myType == ObjectType.NEITHER))
        {
            int sharedStatus = ((objectModel == ObjectModel.SINGLE) ? ObjectStore.OS_UNSHARED
                    : ObjectStore.OS_SHARED);

            arjPropertyManager.getObjectStoreEnvironmentBean().setShare(
                    sharedStatus);
            arjPropertyManager.getObjectStoreEnvironmentBean().setLocalOSRoot(
                    storeRoot);

            try
            {
                Class osc = Class.forName(objectStoreType);

                objectStore = (ObjectStore) osc.newInstance();
            }
            catch (final Throwable ex)
            {
                throw new FatalError(tsLogger.log_mesg
                        .getString("com.arjuna.ats.arjuna.StateManager_16")
                        + " " + objectStoreType);
            }
        }
        else
        {
            /*
             * Currently we should never get here! However, since Arjuna
             * supports a volatile (in memory) object store we will also
             * eventually, probably through a set of native methods.
             */

            if (tsLogger.arjLoggerI18N.isWarnEnabled())
                tsLogger.arjLoggerI18N
                        .warn("com.arjuna.ats.arjuna.StateManager_13");

            throw new FatalError(tsLogger.log_mesg
                    .getString("com.arjuna.ats.arjuna.StateManager_14"));

            // objectStore = new
            // ObjectStore(ArjunaNames.Implementation_ObjectStore_VolatileStore
            // (), storeRoot);
        }

        /*
         * Do any work needed to initialise the object store. Really only makes
         * sense for replicated object store where we attempt to do early
         * binding to the replicas.
         */

        objectStore.initialise(get_uid(), type());
    }

    /**
     * Do we need to load the object's state?
     *
     * @return <code>true</code> if the object state should be loaded,
     *         <code>false</code> otherwise.
     */

    protected final boolean loadObjectState ()
    {
        boolean load = (objectModel != ObjectModel.SINGLE);

        /*
         * MULTIPLE object model requires loading of state every time, even if
         * we are RECOVERABLE - we use the volatile store.
         */

        if (!load)
        {
            /*
             * Must be SINGLE object model. So, is this the first time? If so,
             * load state.
             */

            if ((myType != ObjectType.RECOVERABLE) && (!activated))
                load = true;
        }

        return load;
    }

    /*
     * Called ONLY by ActivationRecords!
     */

    /**
     * Remove action from list of using actions. If the action list empties
     * reset state to PASSIVE. The second param tells why the action should be
     * forgotten. This aids in resetting the state correctly.
     */

    protected final synchronized boolean forgetAction (BasicAction action,
            boolean committed, int recordType)
    {
        if (tsLogger.arjLogger.isDebugEnabled())
        {
            tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
                    VisibilityLevel.VIS_PROTECTED, FacilityCode.FAC_STATE_MAN,
                    "StateManager::forgetAction("
                            + ((action != null) ? action.get_uid() : Uid
                                    .nullUid()) + ")" + " for object-id "
                            + objectUid);
        }

        synchronized (modifyingActions)
        {
            modifyingActions.remove(action.get_uid());
        }

        if (recordType != RecordType.RECOVERY)
        {
            synchronized (usingActions)
            {
                if (usingActions != null)
                {
                    usingActions.remove(action.get_uid());

                    if (usingActions.size() == 0)
                    {
                        if (committed)
                        {
                            if ((myType == ObjectType.RECOVERABLE)
                                    && (objectModel == ObjectModel.SINGLE))
                            {
                                initialStatus = currentStatus = ObjectStatus.ACTIVE;
                            }
                            else
                            {
                                initialStatus = currentStatus = ObjectStatus.PASSIVE;
                            }
                        }
                        else
                            currentStatus = initialStatus;
                    }
                }
            }
        }

        return true;
    }

    /**
     * Remember that the specified transaction is using the object.
     */

    protected final synchronized boolean rememberAction (BasicAction action,
            int recordType)
    {
        if (tsLogger.arjLogger.isDebugEnabled())
        {
            tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
                    VisibilityLevel.VIS_PROTECTED, FacilityCode.FAC_STATE_MAN,
                    "StateManager::rememberAction("
                            + ((action != null) ? action.get_uid() : Uid
                                    .nullUid()) + ")" + " for object-id "
                            + objectUid);
        }

        boolean result = false;

        if (recordType != RecordType.RECOVERY)
        {
            if ((action != null) && (action.status() == ActionStatus.RUNNING))
            {
                synchronized (usingActions)
                {
                    if (usingActions.get(action.get_uid()) == null)
                        usingActions.put(action.get_uid(), action);
                }
            }
        }

        result = true;

        return result;
    }

    /**
     * @return the mutex object used to lock this object.
     * @since JTS 2.1.
     */

    protected final Mutex getMutex ()
    {
        return mutex;
    }

    /**
     * @return the result of the attempt to lock this object.
     * @since JTS 2.1.
     */

    protected final boolean lockMutex ()
    {
        if (mutex.lock() == Mutex.LOCKED)
            return true;
        else
            return false;
    }

    /**
     * @return the result of the attempt to unlock this object.
     * @since JTS 2.1.
     */

    protected final boolean unlockMutex ()
    {
        if (mutex.unlock() == Mutex.UNLOCKED)
            return true;
        else
            return false;
    }

    /**
     * @return <code>true</code> if the object was locked, <code>false</code> if
     *         the attempt would cause the thread to block.
     * @since JTS 2.1.
     */

    protected final boolean tryLockMutex ()
    {
        if (mutex.tryLock() == Mutex.LOCKED)
            return true;
        else
            return false;
    }

    /*
     * Package scope.
     */

    /*
     * Set the status of the object to destroyed so that we can no longer use
     * it.
     */

    synchronized final void destroyed ()
    {
        currentStatus = ObjectStatus.DESTROYED;
    }

    protected Hashtable modifyingActions;

    protected Hashtable usingActions;

    protected Uid objectUid;

    protected int objectModel = ObjectModel.SINGLE;

    private boolean activated;

    private boolean currentlyActivated;

    private int currentStatus;

    private int initialStatus;

    private int myType;

    private ObjectStore objectStore;

    private String storeRoot;

    private Mutex mutex = new Mutex();

    private static final String marker = "#ARJUNA#";
}
TOP

Related Classes of com.arjuna.ats.arjuna.StateManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.