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

jadex.bpmn.runtime.BpmnInterpreter 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: 2.4
Show newest version
package jadex.bpmn.runtime;

import jadex.bpmn.model.MActivity;
import jadex.bpmn.model.MBpmnModel;
import jadex.bpmn.model.MParameter;
import jadex.bpmn.model.MPool;
import jadex.bpmn.model.MSequenceEdge;
import jadex.bpmn.runtime.handler.DefaultActivityHandler;
import jadex.bpmn.runtime.handler.DefaultStepHandler;
import jadex.bpmn.runtime.handler.EventEndErrorActivityHandler;
import jadex.bpmn.runtime.handler.EventIntermediateErrorActivityHandler;
import jadex.bpmn.runtime.handler.EventIntermediateMessageActivityHandler;
import jadex.bpmn.runtime.handler.EventIntermediateMultipleActivityHandler;
import jadex.bpmn.runtime.handler.EventIntermediateNotificationHandler;
import jadex.bpmn.runtime.handler.EventIntermediateTimerActivityHandler;
import jadex.bpmn.runtime.handler.EventMultipleStepHandler;
import jadex.bpmn.runtime.handler.GatewayORActivityHandler;
import jadex.bpmn.runtime.handler.GatewayParallelActivityHandler;
import jadex.bpmn.runtime.handler.GatewayXORActivityHandler;
import jadex.bpmn.runtime.handler.SubProcessActivityHandler;
import jadex.bpmn.runtime.handler.TaskActivityHandler;
import jadex.bpmn.runtime.task.ExecuteStepTask;
import jadex.bpmn.tools.ProcessThreadInfo;
import jadex.bridge.ComponentChangeEvent;
import jadex.bridge.ComponentTerminatedException;
import jadex.bridge.IComponentAdapter;
import jadex.bridge.IComponentAdapterFactory;
import jadex.bridge.IComponentChangeEvent;
import jadex.bridge.IComponentDescription;
import jadex.bridge.IComponentInstance;
import jadex.bridge.IComponentManagementService;
import jadex.bridge.IComponentStep;
import jadex.bridge.IExternalAccess;
import jadex.bridge.IInternalAccess;
import jadex.bridge.IMessageAdapter;
import jadex.bridge.IMessageService;
import jadex.bridge.MessageType;
import jadex.bridge.service.IServiceContainer;
import jadex.bridge.service.RequiredServiceBinding;
import jadex.bridge.service.RequiredServiceInfo;
import jadex.bridge.service.SServiceProvider;
import jadex.bridge.service.clock.IClockService;
import jadex.bridge.service.clock.ITimedObject;
import jadex.commons.IFilter;
import jadex.commons.IValueFetcher;
import jadex.commons.future.DefaultResultListener;
import jadex.commons.future.DelegationResultListener;
import jadex.commons.future.Future;
import jadex.commons.future.IFuture;
import jadex.commons.future.IResultListener;
import jadex.javaparser.IParsedExpression;
import jadex.javaparser.SJavaParser;
import jadex.kernelbase.AbstractInterpreter;
import jadex.kernelbase.InterpreterFetcher;

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

/**
 *  The bpmn interpreter is able to execute bpmn diagrams.
 *  
 *  Arguments are treated in BPMN as parameters, i.e. the argument
 *  values will be fed into corresponding parameters.
 */
public class BpmnInterpreter extends AbstractInterpreter implements IComponentInstance, IInternalAccess
{	
	//-------- static part --------
	
	/** Constant for step event. */
	public static final String TYPE_ACTIVITY = "activity";
	
	/** The change event prefix denoting a thread event. */
	public static final String	TYPE_THREAD	= "thread";
	
	/** The activity execution handlers (activity type -> handler). */
	public static final Map DEFAULT_ACTIVITY_HANDLERS;
	
	/** The step execution handlers (activity type -> handler). */
	public static final Map DEFAULT_STEP_HANDLERS;

	/** The flag for all pools. */
	public static final String ALL = "All";
	
