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

jadex.bpmn.runtime.ProcessThread Maven / Gradle / Ivy

Go to download

The Jadex BPMN kernel provides a workflow kernel for the standardized business process modeling notation. The kernel relies on annotated BPMN diagrams, which include detailed execution information.

There is a newer version: 4.0.267
Show newest version
package jadex.bpmn.runtime;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import jadex.bpmn.features.IBpmnComponentFeature;
import jadex.bpmn.features.IInternalBpmnComponentFeature;
import jadex.bpmn.model.MActivity;
import jadex.bpmn.model.MBpmnModel;
import jadex.bpmn.model.MDataEdge;
import jadex.bpmn.model.MLane;
import jadex.bpmn.model.MParameter;
import jadex.bpmn.model.MPool;
import jadex.bpmn.model.MSequenceEdge;
import jadex.bpmn.model.MSubProcess;
import jadex.bpmn.model.MTask;
import jadex.bpmn.model.task.ITask;
import jadex.bpmn.model.task.ITaskContext;
import jadex.bpmn.runtime.handler.ICancelable;
import jadex.bpmn.runtime.handler.SplitInfo;
import jadex.bpmn.runtime.handler.SubProcessActivityHandler.SubprocessResultHandler;
import jadex.bridge.IInternalAccess;
import jadex.bridge.component.IArgumentsResultsFeature;
import jadex.bridge.component.IExecutionFeature;
import jadex.bridge.component.IMonitoringComponentFeature;
import jadex.bridge.modelinfo.UnparsedExpression;
import jadex.bridge.service.types.monitoring.IMonitoringEvent;
import jadex.bridge.service.types.monitoring.IMonitoringService.PublishEventLevel;
import jadex.bridge.service.types.monitoring.IMonitoringService.PublishTarget;
import jadex.commons.IFilter;
import jadex.commons.IResultCommand;
import jadex.commons.IValueFetcher;
import jadex.commons.SReflect;
import jadex.commons.SUtil;
import jadex.commons.Tuple2;
import jadex.commons.collection.IndexMap;
import jadex.commons.future.IResultListener;
import jadex.commons.transformation.BasicTypeConverter;
import jadex.commons.transformation.IObjectStringConverter;
import jadex.commons.transformation.IStringObjectConverter;
import jadex.javaparser.IParsedExpression;
import jadex.javaparser.SimpleValueFetcher;

/**
 *  Representation of a single control flow in a BPMN process instance,
 *  i.e. an instance of a sequence flow.
 */
public class ProcessThread	implements ITaskContext
{
	//-------- attributes --------
	
	/** The thread id. */
	protected String id;
	
	/** The next activity. */
	protected MActivity	activity;
	
	/** The last edge (if any). */
	protected MSequenceEdge	edge;
		
	/** The data of the current or last activity. */
	protected Map data;
	
	/** The data of the current data edges. */
	protected Map dataedges;
	
	/** The parent process thread. */
	protected ProcessThread parent;
	
	/** The subthreads. */
	protected List subthreads;
	
	/** The Bpmn instance. */
	protected IInternalAccess instance;
	
	/** The exception that has just occurred in the process (if any). */
	protected Exception	exception;
	
	/** Is the process in a waiting state. */
	protected boolean waiting;
	
	/** The wait info. */
	protected ICancelable cancelinfo;
	
	/** The wait filter. */
	protected IFilter waitfilter;
	
	/** The current task. */
	protected ITask task;
	
	/** Is the task canceled. */
	protected boolean canceled;
	
	/** The id counter for sub processes. */
	public int idcnt;
	
	/** The split infos. */
	public Map splitinfos;
	
	/** The loop command. */
	protected IResultCommand loopcmd;
	
	/** The subprocess intermediate result received command. */
	protected SubprocessResultHandler resulthandler;
	
	//-------- constructors --------

	/**
	 *  Create a new process instance.
	 *  @param activity	The current activity.
	 */
	public ProcessThread(MActivity activity, ProcessThread parent, IInternalAccess instance)
	{
		this(activity, parent, instance, false);
	}
	
	/**
	 *  Create a new process instance.
	 *  @param activity	The current activity.
	 */
	public ProcessThread(MActivity activity, ProcessThread parent, IInternalAccess instance, boolean passive)
	{
		this.id	= parent!=null? parent.getNextChildId(): null;
		this.parent = parent;
		this.instance = instance;
		
		if(passive)
		{
			this.activity	= activity;
		}
		else
		{
			setActivity(activity);
		}			
	}
	
	//-------- methods --------
	
	/**
	 *  Get the model.
	 *  @return	The bpmn model.
	 */
	public MBpmnModel getBpmnModel()
	{
		return (MBpmnModel)instance.getModel().getRawModel();
	}
	
	/**
	 *  Get the id.
	 *  @return The id.
	 */
	public String getId()
	{
		return this.id;
	}	
	
	/**
	 *  Get the activity.
	 *  @return The activity.
	 */
	public MActivity getActivity()
	{
		return this.activity;
	}

	/**
	 *  Set the next activity. Sets the last edge to null.
	 *  Should only be used, when no edge available (e.g. start events or event handlers of subprocesses).
	 */
	public void	setActivity(MActivity activity)
	{
		this.activity	= activity;
		
		// Clear edge and resulthandler on each transition
		this.edge	= null;
		this.resulthandler = null;
		
		if(activity!=null)
			scheduleExecution();
//		else
//			System.out.println("activity to null: "+getId());
		
		if(getInstance().getComponentFeature0(IMonitoringComponentFeature.class)!=null && getInstance().getComponentFeature(IMonitoringComponentFeature.class).hasEventTargets(PublishTarget.TOALL, PublishEventLevel.FINE))
			getInstance().getComponentFeature(IMonitoringComponentFeature.class).publishEvent(getBpmnFeature(getInstance()).createThreadEvent(IMonitoringEvent.EVENT_TYPE_MODIFICATION, this), PublishTarget.TOALL);
	}
	
