/**
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 -
 - JASS: Java Advanced tranSaction Support
 -
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 -
 - This module was originally developed by 
 -
 -    LSD (Distributed Systems Lab, http://lsd.ls.fi.upm.es/lsd/lsd.htm)
 -    at Universidad Politecnica de Madrid (UPM) as an ObjectWeb Consortium
 -    (http://www.objectweb.org) project.
 -
 -    This project has been partially funded by the European Commission under
 -    the IST programme of V FP grant IST-2001-37126 and by the Spanish
 -    Ministry of Science & Technology (MCyT) grants TIC2002-10376-E and
 -    TIC2001-1586-C03-02
 -
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 -  The original code and portions created by LSD are 
 -  Copyright (c) 2004 LSD (UPM)  
 -  All rights reserved.
 -  
 - Redistribution and use in source and binary forms, with or without 
 - modification, are permitted provided that the following conditions are met:
 -
 - -Redistributions of source code must retain the above copyright notice, this
 - list of conditions and the following disclaimer. 
 -
 - -Redistributions in binary form must reproduce the above copyright notice, 
 - this list of conditions and the following disclaimer in the documentation 
 - and/or other materials provided with the distribution. 
 -
 - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
 - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 - POSSIBILITY OF SUCH DAMAGE.
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 -
 -  Author: Francisco Perez Sorrosal (frperezs)
 -
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/

package org.objectweb.jass.as;

import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;

import javax.activity.ActionNotFoundException;
import javax.activity.ActivityCoordinator;
import javax.activity.ActivityNotProcessedException;
import javax.activity.ActivityPendingException;
import javax.activity.CompletionStatus;
import javax.activity.ContextPendingException;
import javax.activity.CoordinationInformation;
import javax.activity.GlobalId;
import javax.activity.InvalidActivityException;
import javax.activity.InvalidStateException;
import javax.activity.NoImplementException;
import javax.activity.NotOriginatorException;
import javax.activity.Outcome;
import javax.activity.Signal;
import javax.activity.SignalSetActiveException;
import javax.activity.SignalSetUnknownException;
import javax.activity.Status;
import javax.activity.SystemException;
import javax.activity.coordination.Action;
import javax.activity.coordination.SignalSet;

import org.apache.log4j.Logger;
import org.jboss.util.timeout.Timeout;
import org.jboss.util.timeout.TimeoutFactory;
import org.jboss.util.timeout.TimeoutTarget;
import org.objectweb.jass.as.util.SignalSetInfo;

/**
 * Each object of this class represents an activity and its coordinator. 
 * Implements the ASActivityCoordinator interface in order to be able to 
 * broadcast signals to registered actions. TimeoutTarget is implemented in 
 * order to be warned of timeout.
 * @author fran
 * Date: Feb 16, 2004
 * org.objectweb.jass.asActivityImpl.java
 */