	static
	{
		Map stephandlers = new HashMap();
		
		stephandlers.put(IStepHandler.STEP_HANDLER, new DefaultStepHandler());
		stephandlers.put(MBpmnModel.EVENT_INTERMEDIATE_MULTIPLE, new EventMultipleStepHandler());
		
		DEFAULT_STEP_HANDLERS = Collections.unmodifiableMap(stephandlers);
		
		Map activityhandlers = new HashMap();
		
		// Task/Subprocess handler.
		activityhandlers.put(MBpmnModel.TASK, new TaskActivityHandler());
		activityhandlers.put(MBpmnModel.SUBPROCESS, new SubProcessActivityHandler());
	
		// Gateway handler.
		activityhandlers.put(MBpmnModel.GATEWAY_PARALLEL, new GatewayParallelActivityHandler());
		activityhandlers.put(MBpmnModel.GATEWAY_DATABASED_EXCLUSIVE, new GatewayXORActivityHandler());
		activityhandlers.put(MBpmnModel.GATEWAY_DATABASED_INCLUSIVE, new GatewayORActivityHandler());
	
		// Initial events.
		// Options: empty, message, rule, timer, signal, multi, link
		// Missing: link 
		// Note: non-empty start events are currently only supported in subworkflows
		// It is currently not possible to start a top-level workflow using the other event types,
		// i.e. the creation of a workflow is not supported. 
		activityhandlers.put(MBpmnModel.EVENT_START_EMPTY, new DefaultActivityHandler());
		activityhandlers.put(MBpmnModel.EVENT_START_TIMER, new EventIntermediateTimerActivityHandler());
		activityhandlers.put(MBpmnModel.EVENT_START_MESSAGE, new EventIntermediateMessageActivityHandler());
		activityhandlers.put(MBpmnModel.EVENT_START_MULTIPLE, new EventIntermediateMultipleActivityHandler());
		activityhandlers.put(MBpmnModel.EVENT_START_RULE, new EventIntermediateNotificationHandler());
		activityhandlers.put(MBpmnModel.EVENT_START_SIGNAL, new EventIntermediateNotificationHandler());
			
		// Intermediate events.
		// Options: empty, message, rule, timer, error, signal, multi, link, compensation, cancel
		// Missing: link, compensation, cancel
		activityhandlers.put(MBpmnModel.EVENT_INTERMEDIATE_EMPTY, new DefaultActivityHandler());
		activityhandlers.put(MBpmnModel.EVENT_INTERMEDIATE_MESSAGE, new EventIntermediateMessageActivityHandler());
		activityhandlers.put(MBpmnModel.EVENT_INTERMEDIATE_RULE, new EventIntermediateNotificationHandler());
		activityhandlers.put(MBpmnModel.EVENT_INTERMEDIATE_TIMER, new EventIntermediateTimerActivityHandler());
		activityhandlers.put(MBpmnModel.EVENT_INTERMEDIATE_ERROR, new EventIntermediateErrorActivityHandler());
		activityhandlers.put(MBpmnModel.EVENT_INTERMEDIATE_MULTIPLE, new EventIntermediateMultipleActivityHandler());
		activityhandlers.put(MBpmnModel.EVENT_INTERMEDIATE_SIGNAL, new EventIntermediateNotificationHandler());
//		defhandlers.put(MBpmnModel.EVENT_INTERMEDIATE_RULE, new UserInteractionActivityHandler());
		
		// End events.
		// Options: empty, message, error, compensation, terminate, signal, multi, cancel, link
		// Missing: link, compensation, cancel, terminate, signal, multi
		activityhandlers.put(MBpmnModel.EVENT_END_EMPTY, new DefaultActivityHandler());
		activityhandlers.put(MBpmnModel.EVENT_END_ERROR, new EventEndErrorActivityHandler());
		activityhandlers.put(MBpmnModel.EVENT_END_MESSAGE, new EventIntermediateMessageActivityHandler());

		DEFAULT_ACTIVITY_HANDLERS = Collections.unmodifiableMap(activityhandlers);
	}
	
	//-------- attributes --------
	
	/** The micro agent model. */
	protected MBpmnModel model;
	
	// todo: allow multiple pools/lanes?
	/** The configuration. */
	protected String pool;
	
	/** The configuration. */
	protected String lane;
	
	/** The activity handlers. */
	protected Map activityhandlers;
	
	/** The step handlers. */
	protected Map stephandlers;

	/** The thread context. */
	protected ThreadContext	context;
	
	/** The context variables. */
	protected Map variables;
	
	/** The finishing flag marker. */
	protected boolean finishing;
	
	/** The messages waitqueue. */
	protected List messages;
	
	/** The inited future. */
	protected Future inited;
	
	/** The started flag. */
	protected boolean started;
	
	/** The thread id counter. */
	protected int idcnt;
	
	//-------- constructors --------
	