	/**
	 *  Is the current task canceled?
	 *  @return The canceled flag.
	 */
	public boolean isCanceled()
	{
		return canceled;
	}
	
	/**
	 *  Set the canceled state.
	 *  @param canceled True, if canceled.
	 */
	public void setCanceled(boolean canceled)
	{
		this.canceled = canceled;
	}
	
	/**
	 *  Gets the current task.
	 *  @return The current task.
	 */
	public ITask getTask()
	{
		return task;
	}
	
	/**
	 *  Sets the current task.
	 *  @param task The current task.
	 */
	public void setTask(ITask task)
	{
		this.task = task;
	}
	
	/**
	 *  Get the last edge (if any).
	 *  @return The edge.
	 */
	public MSequenceEdge getLastEdge()
	{
		return this.edge;
	}

	/**
	 *  Set the last edge. Also sets the next activity.
	 *  @param edge	The edge.
	 */
	public void setLastEdge(MSequenceEdge edge)
	{
		setActivity(edge!=null ? (MActivity)edge.getTarget() : null);
		this.edge	= edge;
	}
	
	/**
	 *  Is the process in a waiting state (i.e. blocked)? 
	 *  @return The waiting flag.
	 */
	public boolean	isWaiting()
	{
//		return this.waiting!=null;
		return waiting;
	}
	
	/**
	 *  Set the waiting state.
	 */
	public void setWaiting(boolean waiting)
	{
//		System.out.println("Set waiting thread: "+getId()+" "+waiting);
		this.waiting = waiting;
		if(getInstance().getComponentFeature0(IMonitoringComponentFeature.class)!=null && getInstance().getComponentFeature(IMonitoringComponentFeature.class).hasEventTargets(PublishTarget.TOALL, PublishEventLevel.FINE))
			getInstance().getComponentFeature(IMonitoringComponentFeature.class).publishEvent(getBpmnFeature(getInstance()).createThreadEvent(IMonitoringEvent.EVENT_TYPE_MODIFICATION, this), PublishTarget.TOALL);
	}
	
	/**
	 *  Set to non waiting.
	 */
	public void	setNonWaiting()
	{
//		boolean waswaiting = waiting;
		
		// Canel waiting elements like a timer
		if(cancelinfo!=null)
			cancelinfo.cancel();
		
		this.waiting = false;
//		this.waitinfo = null;
		this.cancelinfo = null;
		this.waitfilter = null;
		if(getInstance().getComponentFeature0(IMonitoringComponentFeature.class)!=null && getInstance().getComponentFeature(IMonitoringComponentFeature.class).hasEventTargets(PublishTarget.TOALL, PublishEventLevel.FINE))
			getInstance().getComponentFeature(IMonitoringComponentFeature.class).publishEvent(getBpmnFeature(getInstance()).createThreadEvent(IMonitoringEvent.EVENT_TYPE_MODIFICATION, this), PublishTarget.TOALL);

//		if(waswaiting)
//			scheduleExecution();
		
//		System.out.println("Thread: "+ComponentIdentifier.LOCAL.get()+", "+getId()+" "+waiting);
	}
	
	/**
	 *  Schedule notification of this thread.
	 */
	protected void scheduleExecution()
	{
		getInstance().getComponentFeature(IExecutionFeature.class).scheduleStep(new ExecuteProcessThread(this)).addResultListener(new IResultListener()
		{
			public void resultAvailable(Void result)
			{
			}
			
			public void exceptionOccurred(Exception exception)
			{
				// nop, can happen when step is invalid
				// todo: use StepInvalidException?!
			}
		});
	}
	
	/**
	 *  Get the waiting type. 
	 *  @return The waiting type.
	 * /
	public String getWaitingState()
	{
		return this.waiting;
	}*/

	/**
	 *  Set the process waiting state (i.e. blocked). 
	 *  @param waiting	The waiting flag.
	 * /
	public void setWaitingState(String waiting)
	{
		this.waiting = waiting;
//		System.out.println("Thread: "+this+" "+waiting);
	}*/
	
	// todo: refactor/simplify the waiting stuff.
	
	/**
	 *  Set the process waiting info. 
	 *  @param waiting	The waiting info.
	 */
	public void setWaitInfo(ICancelable cancelinfo)
	{
//		this.waitinfo = waitinfo;
		this.cancelinfo = cancelinfo;
//		System.out.println("Thread waitinfo: "+ComponentIdentifier.LOCAL.get()+", "+getId()+" "+waitinfo);
	}
	
	/**
	 *  Get the waitinfo.
	 *  @return The waitinfo.
	 */
	public ICancelable getWaitInfo()
	{
		return this.cancelinfo;
	}
	
	/**
	 *  Get the wait filter.
	 *  @return The waitfilter.
	 */
	public IFilter getWaitFilter()
	{
		return this.waitfilter;
	}