public class ActivityImpl
	implements ActivityCoordinator, TimeoutTarget, Serializable {

	// Constants -----------------------------------------------------

	// Attributes ----------------------------------------------------

	private static Logger log = Logger.getLogger(ActivityImpl.class);

	// Unique activity identifier
	private ActivityIdImpl activityId;
	// Indicates the superior ActivityCoordinator of the activity
	private ActivityImpl superior = null;
	// Contains the childs of the activity
	private LinkedList childs = new LinkedList();
	// Activity current status
	private int status;
	// Activity completion status
	private int completionStatus;
	// The time the activity was started
	private long start;
	// Activity timeout
	private Timeout timeout = null;
	private boolean timedOut = false;
	// Contains objects representing information about the registered
	// signalSets. The keys are the signalSetNames
	private HashMap signalSets = new HashMap();
	// Indicates the name of the completion SignalSet
	private String completionSSName = null;
	// A reference to UserActivity implementation
	private UserActivityImpl manager = null;
	// Mutex for thread-safety. This should only be changed in the lock()
	// and unlock() methods
	private boolean locked = false;
	// Flags that the activity has finished
	private boolean done = false;

	private HashSet threads = new HashSet();

	// Constructors ---------------------------------------------------------------

	public ActivityImpl(
		UserActivityImpl userActivityImpl,
		ActivityIdImpl activityId,
		ActivityImpl superior,
		int timeout) {

		log.info(
			Thread.currentThread().getName()
				+ " STARTING ACTIVITY "
				+ activityId.print()
				+ " ...");
		this.manager = userActivityImpl;
		this.activityId = activityId;
		this.superior = superior;
		// Now its time to set the starting time of the currentActivity and
		// the timeout in milliseconds (timeout parameter in secs * 1000)
		start = System.currentTimeMillis();
		// We only set the timeout when its distinct from zero
		if (timeout != 0) {
			this.timeout =
				TimeoutFactory.createTimeout(start + (timeout * 1000), this);
			log.info("\tTimeout = " + timeout);
		} else
			log.info("\tTimeout NOT set to this activity");
		// Finally, we set the correct statuses to start an activity
		this.status = Status.StatusActive;
		this.completionStatus = CompletionStatus.CompletionStatusFail;
		log.info(
			Thread.currentThread().getName()
				+ " ACTIVITY "
				+ activityId.print()
				+ " STARTED!!!");
	}

	// Public ---------------------------------------------------------------------

	// ActivityCoordinator implementation -------------------------------------

	/**
	 * Establishes an interest relationship between the specified Action and
	 * SignalSet for the Activity represented by the target ActivityCoordinator.
	 * When a Signal which is a member of the SignalSet is sent, the
	 * processSignal method of the Action will be invoked with that  Signal. If
	 * multiple Actions are registered, then priority may be used to place an
	 * order on how they will be invoked when Signals are sent: higher priority
	 * Actions will occur first in the Action list, and hence be invoked before
	 * other, lower priority, Actions. The priority value must be a positive
	 * value; a value of zero means that the Activity Service implementation is
	 * free to place the Action at any point in the Action list. If the
	 * specified Action is registered multiple times for the same SignalSet
	 * then it will be invoked multiple times with the Signals from that
	 * SignalSet.
	 * @param action
	 * @param signalSetName
	 * @param priority
	 * @throws SignalSetUnknownException
	 * @throws SystemException
	 * @throws java.lang.IllegalStateException
	 */
	public void addAction(
		Action action,
		java.lang.String signalSetName,
		int priority)
		throws
			SignalSetUnknownException,
			SystemException,
			java.lang.IllegalStateException {

		SignalSetInfo ssInfo = null;

		// If the signalSet does not exists in the activity
		// instance (signalSets HashMap)...
		if (!signalSets.containsKey(signalSetName)) {
			log.info(
				activityId.print()
					+ " SignalSet "
					+ signalSetName
					+ " not yet registered. Registering SignalSet and "
					+ "adding action...");
			SignalSet signalSet = null;
			try {
				// ... First, we obtain an instance and associate with its name...
				signalSet = manager.getService().getSignalSet(signalSetName);
				signalSet.setActivityCoordinator(this);
				ssInfo = new SignalSetInfo(signalSet);
				ssInfo.addAction(action, priority, false);
				signalSets.put(signalSetName, ssInfo);
				log.info(
					activityId.print()
						+ " SignalSet "
						+ signalSetName
						+ " is now registered");
			} catch (SignalSetUnknownException e) {
				log.warn("Null SignalSet");
				e.printStackTrace();
			} catch (NoImplementException e) {
				e.printStackTrace();
			} catch (SignalSetActiveException e) {
				e.printStackTrace();
			}
		} else { // The signalSet already exists in the HashMap
			log.info(
				activityId.print()
					+ " SignalSet already registered. "
					+ "Adding action...");
			ssInfo = (SignalSetInfo) signalSets.get(signalSetName);
			ssInfo.addAction(action, priority, false);
			signalSets.put(signalSetName, ssInfo);
		}

		log.info(
			activityId.print()
				+ " Action added. Actions in SignalSet "
				+ signalSetName
				+ " = "
				+ ssInfo.getNumberRegisteredActions());

	}

	/** Repasar
	 * Establishes an interest relationship between the specified
	 * Action and all SignalSets used by the Activity represented
	 * by the target ActivityCoordinator. When a Signal which is
	 * a member of any of the SignalSets is sent, the processSignal
	 * method of the Action will be invoked with that Signal.
	 * If multiple Actions are registered, then priority may be used
	 * to place an order on how they will be invoked when Signals are
	 * sent: higher priority Actions will occur first in the Action
	 * list, and hence be invoked before other, lower priority, Actions.
	 * The priority value must be a positive value; a value of zero
	 * means that the Activity Service implementation is free to place
	 * the Action at any point in the Action list.
	 * If the specified Action is registered multiple times with the
	 * target ActivityCoordinator then it will be invoked multiple times
	 * with each Signal.
	 * @param action
	 * @param priority
	 * @throws SystemException
	 * @throws java.lang.IllegalStateException
	 */
	public void addGlobalAction(Action action, int priority)
		throws SystemException, java.lang.IllegalStateException {

		// First, we get all registered signal sets...
		Set keys = signalSets.keySet();
		Iterator it = keys.iterator();

		// ...and then we iterate on them adding the action.
		while (it.hasNext()) {
			SignalSetInfo ssInfo = (SignalSetInfo) it.next();
			ssInfo.addAction(action, priority, true);
		}

	}

	/**
	 * Removes the interest relationship between the specified Action
	 * and SignalSet for the Activity represented by the target
	 * ActivityCoordinator. If signalSetName is an empty String then
	 * the interest relationship is removed between the specified Action
	 * and all SignalSets in the target Activity. This operation has no
	 * effect on any global actions.
	 * If the specified Action was registered multiple times with the
	 * target ActivityCoordinator then only a single instance of this Action
	 * is removed by each call to this method.
	 * @param action
	 * @param signalSetName
	 * @throws ActionNotFoundException
	 * @throws SystemException
	 * @throws java.lang.IllegalStateException
	 */
	public void removeAction(Action action, java.lang.String signalSetName)
		throws
			ActionNotFoundException,
			SystemException,
			java.lang.IllegalStateException {

		boolean result;

		if ((status == Status.StatusCompleting)
			|| (status == Status.StatusCompleted)
			|| (status == Status.StatusCompletingHeuristic))
			throw new IllegalStateException();

		if (!signalSets.containsKey(signalSetName))
			throw new SystemException(
				"SignalSet " + signalSetName + " not registered");

		result =
			((SignalSetInfo) signalSets.get(signalSetName)).removeAction(
				action,
				false);

		if (result == false)
			throw new ActionNotFoundException();
	}

	public void removeGlobalAction(Action action)
		throws
			ActionNotFoundException,
			SystemException,
			java.lang.IllegalStateException {

		if ((status == Status.StatusCompleting)
			|| (status == Status.StatusCompleted)
			|| (status == Status.StatusCompletingHeuristic))
			throw new IllegalStateException();
	}

	/** 
	 * Returns all the Actions that have been registered with
	 * an interest in the specified signalSetName. This method may
	 * be used by a HLS that has determined that an Activity needs
	 * to be persisted and needs to log the Actions registered in the Activity.
	 * @param signalSetName -the name of a SignalSet supported by the HLS.
	 * Only Actions registered with an interest in this SignalSet are
	 * returned by this operation. If this parameter is null then all
	 * global Actions are returned.
	 * @return The array of Actions registered with an interest in the
	 * specified SignalSet. If there are none, then a zero element array
	 * is returned.
	 * @throws SignalSetUnknownException - Thrown if the specified signalSetName
	 * is not recognized.
	 * @throws SystemException - Thrown if the Activity service encounters an
	 * unexpected error condition.
	 */
	public Action[] getActions(java.lang.String signalSetName)
		throws SignalSetUnknownException, SystemException {

		if (!signalSets.containsKey(signalSetName))
			throw new SignalSetUnknownException();

		return ((SignalSetInfo) signalSets.get(signalSetName)).getActions();
	}

	/** OK
	 * Returns the number of Actions, including global Actions,
	 * registered with the target ActivityCoordinator with an
	 * interest in the specified SignalSet.
	 * @param signalSetName
	 * @throws SystemException
	 * @throws SignalSetUnknownException
	 */
	public int getNumberRegisteredActions(java.lang.String signalSetName)
		throws SignalSetUnknownException, SystemException {

		if (!signalSets.containsKey(signalSetName))
			throw new SignalSetUnknownException();

		return ((SignalSetInfo) signalSets.get(signalSetName))
			.getNumberRegisteredActions();
	}

	/**
	 * Sets the name of the SignalSet that should be used for the
	 * distribution of completion signals when the current Activity
	 * completes.
	 * This method can be called multiple times during the lifetime
	 * of an Activity, before the Activity starts completion processing.
	 * The last value specified before completion processing starts
	 * is used during completion processing.
	 * @param signalSetName
	 * @throws SystemException
	 * @throws SignalSetUnknownException
	 * @throws IllegalStateException
	 */
	public void setCompletionSignalSetName(java.lang.String signalSetName)
		throws
			SignalSetUnknownException,
			SystemException,
			java.lang.IllegalStateException {

		this.completionSSName = signalSetName;
	}

	/**
	 * Returns the name of the SignalSet, if any, that will be
	 * used for the distribution of completion signals when the
	 * current Activity completes.
	 * @throws SystemException
	 */
	public java.lang.String getCompletionSignalSetName()
		throws SystemException {

		return this.completionSSName;
	}

	/**
	 * Returns the parent ActivityCoordinator or null if the
	 * target ActivityCoordinator represents a top-level Activity.
	 * @return The parent ActivityCoordinator or null if the target
	 * ActivityCoordinator represents a top-level Activity.
	 * @throws SystemException - Thrown if the Activity service encounters an unexpected
	 * error condition.
	 */
	public ActivityCoordinator getParent() throws SystemException {

		return superior;
	}

	/**
	 * Returns the GlobalId of the Activity represented by the target ActivityCoordinator.
	 * @return The GlobalId of the Activity represented by the target ActivityCoordinator.
	 * @throws SystemException - Thrown if the Activity service encounters an unexpected
	 * error condition.
	 */
	public GlobalId getGlobalId() throws SystemException {

		return activityId;
	}

	/**
	 * Returns the Status of the Activity represented by the target ActivityCoordinator.
	 * @return The Status of the Activity represented by the target ActivityCoordinator.
	 * @throws SystemException - Thrown if the Activity service encounters an unexpected
	 * error condition.
	 */
	public int getStatus() throws SystemException {

		return status;
	}

	/**
	 * Returns the Status of the Activity represented by the target ActivityCoordinator's
	 * parent. If the target ActivityCoordinator represents a top-level
	 * Activity, then the Status of this top-level Activity is returned.
	 * @return The Status of the target ActivityCoordinator's parent Activity or the Status
	 * of the target ActivityCoordinator if the target object represents a
	 * top-level Activity.
	 * @throws SystemException - Thrown if the Activity service encounters an unexpected
	 * error condition.
	 */
	public int getParentStatus() throws SystemException {

		if (superior != null)
			return superior.getStatus();
		else
			return status;
	}

	/**
	 * Returns a printable string describing the Activity represented by the target
	 * ActivityCoordinator.
	 * @return A printable string describing the Activity represented by the target
	 * ActivityCoordinator.
	 * @throws SystemException - Thrown if the Activity service encounters an unexpected
	 * error condition.
	 */
	public java.lang.String getName() throws SystemException {

		return activityId.print();
	}

	/**
	 * Returns true if the specified coord represents the same Activity as the
	 * target ActivityCoordinator.
	 * @return true if the specified coord represents the same Activity as the
	 * target ActivityCoordinator.
	 * @throws SystemException - Thrown if the Activity service encounters an unexpected
	 * error condition.
	 */
	public boolean isSameActivity(ActivityCoordinator coord)
		throws SystemException {

		if (coord != null && coord instanceof ActivityImpl)
			return this.activityId.equals(coord.getGlobalId());

		return false;
	}

	/**
	 * Causes the Activity associated with the target ActivityCoordinator to
	 * complete with the specified CompletionStatus. The Activity service sets
	 * the Status to StatusCompleting before asking any completion SignalSet
	 * to start producing Signals.
	 * If there are any child active or suspended Activities or transactions
	 * and the CompletionStatus is CompletionStatusFail or
	 * CompletionStatusFailOnly then those child Activites will have their
	 * CompletionStatuses set to CompletionStatusFailOnly and any child
	 * transaction will be called to setRollbackOnly.
	 * The UserActivity interface should be used in preference to the
	 * ActivityCoordinator for completing Activities in most circumstances.
	 * This method is provided primarily for use after recovery when the
	 * Activity to be completed is not associated with the calling thread.
	 * @param completionStatus - the CompletionStatus with which the target
	 * Activity should end.
	 * @return The Outcome returned is both set by and given meaning by the
	 * SignalSet used for completion. In the absence of a CompletionSignalSet
	 * a null Outcome object reference is returned.
	 * @throws ActivityPendingException
	 * @throws ContextPendingException
	 * @throws NotOriginatorException
	 * @throws InvalidStateException
	 * @throws ActivityNotProcessedException
	 * @throws SystemException
	 */
	public Outcome completeActivity(int completionStatus)
		throws
			ActivityPendingException,
			ContextPendingException,
			NotOriginatorException,
			InvalidStateException,
			ActivityNotProcessedException,
			SystemException {

		// We set the completion status of this instance and...
		this.completionStatus = completionStatus;
		// ...we delegate on the implementation dependant method
		return completeActivity();
	}

	public Outcome heuristicComplete(int completionStatus)
		throws
			ActivityPendingException,
			ContextPendingException,
			InvalidStateException,
			ActivityNotProcessedException,
			SystemException {

		return null;
	}

	/**
	 * Causes the SignalSet specified by signalSetName to start producing
	 * signals for all registered Actions at times other than during completion.
	 * Pre-defined SignalSets, such as Synchronization, cannot be requested to
	 * produce signals via this method.
	 * @param signalSetName - the name of the SignalSet that is to produce
	 * signals.
	 * @param completionStatus - the CompletionStatus  of the Activity at
	 * the time of calling.
	 * The completionStatus may influence the signals that are produced.
	 * @return The Outcome returned is both set by and given meaning by the
	 * specified SignalSet. It may be null.
	 * @throws SignalSetUnknownException
	 * @throws ActivityNotProcessedException
	 * @throws InvalidActivityException
	 * @throws SystemException
	 */
	public Outcome processSignalSet(
		java.lang.String signalSetName,
		int completionStatus)
		throws
			SignalSetUnknownException,
			ActivityNotProcessedException,
			InvalidActivityException,
			SystemException {

		SignalSet processingSS = null;
		Action[] actions = null;
		Signal signal = null;
		Outcome out = null;
		boolean nextSignal;
		int i;

		try {
			// If the SignalSet is not registered we throw an exception
			if (!signalSets.containsKey(signalSetName))
				throw new SignalSetUnknownException();

			// First we obtain the SignalSet to process
			processingSS =
				((SignalSetInfo) signalSets.get(signalSetName)).getSignalSet();
			processingSS.setCompletionStatus(completionStatus, status);

			log.info(
				activityId.print()
					+ " Processing SignalSet -> "
					+ processingSS.getSignalSetName());

			actions = getActions(signalSetName);
			if (actions != null) {
				while ((signal = processingSS.getSignal()) != null) {
					i = 0;
					nextSignal = false;

					log.info(
						activityId.print()
							+ " is sending "
							+ signal.getName()
							+ " signal to registered actions ("
							+ actions.length
							+ ")");

					while ((i < actions.length) && (nextSignal == false)) {
						if (actions[i] != null) {
							log.info(activityId.print() + " Send number: " + i);
							out = actions[i].processSignal(signal);
							// We obtain and analize the coordination information
							CoordinationInformation info =
								processingSS.setResponse(out);

							if ((info != null)
								&& (info.isActionInterested() == false)) {
								// WARNING: We must remove the action in order
								// to not process it in other processings
								// while this activity still is alive...
								removeAction(actions[i], completionSSName);
								// ...and remove the action from array for the
								// current processing...
								actions[i] = null;
							}

							if ((info != null) && (info.useNextSignal() == true)) {
								nextSignal = true;
								break; // We take other signal
							}
						}
						i = i + 1;
					}
				}
				// Finally, we obtain the SignalSet outcome
				out = processingSS.getOutcome();
			} else
				log.info(activityId.print() + " has NO actions to process");
		} catch (SignalSetUnknownException e) {
			status = Status.StatusError;
			throw new SignalSetUnknownException();
		} catch (Exception e) {
			e.printStackTrace();
		}

		return out;
	}

	// Implements TimeoutTarget -----------------------------------------------

	/**
	 * Called when our timeout expires.
	 */
	public void timedOut(Timeout timeout) {
		try {
			lock();

			log.warn(
				"Activity "
					+ activityId.print()
					+ " TIMED OUT !!!!!"
					+ " Status = "
					+ getStringStatus(status));

			if (this.timeout == null)
				return; // Don't race with timeout cancellation.
			this.timeout = null;
			timedOut = true;

			switch (status) {
				case Status.StatusCompleting :
				case Status.StatusCompleted :
					// Give the chance to finishing
					return;
				case Status.StatusActive :
				case Status.StatusError :
					// We mark the status of the child activities as
					// CompletionStatusFailOnly
					markChildActivitiesWithFail();
					return;
				default :
					log.warn(
						"Unknown status at timeout, current Activity = "
							+ toString());
					status = Status.StatusUnknown;
					return;
			}
		} finally {
			unlock();
		}
	}

	// My Public Methods ------------------------------------------------------

	/**
	 * Associates the calling thread with this activity.
	 */
	public void associateCurrentThread() {
		log.debug(
			"Associating thread "
				+ Thread.currentThread().getName()
				+ " with activity "
				+ activityId.print());
		threads.add(Thread.currentThread());
	}

	/**
	 * Disassociates the calling thread from this activity.
	 */
	public void disassociateCurrentThread() {
		log.debug(
			"Disassociating thread "
				+ Thread.currentThread().getName()
				+ " from activity "
				+ activityId.print());
		threads.remove(Thread.currentThread());
		Thread.interrupted();
	}

	/**
	 *  Return the string representation of the activity status.
	 *
	 * @param status the integer status to convert.
	 * @return the string representation of the status.  
	 */
	public String getStringStatus(int status) {

		switch (status) {
			case Status.StatusActive :
				return "STATUS_ACTIVE";
			case Status.StatusCompleted :
				return "STATUS_COMPLETED";
			case Status.StatusCompleting :
				return "STATUS_COMPLETING";
			case Status.StatusCompletingHeuristic :
				return "STATUS_COMPLETING_HEURISTIC";
			case Status.StatusError :
				return "STATUS_ERROR";
			case Status.StatusNoActivity :
				return "STATUS_NO_ACTIVITY";
			case Status.StatusUnknown :
				return "STATUS_UNKNOWN";

			default :
				return "STATUS_UNKNOWN(" + status + ")";
		}

	}

	/**
	 * Return the string representation of the activity CompletionStatus.
	 * 
	 * @param completionStatus the integer completion status to convert.
	 * @return the string representation of the completion status.
	 */	
	public String getStringCompletionStatus(int completionStatus) {

		switch (completionStatus) {
			case CompletionStatus.CompletionStatusFail :
				return "COMPLETIONSTATUS_FAIL";
			case CompletionStatus.CompletionStatusFailOnly :
				return "COMPLETIONSTATUS_FAIL_ONLY";
			case CompletionStatus.CompletionStatusSuccess :
				return "COMPLETIONSTATUS_SUCCESS";

			default :
				return "COMPLETIONSTATUS_UNKNOWN(" + completionStatus + ")";
		}

	}

	// My Protected ---------------------------------------------------------------

	/**
	 * Protected method to complete the activity.
	 */
	protected Outcome completeActivity()
		throws
			ActivityPendingException,
			ContextPendingException,
			NotOriginatorException,
			InvalidStateException,
			ActivityNotProcessedException,
			SystemException {

		SignalSet processingSS = null;
		Outcome out = null;
		Signal signal = null;
		boolean nextSignal;
		boolean isRegisteredCSS = true;
		int i = 0;

		log.info(
			Thread.currentThread().getName()
				+ " COMPLETING ACTIVITY "
				+ activityId.print()
				+ " ...");

		if ((completionStatus == CompletionStatus.CompletionStatusSuccess)
			&& (childs.size() > 0))
			throw new ContextPendingException();
		if (threads.size() > 1)
			throw new ActivityPendingException();

		if (timedOut)
			completionStatus = CompletionStatus.CompletionStatusFail;

		if (timeout != null)
			timeout.cancel();

		log.info(
			"\tCompletionStatus = " + getStringCompletionStatus(completionStatus));

		// We set the status to say that we are completing
		status = Status.StatusCompleting;

		// It will contain the actions added to a specific SignalSet
		Action[] actions = null;

		if (completionSSName == null)
			// It may be not have been set yet by the HLS
			completionSSName = manager.getService().getCompletionSignalSetName();

		try {
			// The Completion SignalSet may or not have been registered previously
			if (signalSets.containsKey(completionSSName)) {
				processingSS =
					((SignalSetInfo) signalSets.get(completionSSName))
						.getSignalSet();
				log.debug(
					activityId.print() + completionSSName + " already registered");
			} else {
				// It may throw SignalSetUnknownException
				processingSS = manager.getService().getSignalSet(completionSSName);
				processingSS.setActivityCoordinator(this);
				isRegisteredCSS = false;
				log.debug(
					activityId.print() + completionSSName + " not registered");
			}

			log.info("\tProcessing Completion SignalSet -> " + completionSSName);
			log.debug(
				activityId.print()
					+ " is sending "
					+ getStringCompletionStatus(completionStatus)
					+ " CompletionStatus and "
					+ getStringStatus(status)
					+ " Status to the SignalSet");
			processingSS.setCompletionStatus(completionStatus, status);

			// If CompletionSS has been registered previously, we get its
			// actions. Else their actions will be null.
			if (isRegisteredCSS == true)
				actions =
					((SignalSetInfo) signalSets.get(completionSSName)).getActions();

			if (actions != null) {
				while ((signal = processingSS.getSignal()) != null) {
					i = 0;
					nextSignal = false;

					log.info(
						"\tSending "
							+ signal.getName()
							+ " signal to registered actions ("
							+ actions.length
							+ ")");
					while ((i < actions.length) && (nextSignal == false)) {
						if (actions[i] != null) {
							log.debug(activityId.print() + " Send number: " + i);
							out = actions[i].processSignal(signal);
							// We obtain and analize the coordination information
							CoordinationInformation info =
								processingSS.setResponse(out);

							if ((info != null)
								&& (info.isActionInterested() == false)) {
								// WARNING: We must remove the action in order
								// to not process it in other processings
								// while this activity still is alive...
								removeAction(actions[i], completionSSName);
								// ...and remove the action from array for the
								// current processing...
								actions[i] = null;
							}

							if ((info != null) && (info.useNextSignal() == true)) {
								nextSignal = true;
								break; // We take other signal
							}
						}
						i = i + 1;
					}
				}
				// WARNING!!! We can't modify a CompletionStatusFailOnly completionStatus
				if (completionStatus != CompletionStatus.CompletionStatusFailOnly)
					completionStatus = processingSS.getCompletionStatus();
				else
					//markChildActivitiesWithFail();
					throw new InvalidStateException();
			} else
				log.debug("\tNO actions to process");

			//	Finally, we obtain the SignalSet outcome and CompletionStatus
			out = processingSS.getOutcome();
			processingSS.destroy();
			log.info("\tCompletion SignalSet processing completed.");
			log.info(
				"\tFinal CompletionStatus = "
					+ getStringCompletionStatus(completionStatus));

			status = Status.StatusCompleted;			
		} catch (SignalSetUnknownException e) {
			status = Status.StatusError;
			throw new ActivityNotProcessedException();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// We finish the instance
			log.info(
				Thread.currentThread().getName()
					+ " ACTIVITY "
					+ activityId.print()
					+ " COMPLETED!!!");
			instanceDone();
		}
		return out;
	}

	/**
	 * Public method to set the completionStatus of this activity
	 */
	protected void setCompletionStatus(int completionStatus)
		throws InvalidStateException {

		if (completionStatus != CompletionStatus.CompletionStatusFailOnly)
			if (this.completionStatus
				== CompletionStatus.CompletionStatusFailOnly)
				throw new InvalidStateException();
		this.completionStatus = completionStatus;
	}

	/**
	 *  Public method to get the completionStatus of this activity
	 */
	protected int getCompletionStatus() {
		return completionStatus;
	}

	/**
	 * Adds a child to the children list.
	 * @param child - The new child.
	 */
	protected void addChild(ActivityImpl child) {
		childs.add(child);
	}

	/**
	 * Removes a child from the list of children.
	 * @param child - The child to remove.
	 */
	protected void removeChild(ActivityImpl child) {
		childs.remove(child);
	}

	/**
	 * Returns an array of activity children.
	 * @return - The array with all the children of this activity.
	 */
	protected ActivityImpl[] getChilds() {
		ActivityImpl[] array =
			(ActivityImpl[]) childs.toArray(new ActivityImpl[childs.size()]);
		return array;
	}

	// My Private -----------------------------------------------------------------

	/**
	 * Mark the child activities with fail. This is done when the
	 * timeout expires.
	 */
	private void markChildActivitiesWithFail() {

		ActivityImpl child = null;

		if (childs != null) {
			try {
				log.debug(
					"Marking with CompletionStatusFailOnly childs... of "
						+ getName());
				Iterator it = childs.iterator();
				while (it.hasNext()) {
					child = (ActivityImpl) it.next();
					child.setCompletionStatus(
						CompletionStatus.CompletionStatusFailOnly);
					log.debug("Child " + child.getName() + " marked!!!");
					child.markChildActivitiesWithFail();
				}
			} catch (SystemException e) {
				e.printStackTrace();
			} catch (InvalidStateException e) {
				log.debug(
					"Child "
						+ child
						+ "is already marked as CompletionStatusFailOnly ");
			}
		}
	}

	/**
	 *  Lock this instance.
	 */
	private synchronized void lock() {

		if (done)
			throw new IllegalStateException("Activity has terminated");

		if (locked) {
			log.warn("Lock contention, activity=" + toString());
			//DEBUG Thread.currentThread().dumpStack();
			while (locked) {
				try {
					// Wakeup happens when:
					// - notify() is called from unlock()
					// - notifyAll is called from instanceDone()
					wait();
				} catch (InterruptedException ex) {
					// ignore
				}

				if (done)
					throw new IllegalStateException("Activity has now terminated");
			}
		}
		locked = true;
	}

	/**
	 *  Unlock this instance.
	 */
	private synchronized void unlock() {

		if (!locked) {
			log.warn(
				"Unlocking, but not locked, activity=" + toString(),
				new Throwable("[Stack trace]"));
		}
		locked = false;
		notify();
	}

	/**
	 * Mark this activity as non-existing
	 */
	private synchronized void instanceDone() {

		// If activity is nested, remove the parent association
		if (superior != null)
			superior.removeChild(this);
		// Release this activity from the table for GC
		manager.releaseActivityImpl(this);
		// Set the status to StatusNoActivity
		status = Status.StatusNoActivity;
		// Notify all threads waiting for the lock
		notifyAll();
		// This instance is finished
		done = true;
	}

	// Inner classes --------------------------------------------------------------

}