	/**
	 *  Create a new bpmn process.
	 *  @param adapter The adapter.
	 */
	// Constructor for bdi plan interpreter
	public BpmnInterpreter(IComponentAdapter adapter, MBpmnModel model, Map arguments, 
		String config, final IExternalAccess parent, Map activityhandlers, Map stephandlers, 
		IValueFetcher fetcher, IComponentManagementService cms, IClockService cs, IMessageService ms,
		IServiceContainer container)
	{
		super(null, model.getModelInfo(), config, null, parent, null, true, new Future());
		construct(model, activityhandlers, stephandlers);		
		this.fetcher = fetcher!=null? fetcher: new BpmnInstanceFetcher(this, fetcher);
		this.adapter = adapter;
		this.container = container;
		
		variables.put("$cms", cms);
		variables.put("$clock", cs);
		variables.put("$msgservice", ms);
		
		initContextVariables();
		
		// Create initial thread(s). 
		List startevents = model.getStartActivities(null, null);
		for(int i=0; startevents!=null && i0)
			{
				int idx	= config.indexOf('.');
				if(idx==-1)
				{
					this.pool	= config;
					this.lane	= null;
				}
				else
				{
					this.pool	= config.substring(0, idx);
					this.lane	= config.substring(idx+1);
				}
			}
		}
		
		this.activityhandlers = activityhandlers!=null? activityhandlers: DEFAULT_ACTIVITY_HANDLERS;
		this.stephandlers = stephandlers!=null? stephandlers: DEFAULT_STEP_HANDLERS;
		this.context = new ThreadContext(model);
		this.messages = new ArrayList();
		this.variables	= getArguments()!=null ? new HashMap(getArguments()) : new HashMap();
	}
	
	//-------- IComponentInstance interface --------
	
	/**
	 *  Can be called on the component thread only.
	 * 
	 *  Main method to perform component execution.
	 *  Whenever this method is called, the component performs
	 *  one of its scheduled actions.
	 *  The platform can provide different execution models for components
	 *  (e.g. thread based, or synchronous).
	 *  To avoid idle waiting, the return value can be checked.
	 *  The platform guarantees that executeStep() will not be called in parallel. 
	 *  @return True, when there are more actions waiting to be executed. 
	 */
	public boolean executeStep()
	{
		boolean ret = false;
		
		try
		{
			if(!isFinished(pool, lane) && isReady(pool, lane))
				executeStep(pool, lane);
			
//			System.out.println("After step: "+this.getComponentAdapter().getComponentIdentifier().getName()+" "+isFinished(pool, lane));
			
			if(!finishing && isFinished(pool, lane) && !model.isKeepAlive() && started)
			{
//				System.out.println("terminating: "+getComponentIdentifier());
				finishing = true;
//				((IComponentManagementService)variables.get("$cms")).destroyComponent(adapter.getComponentIdentifier());
				
				SServiceProvider.getService(getServiceProvider(), IComponentManagementService.class)
					.addResultListener(createResultListener(new DefaultResultListener()
				{
					public void resultAvailable(Object result)
					{
						((IComponentManagementService)result).destroyComponent(adapter.getComponentIdentifier());
					}
				}));
			}
			
//			System.out.println("Process wants: "+this.getComponentAdapter().getComponentIdentifier().getLocalName()+" "+!isFinished(null, null)+" "+isReady(null, null));
			
			ret = !isFinished(pool, lane) && isReady(pool, lane);
		}
		catch(ComponentTerminatedException ate)
		{
			// Todo: fix kernel bug.
			ate.printStackTrace();
		}
		
		return ret;
	}

	/**
	 *  Execute the init step 1.
	 * /
	protected void executeInitStep1()
	{
		// Fetch and cache services, then init service container.
		// Note: It is very tricky to call createResultListener() in the constructor as this
		// indirectly calls adapter.wakeup() but the component shouldn't run automatically!
		
		// todo: remove this hack of caching services
		final boolean services[] = new boolean[3];
		SServiceProvider.getServiceUpwards(getServiceContainer(), IComponentManagementService.class)
			.addResultListener(createResultListener(new DefaultResultListener()
		{
			public void resultAvailable(Object result)
			{
				variables.put("$cms", result);
				boolean init2;
				synchronized(services)
				{
					services[0]	= true;
					init2 = services[0] && services[1] && services[2];
				}
				if(init2)
				{
					executeInitStep2();
				}
			}
			public void exceptionOccurred(Exception exception)
			{
				inited.setException(exception);
			}
		}));
		SServiceProvider.getService(getServiceContainer(), IClockService.class, RequiredServiceInfo.SCOPE_PLATFORM)
			.addResultListener(createResultListener(new DefaultResultListener()
		{
			public void resultAvailable(Object result)
			{
				variables.put("$clock", result);
				boolean init2;
				synchronized(services)
				{
					services[1]	= true;
					init2 = services[0] && services[1] && services[2];
				}
				if(init2)
				{
					executeInitStep2();
				}
			}
			public void exceptionOccurred(Exception exception)
			{
				inited.setException(exception);
			}
		}));
		SServiceProvider.getService(getServiceContainer(), IMessageService.class, RequiredServiceInfo.SCOPE_PLATFORM)
			.addResultListener(createResultListener(new IResultListener()
		{
			public void resultAvailable(Object result)
			{
				variables.put("$msgservice", result);
				boolean init2;
				synchronized(services)
				{
					services[2]	= true;
					init2 = services[0] && services[1] && services[2];
				}
				if(init2)
				{
					executeInitStep2();
				}
			} 
			public void exceptionOccurred(Exception exception)
			{
				inited.setException(exception);
			}
		}));
	}*/
	