	/**
	 *  Set the wait filter.
	 *  @param waitfilter The waitfilter to set.
	 */
	public void setWaitFilter(IFilter waitfilter)
	{
		this.waitfilter = waitfilter;
	}
	
//	/**
//	 *  Get the thread context
//	 *  @return	The thread context.
//	 */
//	public ThreadContext getThreadContext()
//	{
//		return context;
//	}
//	
//	/**
//	 *  Set the context.
//	 *  @param context The context to set.
//	 */
//	public void setThreadContext(ThreadContext context)
//	{
//		this.context = context;
//	}

	/**
	 *  Create a copy of this thread (e.g. for a parallel split).
	 */
	public ProcessThread createCopy()
	{
		ProcessThread	ret	= new ProcessThread(activity, parent, instance);
		ret.edge	= edge;
		ret.data	= data!=null? new HashMap(data): null;
		ret.dataedges	= dataedges!=null? new HashMap(dataedges): null;
		ret.splitinfos	= splitinfos!=null ? new LinkedHashMap(splitinfos) : null;
		return ret;
	}
	
	/**
	 *  Create a copy of this thread (e.g. for a parallel split).
	 */
	public void copy(ProcessThread ret)
	{
		ret.edge	= edge;
		ret.data	= data!=null? new HashMap(data): null;
		ret.dataedges	= dataedges!=null? new HashMap(dataedges): null;
		ret.splitinfos	= splitinfos!=null ? new LinkedHashMap(splitinfos) : null;
	}
	
	/**
	 *  Test if a parameter has been set on activity.
	 *  @param name	The parameter name. 
	 *  @return	True if parameter is known.
	 */
	public boolean hasOwnParameterValue(String name)
	{
		return data!=null && data.containsKey(name);
	}
	
	//-------- ITaskContext --------
	
	/**
	 *  Test if a parameter has been set on activity.
	 *  @param name	The parameter name. 
	 *  @return	True if parameter is known.
	 */
	public boolean hasParameterValue(String name)
	{
		return hasOwnParameterValue(name) || getParent()!=null 
			&& getParent().hasParameterValue(name);
	}

	/**
	 *  Get the model element.
	 *  @return	The model of the task.
	 */
	public MActivity getModelElement()
	{
		return activity;
	}

	/**
	 *  Get the value of a parameter.
	 *  @param name	The parameter name. 
	 *  @return	The parameter value. 
	 */
	public Object getParameterValue(String name)
	{
		return hasOwnParameterValue(name)? data.get(name): 
			getParent()!=null? getParent().getParameterValue(name): null;
	}
	
	/**
	 *  Get the parameters.
	 *  @return The parameters.
	 */
	public Map getParameters()
	{
		return data;
	}
	
	/**
	 *  Set the value of a parameter.
	 *  @param name	The parameter name. 
	 *  @param value	The parameter value. 
	 */
	public void	setDataEdgeValue(String name, Object value)
	{
		if(dataedges == null)
		{
			dataedges = new HashMap();
		}
		dataedges.put(name, value);
	}

	/**
	 *  Set the value of a parameter.
	 *  @param name	The parameter name. 
	 *  @param value	The parameter value. 
	 */
	public void	setParameterValue(String name, Object value)
	{
		setParameterValue(name, null, value);
	}
	
	/**
	 *  Set the value of a parameter.
	 *  @param name	The parameter name. 
	 *  @param value	The parameter value. 
	 */
	public void	setParameterValue(String name, Object key, Object value)
	{
		internalSetParameterValue(name, key, value, this);
	}

	/**
	 *  Set the value of a parameter.
	 *  @param name	The parameter name. 
	 *  @param value	The parameter value. 
	 */
	protected void	internalSetParameterValue(String name, Object key, Object value, ProcessThread start)
	{
		if(getActivity()!=null && getActivity().hasParameter(name))
		{
			// Local parameter
			setOrCreateParameterValue(name, key, value);
		}
		else if(getParent()!=null)
		{
			// Try local parameter in parent
			getParent().internalSetParameterValue(name, key, value, start);
		}
//		else if(getParent()==null && instance.getModelElement().getContextVariable(name)!=null)
		else if(getParent()==null && ((MBpmnModel)getInstance().getModel().getRawModel()).getContextVariable(name)!=null)
		{
			// Global parameter
			setOrCreateParameterValue(name, key, value);			
		}
		else
		{
			throw new RuntimeException("No such parameter: "+name+", "+start);
		}
	}

	/**
	 *  Set or create a parameter value directly in this thread.
	 */
	public void setOrCreateParameterValue(String name, Object value)
	{
		setOrCreateParameterValue(name, null, value);
	}
	
	/**
	 *  Set or create a parameter value directly in this thread.
	 */
	public void setOrCreateParameterValue(String name, Object key, Object value)
	{
		if(data==null)
			data = new HashMap();
		
		if(key==null)
		{
			data.put(name, value);	
		}
		else
		{
			Object coll = data.get(name);
			if(coll instanceof List)
			{
				int index = ((Number)key).intValue();
				if(index>=0)
					((List)coll).set(index, value);
				else
					((List)coll).add(value);
			}
			else if(coll!=null && coll.getClass().isArray())
			{
				int index = ((Number)key).intValue();
				Array.set(coll, index, value);
			}
			else if(coll instanceof Map)
			{
				((Map)coll).put(key, value);
			}
			else if(coll instanceof Set)
			{
				((Set)coll).add(value);
			}
//			else
//			{
//				throw new RuntimeException("Unsupported collection type: "+coll);
//			}
		}
		
		if(getActivity() instanceof MSubProcess)
		{
			if(getActivity().hasParameter(name) && getActivity().getParameter(name).isOut())
			{
//				System.out.println("setting subprocess out parameter: "+name+" "+value);
				// Hack?! should this be called directly?
				if(resulthandler==null)
					resulthandler = new SubprocessResultHandler(this, activity);
				resulthandler.handleProcessResult(name, key, value);
//				SubProcessActivityHandler.handleProcessResult(instance, this, activity, name, key, value);
			}
		}
	}
	