//	/**
//	 *  Execute the init step 2.
//	 */
//	protected void executeInitStep2()
//	{
//		// Initialize context variables.
//		variables.put("$interpreter", this);
//		
//		init(getModel(), getConfiguration(), arguments)
//			.addResultListener(createResultListener(new DelegationResultListener(inited)
//		{
//			public void customResultAvailable(Object result)
//			{
//				// Notify cms that init is finished.
//				initContextVariables();
//
//				inited.setResult(new Object[]{BpmnInterpreter.this, adapter});
//			}
//		}));
//	}

	/**
	 *  Start the component behavior.
	 */
	public void startBehavior()
	{
		assert !isExternalThread();
		
		// Start initial steps (if any).
		super.startBehavior();
		
		// Use step in case agent is started as suspended.
		scheduleStep(new IComponentStep()
		{
			public Object execute(IInternalAccess ia)
			{
				// Create initial thread(s). 
				List startevents	= model.getStartActivities(pool, lane);
				for(int i=0; startevents!=null && i0 /*&& MBpmnModel.EVENT_INTERMEDIATE_MESSAGE.equals(thread.getActivity().getActivityType()) 
				&& (thread.getPropertyValue(EventIntermediateMessageActivityHandler.PROPERTY_MODE)==null 
					|| EventIntermediateMessageActivityHandler.MODE_RECEIVE.equals(thread.getPropertyValue(EventIntermediateMessageActivityHandler.PROPERTY_MODE)))*/)
			{
				boolean processed = false;
				for(int i=0; i=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
//				{
//					throw new RuntimeException("Unsupported collection type: "+coll);
//				}
		}
	}
	
//	/**
//	 *  Fires an activity execution event.
//	 *  @param threadid ID of the executing ProcessThread.
//	 *  @param activity The activity being executed.
//	 */
//	protected void fireStartActivity(String threadid, MActivity activity)
//	{
////		System.out.println("fire start: "+activity);
//		notifyListeners(new ComponentChangeEvent(IComponentChangeEvent.EVENT_TYPE_CREATION, ACTIVITY, activity.getName(), threadid, getComponentIdentifier(), null));
//	}
//	
//	/**
//	 *  Fires an activity execution event.
//	 *  @param threadid ID of the executing ProcessThread.
//	 *  @param activity The activity being executed.
//	 */
//	protected void fireEndActivity(String threadid, MActivity activity)
//	{
////		System.out.println("fire end: "+activity);
//		if(activitylisteners!=null)
//		{
//			for(Iterator it = activitylisteners.iterator(); it.hasNext(); )
//				((IActivityListener)it.next()).activityEnded(new ChangeEvent(threadid, null, activity));
//		}
//	}
	
	/**
	 *  Schedule a step of the agent.
	 *  May safely be called from external threads.
	 *  @param step	Code to be executed as a step of the agent.
	 *  @return The result of the step.
	 */
	protected int cnt;
	public IFuture scheduleStep(final IComponentStep step)
	{
		final Future ret = new Future();
		
		getComponentAdapter().invokeLater(new Runnable()
		{
			public void run()
			{
				// To schedule a step an implicit activity is created.
				// In order to put the step parameter value it is necessary
				// to have an edge with a mapping. Otherwise the parameter
				// value with be deleted in process thread updateParametersBeforeStep().
				
				MActivity act = new MActivity();
				act.setName("External Step Activity: "+(cnt++));
				act.setClazz(ExecuteStepTask.class);
				act.addParameter(new MParameter(MParameter.DIRECTION_IN, Object[].class, "step", null));
				act.setActivityType(MBpmnModel.TASK);
				MSequenceEdge edge = new MSequenceEdge();
				edge.setTarget(act);
				edge.addParameterMapping("step", SJavaParser.parseExpression("step", null, null), null);
				act.addIncomingSequenceEdge(edge);
				MPool pl = pool!=null? model.getPool(pool): (MPool)model.getPools().get(0);
				act.setPool(pl);
				ProcessThread thread = new ProcessThread(""+idcnt++, act, context, BpmnInterpreter.this);
				thread.setLastEdge(edge);
				thread.setParameterValue("step", new Object[]{step, ret});
				context.addExternalThread(thread);
			}
		});
		return ret;
	}
	
	/**
	 *  Wait for some time and execute a component step afterwards.
	 */
	public IFuture waitFor(final long delay, final IComponentStep step)
	{
		// todo: remember and cleanup timers in case of component removal.
		
		final Future ret = new Future();
		
		SServiceProvider.getService(getServiceContainer(), IClockService.class, RequiredServiceInfo.SCOPE_PLATFORM)
			.addResultListener(createResultListener(new DelegationResultListener(ret)
		{
			public void customResultAvailable(Object result)
			{
				IClockService cs = (IClockService)result;
				cs.createTimer(delay, new ITimedObject()
				{
					public void timeEventOccurred(long currenttime)
					{
						scheduleStep(step).addResultListener(new DelegationResultListener(ret));
					}
				});
			}
		}));
		
		return ret;
	}
	
//	/**
//	 *  Adds an activity listener. The listener will be called
//	 *  once a process thread executes a new activity.
//	 *  @param listener The activity listener.
//	 */
//	public void addActivityListener(IActivityListener listener)
//	{
//		if(activitylisteners==null)
//			activitylisteners = new ArrayList();
//		activitylisteners.add(listener);
//	}
//	
//	/**
//	 *  Removes an activity listener.
//	 *  @param listener The activity listener.
//	 */
//	public void removeActivityListener(IActivityListener listener)
//	{
//		if(activitylisteners!=null)
//			activitylisteners.remove(listener);
//	}
	
	/**
	 * 
	 */
	public ProcessThreadInfo createProcessThreadInfo(ProcessThread thread)
	{
		 ProcessThreadInfo info = new ProcessThreadInfo(thread.getId(), thread.getActivity().getBreakpointId(),
            thread.getActivity().getPool()!=null ? thread.getActivity().getPool().getName() : null,
            thread.getActivity().getLane()!=null ? thread.getActivity().getLane().getName() : null,
            thread.getException()!=null ? thread.getException().toString() : "",
            thread.isWaiting(), thread.getData()!=null ? thread.getData().toString() : "");
		 return info;
	}

	//-------- abstract interpreter methods --------
	
	/**
	 *  Get the value fetcher.
	 */
	public IValueFetcher getFetcher()
	{
		if(fetcher==null)
		{
			fetcher = new BpmnInstanceFetcher(this, new InterpreterFetcher(this));
		}
		return fetcher;
	}
	
//	/**
//	 *  Add a default value for an argument (if not already present).
//	 *  Called once for each argument during init.
//	 *  @param name	The argument name.
//	 *  @param value	The argument value.
//	 */
//	public void	addDefaultArgument(String name, Object value)
//	{
//		// super to ensure that getArguments() return correct values.
//		super.addDefaultArgument(name, value); 
//		if(!variables.containsKey(name))
//		{
////			System.out.println("arg: "+name+" "+value);
//			variables.put(name, value);
//		}
//	}
	
//	/**
//	 *  Add a default value for a result (if not already present).
//	 *  Called once for each result during init.
//	 *  @param name	The result name.
//	 *  @param value	The result value.
//	 */
//	public void	addDefaultResult(String name, Object value)
//	{
////		super.addDefaultResult(name, value);
//		if(!variables.containsKey(name))
//		{
//			variables.put(name, value);
//		}
//	}
	
//	/**
//	 *  Get the results of the component (considering it as a functionality).
//	 *  @return The results map (name -> value). 
//	 */
//	public Map getResults()
//	{
//		IArgument[] results = getModel().getResults();
//		Map res = new HashMap();
//		
//		for(int i=0; i




© 2015 - 2025 Weber Informatics LLC | Privacy Policy