	/**
	 *  Remove the value of a parameter.
	 *  @param name	The parameter name. 
	 */
	public void	removeParameterValue(String name)
	{
		assert activity!=null;
		if(data!=null)
		{
			data.remove(name);
		}
	}
	
	/**
	 *  Get the name of all parameters.
	 *  @return The parameter names.
	 */
	public String[] getParameterNames()
	{
		return data!=null? (String[])data.keySet().toArray(new String[data.size()]): SUtil.EMPTY_STRING_ARRAY;
	}
	
	/**
	 *  Get the name of all parameters.
	 *  @return The parameter names.
	 */
	public Set getAllParameterNames()
	{
		Set ret = new HashSet();
		ProcessThread pt = this;
		while(pt!=null)
		{
			String[] names = getParameterNames();
			for(String name: names)
			{
				ret.add(name);
			}
			pt = pt.getParent();
		}
		return ret;
	}
	
	/**
	 *  Get the value of a property.
	 *  @param name	The property name. 
	 *  @return	The property value. 
	 */
	public Object getPropertyValue(String name)
	{
		return getPropertyValue(name, activity);
	}
	
	/**
	 *  Hack: method is necessary because thread.activity is not always 
	 *  the activity to execute in case of multiple event.
	 *  Get the value of a property.
	 *  @param name	The property name. 
	 *  @return	The property value. 
	 */
	public Object getPropertyValue(String name, MActivity activity)
	{
		assert activity!=null;
		UnparsedExpression	upex	= activity.getPropertyValue(name);
		try
		{
			return upex!=null ? ((IParsedExpression)upex.getParsed()).getValue(new ProcessThreadValueFetcher(this, true, instance.getFetcher())) : null;
		}
		catch(RuntimeException e)
		{
			throw new RuntimeException("Error parsing property: "+instance+", "+this+", "+name+", "+upex, e);
		}
	}
	
	/**
	 *  Test, if a property is declared.
	 *  @param name	The property name.
	 *  @return True, if the property is declared.
	 */
	public boolean	hasPropertyValue(String name)
	{
		return activity.hasPropertyValue(name);
	}
	
//	/**
//	 *  Gets the hard constraints.
//	 *  @return The hard constraints.
//	 */
//	public RHardConstraints getHardConstraints()
//	{
//		return context.getHardConstraints();
//	}
	
//	/**
//	 *  Check if the value of a result is set.
//	 *  @param name	The result name. 
//	 *  @return	True, if the result is set to some value. 
//	 */
//	public boolean hasResultValue(String name)
//	{
//		return instance.hasResultValue(name);
//	}
//
//	/**
//	 *  Get the value of a result.
//	 *  @param name	The result name. 
//	 *  @return	The result value. 
//	 */
//	public Object getResultValue(String name)
//	{
//		return instance.getResultValue(name);
//	}
//	
//	/**
//	 *  Set the value of a result.
//	 *  @param name	The result name. 
//	 *  @param value The result value. 
//	 */
//	public void	setResultValue(String name, Object value)
//	{
//		instance.setResultValue(name, value);
//	}
	
	/**
	 *  Get the exception (if any).
	 *  @return	The exception (if any).
	 */
	public Exception	getException()
	{
		return exception;
	}

	/**
	 *  Set the exception.
	 *  @param exception	The exception.
	 */
	public void	setException(Exception exception)
	{
		this.exception	= exception;
	}
	
	/**
	 *  Get the instance.
	 *  @return The instance.
	 */
	public IInternalAccess getInstance()
	{
		return this.instance;
	}
	
	/**
	 *  Get the data edges.
	 *  @return The data edges.
	 */
	public Map getDataEdges()
	{
		return this.dataedges;
	}
	
	/**
	 *  Get the data.
	 *  @return The data.
	 */
	public Map getData()
	{
		return this.data;
	}

	/**
	 *  Test if the thread belongs to the given pool and/or lane.
	 *  @param pool	The pool to be executed or null for any.
	 *  @param lane	The lane to be executed or null for any. Nested lanes may be addressed by dot-notation, e.g. 'OuterLane.InnerLane'.
	 *  @return True, when the thread belongs to the given pool and/or lane. Also returns true when both pool and lane are null.
	 */
	public boolean belongsTo(String pool, String lane)
	{
		// Pool null for external steps.
		MPool po = getActivity().getPool();
		assert po!=null: getActivity();
		boolean	ret	= pool==null || pool.equals(po.getName());
		
		// Test lane
		if(ret && lane!=null)
		{
			List	lanes	= new ArrayList();
			MLane	mlane	= getActivity().getLane();
			while(mlane!=null)
			{
				lanes.add(mlane);
				mlane	= mlane.getLane();
			}
			
			StringTokenizer	stok	= new StringTokenizer(lane, ".");
			while(ret && stok.hasMoreTokens())
			{
				ret	= !lanes.isEmpty() && ((MLane)lanes.remove(lanes.size()-1)).getName().equals(stok.nextToken());
			}
		}
		
		return ret;
	}

	/**
	 *  Update parameters based on edge inscriptions and initial values.
	 *  @param instance	The calling BPMN instance.
	 */
	public  void updateParametersBeforeStep(IInternalAccess instance)
	{
//		System.out.println("before: "+getActivity());

		if(getActivity().getActivityType()!=null && getActivity().getActivityType().indexOf("Event")!=-1)
		{
			// Just pass on data edge params in case of event
			Map	passedparams = getDataEdgeValues();
			IndexMap params = getActivity().getParameters();
			if(params!=null && passedparams!=null)
			{
				for(Iterator it=params.values().iterator(); it.hasNext(); )
				{
					MParameter param = (MParameter)it.next();
					if(passedparams.containsKey(param.getName()))
					{
						setParameterValue(param.getName(), passedparams.get(param.getName()));
					}
				}
			}
		}
//		else if(MBpmnModel.TASK.equals(getActivity().getActivityType())
//			|| getActivity() instanceof MSubProcess)
		else if(getActivity() instanceof MTask
				|| getActivity() instanceof MSubProcess)
		{
			// Handle parameter passing in edge inscriptions.
			Map	passedparams = null;
			Set	indexparams	= null;
			
			if(getLastEdge()!=null)
			{
				if(getLastEdge().getParameterMappings()!=null)
				{
					IndexMap> mappings = getLastEdge().getParameterMappings();
					if(mappings!=null)
					{
						IValueFetcher fetcher = new ProcessThreadValueFetcher(this, false, instance.getFetcher());
						for(Iterator it=mappings.keySet().iterator(); it.hasNext(); )
						{
							boolean	found	= false;
							String	name	= (String)it.next();
	//						IParsedExpression exp = (IParsedExpression)((Object[])mappings.get(name))[0];
	//						IParsedExpression iexp = (IParsedExpression)((Object[])mappings.get(name))[1];
							IParsedExpression exp = (IParsedExpression)((Tuple2)mappings.get(name)).getFirstEntity().getParsed();
							UnparsedExpression uiexp = ((Tuple2)mappings.get(name)).getSecondEntity();
							IParsedExpression iexp = uiexp != null? (IParsedExpression)uiexp.getParsed(): null;
							Object value;
							Object index;
							try
							{
								value	= exp.getValue(fetcher);
							}
							catch(RuntimeException e)
							{
								throw new RuntimeException("Error parsing parameter value: "+instance+", "+this+", "+name+", "+exp, e);
							}
							try
							{
								index	= iexp!=null ? iexp.getValue(fetcher) : null;
							}
							catch(RuntimeException e)
							{
								throw new RuntimeException("Error parsing parameter index: "+instance+", "+this+", "+name+", "+iexp, e);
							}
									
							// if activity has parameter with name save it in passedparams
							if(getActivity().hasParameter(name))
							{
								if(passedparams==null)
									passedparams	= new HashMap();
								
								// If indexed param, create name, list and add (index, value) entry
								if(iexp!=null)
								{
									if(!passedparams.containsKey(name))
									{
										passedparams.put(name, new ArrayList());
										if(indexparams==null)
											indexparams	= new HashSet();
										
										indexparams.add(name);
									}
									((List)passedparams.get(name)).add(new Object[]{index, value});
								}
								// else save name, value
								else
								{
									passedparams.put(name, value);
								}
								found	= true;
							}
							
							// else if process thread has parameter, set it in thread 
							if(!found && hasParameterValue(name))
							{
								setParameterValue(name, index, value);
								found	= true;
							}
							
							// else if bpmn instance has context variable
							if(!found && getBpmnFeature(instance).hasContextVariable(name))
							{
								if(iexp!=null)
								{
									Object	array	= getBpmnFeature(instance).getContextVariable(name);
									Array.set(array, ((Number)index).intValue(), value);
								}
								else
								{
									getBpmnFeature(instance).setContextVariable(name, value);
								}
								found	= true;
							}
							else if(!found)
							{
								throw new RuntimeException("Unknown parameter or context variable: "+name+", "+this);
							}
						}
					}
				}
				else 
				{
					// Try to find data edges
					passedparams = getDataEdgeValues();
					
//					List ds = getActivity().getIncomingDataEdges();
//					if(ds!=null)
//					{
//						passedparams = new HashMap();
//						
//						for(MDataEdge de: ds)
//						{
//							if(dataedges!=null)
//							{
//								if(dataedges.containsKey(de.getId()))
//								{
//									String pname = de.getTargetParameter();
//									// Value is consumed -> remove?!
//									Object val = dataedges.remove(de.getId());
//								
//									// if already contains value must be identical
//									if(passedparams.containsKey(pname) && !SUtil.equals(passedparams.get(pname), val))
//										throw new RuntimeException("Different edges have different values");
//								
//									passedparams.put(pname, val);
//								}
//								else if (de.getSource() == null)
//								{
//									// Argument data edge
//									passedparams.put(de.getTargetParameter(), instance.getArguments().get(de.getSourceParameter()));
//								}
//								else
//								{
//									String pname = de.getTargetParameter();
//									if (getActivity().getParameters() == null ||
//										getActivity().getParameters().get(pname) == null ||
//										getActivity().getParameters().get(pname).getInitialValueString() == null ||
//										getActivity().getParameters().get(pname).getInitialValueString().length()==0)
//									{
//										throw new RuntimeException("Could not find data edge value for: "+de.getId());
//									}
//								}
//							}
//						}
//					}
				}
			}
			
			// todo: parameter direction / class
			
			Set before = data!=null? new HashSet(data.keySet()): Collections.EMPTY_SET;
			before.remove(ProcessServiceInvocationHandler.THREAD_PARAMETER_SERVICE_RESULT);	// Hack!!! Keep future available locally for thread.
			IValueFetcher fetcher = new ProcessThreadValueFetcher(this, true, instance.getFetcher());
			IndexMap params = getActivity().getParameters();
			if(params!=null)
			{
				Set initialized = new HashSet();
				for(Iterator it=params.values().iterator(); it.hasNext(); )
				{
					MParameter param = (MParameter)it.next();
					
					if(passedparams!=null && passedparams.containsKey(param.getName()))
					{
						if(indexparams!=null && indexparams.contains(param.getName()))
						{
							Object	array	= getParameterValue(param.getName());
							List	values	= (List)passedparams.get(param.getName());
							for(int i=0; i it=params.values().iterator(); it.hasNext(); )
				{
					MParameter param = it.next();
					if(!initialized.contains(param.getName()))
					{
						try
						{
							setParameterValue(param.getName(), param.getInitialValue()==null? null: param.getInitialValue().getParsed() == null? null: ((IParsedExpression) param.getInitialValue().getParsed()).getValue(fetcher));
							before.remove(param.getName());
						}
						catch(RuntimeException e)
						{
							throw new RuntimeException("Error parsing parameter value: "+instance+", "+this+", "+param.getName()+", "+param.getInitialValue(), e);
						}
					}
				}
			}
			
			// Remove old data (all values that have not been renewed).
			for(Iterator it=before.iterator(); it.hasNext(); )
			{
				String name = it.next();
//				System.out.println("removing data: "+name);
				data.remove(name);
			}
		}
	}
	
	/**
	 * 
	 */
	protected Map getDataEdgeValues()
	{
		Map passedparams = null;
		
		// Try to find data edges
		List ds = getActivity().getIncomingDataEdges();
		if(ds!=null && dataedges!=null)
		{
			passedparams	= new HashMap();
			Set	missedparams	= new HashSet();	// Parameters with in edge where no value is available (yet), e.g., tasks after xor join may need value from at least one of many edges.
			
			for(MDataEdge de: ds)
			{
				if(!passedparams.containsKey(de.getTargetParameter()) && dataedges.containsKey(de.getId()))
				{
					String pname = de.getTargetParameter();
					// Value is consumed -> remove?!
					Object val = dataedges.remove(de.getId());
				
					// if already contains value must be identical
					if(passedparams.containsKey(pname) && !SUtil.equals(passedparams.get(pname), val))
						throw new RuntimeException("Different edges have different values");
				
					passedparams.put(pname, val);
					missedparams.remove(de.getTargetParameter());
				}
				else if (de.getSource() == null)
				{
					// Argument data edge
					passedparams.put(de.getTargetParameter(), instance.getComponentFeature(IArgumentsResultsFeature.class).getArguments().get(de.getSourceParameter()));
					missedparams.remove(de.getTargetParameter());
				}
				else
				{
					String pname = de.getTargetParameter();
					if (getActivity().getParameters() == null ||
						getActivity().getParameters().get(pname) == null ||
						getActivity().getParameters().get(pname).getInitialValueString() == null ||
						getActivity().getParameters().get(pname).getInitialValueString().length()==0)
					{
						missedparams.add(pname);
					}
				}
			}
			
			if(!missedparams.isEmpty())
			{
				instance.getLogger().warning("Could not find data edge value for: "+missedparams);
			}
		}
		
		return passedparams;
	}
	
	/**
	 *  Remove in parameters after step.
	 *  @param instance	The calling BPMN instance.
	 */
	public  void updateParametersAfterStep(MActivity activity, IInternalAccess instance)
	{
//		System.out.println("after: "+activity);
		
		if(activity!=null && (activity instanceof MTask 
			|| activity instanceof MSubProcess || activity.getActivityType().indexOf("Event")!=-1))
		{
			// Add parameter value for each out edge using the edge id
			List des = activity.getOutgoingDataEdges();
			if(des!=null && des.size()>0)
			{
				for(MDataEdge de: des)
				{
					String pname = de.getSourceParameter();
					Object value = getParameterValue(pname);
					
					if(value!=null)
					{
						if(de.getParameterMapping()!=null)
						{
							SimpleValueFetcher sf = new SimpleValueFetcher(instance.getFetcher());
							sf.setValue("$value", value);
							sf.setValue(pname, value);
							IValueFetcher fetcher = new ProcessThreadValueFetcher(this, true, sf);
							IParsedExpression exp = (IParsedExpression)de.getParameterMapping().getParsed();
							try
							{
								value	= exp.getValue(fetcher);
							}
							catch(RuntimeException e)
							{
								throw new RuntimeException("Error parsing parameter value: "+instance+", "+this+", "+pname+", "+exp, e);
							}
						}
						
						// Test if parameter value type fits
						MParameter mparam = de.getTarget().getParameters().get(de.getTargetParameter());
						Class mpclz = mparam.getClazz().getType(instance.getClassLoader(), instance.getModel().getAllImports());
						if(!SReflect.isSupertype(mpclz, value.getClass()))
						{
							// Autoconvert basic from string
							if(value instanceof String)
							{
								IStringObjectConverter conv = BasicTypeConverter.getBasicStringConverter(mpclz);
								if(conv!=null)
								{
									try
									{
										value = conv.convertString((String)value, null);
									}
									catch(Exception e)
									{
										e.printStackTrace();
									}
								}
							}
							// Autoconvert basic to string
							else if(mpclz.equals(String.class))
							{
								IObjectStringConverter conv = BasicTypeConverter.getBasicObjectConverter(value.getClass());
								if(conv!=null)
								{
									try
									{
										value = conv.convertObject(value, null);
									}
									catch(Exception e)
									{
										e.printStackTrace();
									}
								}
							}
						}
					}
					if(de.getTarget() == null)
					{
						// Result data edge
						instance.getComponentFeature(IArgumentsResultsFeature.class).getResults().put(de.getTargetParameter(), value);
					}
					else
					{
						setDataEdgeValue(de.getId(), value);
					}
				}
			}
			
			// Remove all in parameters (not in case of events)
			if(activity.getActivityType().indexOf("Event")==-1)
			{
				List params = activity.getParameters(new String[]{MParameter.DIRECTION_IN});
				for(int i=0; i getSplitInfos()
	{
		Collection	ret;
		if(splitinfos!=null)
			ret	= splitinfos.values();
		else
			ret	= Collections.emptyList();
		return ret;
	}
	
	/**
	 *  Get a specific split info, if available.
	 */
	public SplitInfo getSplitInfo(String id)
	{
		return splitinfos!=null ? splitinfos.get(id) : null;
	}
	
	/**
	 *  Add a split info.
	 */
	public void addSplitInfo(SplitInfo spi)
	{
		if(splitinfos==null)
			splitinfos = new LinkedHashMap();
		assert !splitinfos.containsKey(spi.getSplitId());
//		System.out.println("push: "+getId()+" "+splitinfos);
		splitinfos.put(spi.getSplitId(), spi);
	}

	/**
	 *  Remove the split info.
	 */
	public void	removeSplitInfo(SplitInfo spi)
	{
//		System.out.println("pop: "+getId()+" "+splitinfos);
		splitinfos.remove(spi.getSplitId());
	}
//	
//	/**
//	 *  Get the topmost split info.
//	 *  @return The split info.
//	 */
//	public int[] peakSplitInfo()
//	{
//		return (int[])splitinfos.get(splitinfos.size()-1);
//	}
//	
//	/**
//	 *  Get the current split id.
//	 *  @return The split id.
//	 */
//	public int getSplitId()
//	{
//		return splitinfos==null? 0: ((int[])splitinfos.get(splitinfos.size()-1))[0];
//	}
//	
//	/**
//	 *  Get the current split count.
//	 *  @return The split count.
//	 */
//	public int getSplitCount()
//	{
//		return splitinfos==null? 0: ((int[])splitinfos.get(splitinfos.size()-1))[1];
//	}
//	
//	/**
//	 *  Get the current split depth.
//	 *  @return The split depth.
//	 */
//	public int getSplitDepth()
//	{
//		return splitinfos==null? 0: splitinfos.size();
//	}
	
//	/**
//	 *  Returns the current subcontext.
//	 *  @return The current subcontext.
//	 */
//	public ThreadContext getSubcontext()
//	{
//		return subcontext;
//	}
//	
//	/**
//	 *  Sets the subcontext.
//	 */
//	public  void setSubcontext(ThreadContext subcontext)
//	{
//		this.subcontext = subcontext;
//	}
	
	/**
	 *  Remove a sub context but keep the corresponding thread.
	 *  E.g. when a sub process terminates, the sub context is removed
	 *  and the initiating thread continues in the outer context.
	 *  @param context	The sub context to be removed.
	 */
	public void removeSubcontext()
	{
//		assert threads!=null && threads.containsKey(context.getInitiator());
		
		List	subthreads	= getSubthreads();
		if(subthreads!=null)
		{
			ProcessThread[] subt = (ProcessThread[])subthreads.toArray(new ProcessThread[subthreads.size()]);
			for(int i=0; i();
//		
//		threads.put(thread, null);
		if(subthreads==null)
			subthreads	= new ArrayList();
		
		subthreads.add(thread);
		if(getInstance().getComponentFeature0(IMonitoringComponentFeature.class)!=null && thread.getInstance().getComponentFeature(IMonitoringComponentFeature.class).hasEventTargets(PublishTarget.TOALL, PublishEventLevel.FINE))
		{	
			thread.getInstance().getComponentFeature(IMonitoringComponentFeature.class).publishEvent(getBpmnFeature(thread.getInstance()).createThreadEvent(IMonitoringEvent.EVENT_TYPE_CREATION, thread), PublishTarget.TOALL);
		}
//		System.out.println("add: "+thread);
	}
	
	/**
	 *  Add an external thread to this context.
	 *  @param thread	The thread to be added.
	 */
	// Hack!!! Make external threads execute before others.
	public void	addExternalThread(ProcessThread thread)
	{
		if(subthreads==null)
			subthreads	= new ArrayList();
		subthreads.add(0, thread);
////		Map	oldthreads	= threads;
//		Set oldthreads = threads;
////		threads	= new LinkedHashMap();
//		threads = new HashSet();
////		threads.put(thread, null);
//		threads.add(thread);
//		if(oldthreads!=null)
//		{
//			threads.addAll(oldthreads);
////			threads.putAll(oldthreads);
//		}
		
		if(getInstance().getComponentFeature0(IMonitoringComponentFeature.class)!=null && thread.getInstance().getComponentFeature(IMonitoringComponentFeature.class).hasEventTargets(PublishTarget.TOALL, PublishEventLevel.FINE))
		{	
			thread.getInstance().getComponentFeature(IMonitoringComponentFeature.class).publishEvent(getBpmnFeature(thread.getInstance()).createThreadEvent(IMonitoringEvent.EVENT_TYPE_CREATION, thread), PublishTarget.TOALL);
		}
//		System.out.println("add: "+thread);
	}
	
	/**
	 *  Get all threads of the context and all subcontexts.
	 */
	public Set getAllThreads()
	{
		Set ret = new HashSet();
		if(subthreads!=null)
		{
//			for(Iterator it=threads.keySet().iterator(); it.hasNext(); )
			for(Iterator it=subthreads.iterator(); it.hasNext(); )
			{
				ProcessThread pc = it.next();
				ret.add(pc);
//				ThreadContext tc = (ThreadContext)threads.get(pc);
				//ThreadContext tc = pc.getSubcontext();
				if(pc.hasSubthreads())
				{
					ret.addAll(pc.getAllThreads());
				}
			}
		}
		return ret;
	}	
	
//	public void removeSubcontext(ThreadContext context)
//	{
////		assert threads!=null && threads.containsKey(context.getInitiator());
//		
//		Set	subthreads	= context.getThreads();
//		if(subthreads!=null)
//		{
//			ProcessThread[] subt = (ProcessThread[])subthreads.toArray(new ProcessThread[subthreads.size()]);
//			for(int i=0; i getSubthreads()
	{
		return subthreads;
	}
	
	/**
	 *  Test if thread has subthreads.
	 */
	public boolean hasSubthreads()
	{
		return subthreads!=null && !subthreads.isEmpty();
	}
	
	/**
	 *  The context is finished, when there are no (more) threads to execute.
	 *  @param pool	The pool to be executed or null for any.
	 *  @param lane	The lane to be executed or null for any. Nested lanes may be addressed by dot-notation, e.g. 'OuterLane.InnerLane'.
	 *  @return True, when the process instance is finished with regards to the specified pool/lane. When both pool and lane are null, true is returned only when all pools/lanes are finished.
	 */
	public boolean	isFinished(String pool, String lane)
	{
		boolean	finished	= true;
		if(subthreads!=null && !subthreads.isEmpty())
		{
//			for(Iterator it=threads.keySet().iterator(); finished && it.hasNext(); )
			for(Iterator it=subthreads.iterator(); finished && it.hasNext(); )
			{
				finished = !it.next().belongsTo(pool, lane);
			}
		}
		return finished;
	}
	
	/**
	 *  Get an executable thread in the context or its sub contexts.
	 *  @param pool	The pool to be executed or null for any.
	 *  @param lane	The lane to be executed or null for any. Nested lanes may be addressed by dot-notation, e.g. 'OuterLane.InnerLane'.
	 *  @return	An executable thread (if any).
	 */
	public ProcessThread getExecutableThread(String pool, String lane)
	{
		// Iterate over all thread contexts and find executable thread
		ProcessThread	ret	= null;
		if(getSubthreads()!=null)
		{
			for(Iterator it=getSubthreads().iterator(); ret==null && it.hasNext(); )
			{
				ProcessThread thread = it.next();
				
				if(thread.getSubthreads()!=null)
				{
					ret	= thread.getExecutableThread(pool, lane);
				}
				else if(!thread.isWaiting() && thread.belongsTo(pool, lane))
				{
					ret	= thread;
				}
			}
		}
		return ret;
	}
	
	/**
	 *  Get a thread per id.
	 *  @param id The thread id.
	 *  @return The process thread.
	 */
	public ProcessThread getThread(String id)
	{
		ProcessThread ret = null;
		if(SUtil.equals(getId(), id))
		{
			ret = this;
		}
		else if(getSubthreads()!=null)
		{
			for(Iterator it=getSubthreads().iterator(); ret==null && it.hasNext(); )
			{
				ProcessThread thread = it.next();
				ret = thread.getThread(id);
				if(ret!=null)
				{
					break;
				}
			}
		}
		return ret;
	}
	
	/**
	 *  Get a cnt for subprocesses.
	 */
	protected String getNextChildId()
	{
		return getId()!=null? getId()+":"+idcnt++: ""+idcnt++;
	}
	
	/**
	 *  Get the loopcmd.
	 *  @return The loopcmd.
	 */
	public IResultCommand getLoopCommand()
	{
		return loopcmd;
	}

	/**
	 *  Set the loopcmd.
	 *  @param loopcmd The loopcmd to set.
	 */
	public void setLoopCommand(IResultCommand loopcmd)
	{
		this.loopcmd = loopcmd;
	}

	/**
	 *  Method that can be used to determine (override) that the thread is finished.
	 */
	public void notifyFinished()
	{
	}
	
	/**
	 *  Get the bpmn feature.
	 */
	protected IInternalBpmnComponentFeature getBpmnFeature(IInternalAccess ia)
	{
		return (IInternalBpmnComponentFeature)ia.getComponentFeature(IBpmnComponentFeature.class);
	}
	
	/**
	 *  Create a string representation of this process thread.
	 *  @return A string representation of this process thread.
	 */
	public String	toString()
	{
		StringBuffer buf = new StringBuffer();
		buf.append(SReflect.getInnerClassName(this.getClass()));
		buf.append("(id=");
		buf.append(id);
		buf.append("(activity=");
		buf.append(activity);
		buf.append(", data=");
		buf.append(data);
		buf.append(", dataedges=");
		buf.append(dataedges);
		buf.append(", waiting=");
		buf.append(waiting);
		buf.append(")");
		return buf.toString();
	}
}