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

jadex.platform.service.ecarules.RulebaseAgent Maven / Gradle / Ivy

package jadex.platform.service.ecarules;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import jadex.bridge.IInternalAccess;
import jadex.bridge.SFuture;
import jadex.bridge.service.annotation.Service;
import jadex.bridge.service.types.ecarules.IRulebaseEvent;
import jadex.bridge.service.types.ecarules.IRulebaseService;
import jadex.commons.ICommand;
import jadex.commons.IFilter;
import jadex.commons.future.DelegationResultListener;
import jadex.commons.future.Future;
import jadex.commons.future.IFuture;
import jadex.commons.future.ISubscriptionIntermediateFuture;
import jadex.commons.future.ITerminationCommand;
import jadex.commons.future.SubscriptionIntermediateFuture;
import jadex.micro.annotation.Agent;
import jadex.micro.annotation.ProvidedService;
import jadex.micro.annotation.ProvidedServices;
import jadex.rules.eca.IRule;
import jadex.rules.eca.IRulebase;
import jadex.rules.eca.Rulebase;

/**
 *  Agent that encapsulates a rulebase and allows for tracking
 *  changes of it. Can be used to distribute rules among different
 *  rule engine agents that listen on the changes of the rulebase
 *  and do add/remove the same rules locally.
 */
@Agent
@Service
@ProvidedServices(@ProvidedService(type=IRulebaseService.class))
public class RulebaseAgent implements IRulebaseService
{
	/** The agent. */
	@Agent
	protected IInternalAccess agent;
	
	/** The subscriptions. */
	protected List> rbsubscribers = new ArrayList>();
	
	/** The rulebase. */
	protected IRulebase rulebase;
	
	/** The open calls (callid -> set of event ids that have to be acked. */
	protected Map> opencalls = new HashMap>();
	/** callid -> future . */
	protected Map> callfutures = new HashMap>();
	
	// todo?!: change validation interceptor to check whether service AND agent have been initialized 
//	/**
//	 *  Called on agent creation.
//	 */
//	@AgentCreated
//	public void agentCreated()
//	{
//		this.rbsubscribers = new ArrayList>();
//		this.opencalls = new HashMap>();
//		this.callfutures = new HashMap>();
//	}
	
	/**
	 *  Get the rulebase.
	 */
	public IRulebase getRulebase()
	{
		if(rulebase==null)
			rulebase = new Rulebase();
		return rulebase;
	}
	
	/**
	 *  Add a new rule.
	 *  @param rule The rule.
	 */
	public IFuture addRule(IRule rule)
	{
		Future ret = new Future();
		try
		{
			getRulebase().addRule(rule);
			Set evs = new HashSet();
			Integer callid = Integer.valueOf(ret.hashCode());
			callfutures.put(callid, ret);
			opencalls.put(callid, evs);
			notifySubscribers(new RuleAddedEvent(callid.intValue(), rule), evs).addResultListener(new DelegationResultListener(ret));
		}
		catch(RuntimeException e)
		{
			ret.setException(e);
		}
		return ret;
	}
	
	/**
	 *  Remove a rule.
	 *  @param rule The rule.
	 */
	public IFuture removeRule(String rulename)
	{
		Future ret = new Future();
		try
		{
			getRulebase().removeRule(rulename);
			Set evs = new HashSet();
			Integer callid = Integer.valueOf(ret.hashCode());
			callfutures.put(callid, ret);
			opencalls.put(callid, evs);
			notifySubscribers(new RuleRemovedEvent(callid.intValue(), rulename), evs).addResultListener(new DelegationResultListener(ret));
		}
		catch(RuntimeException e)
		{
			ret.setException(e);
		}
		return ret;
	}
	
	/**
	 *  Subscribe to rule base changes.
	 */
	public ISubscriptionIntermediateFuture subscribeToRulebase()
	{
//		System.out.println("subscribed: "+ServiceCall.getCurrentInvocation().getCaller());
		
		final SubscriptionIntermediateFuture ret = (SubscriptionIntermediateFuture)SFuture.getNoTimeoutFuture(SubscriptionIntermediateFuture.class, agent);
		ret.addBackwardCommand(new IFilter()
		{
			public boolean filter(Object obj)
			{
				return obj instanceof FinishedEvent;
			}
		}, new ICommand()
		{
			public void execute(Object args)
			{
				// received a finished id
				FinishedEvent ev = (FinishedEvent)args;
				Integer callid = Integer.valueOf(ev.getCallId());
				Set evs = opencalls.get(callid);
				evs.remove(Integer.valueOf(ev.getId()));
				if(evs.isEmpty())
				{
					opencalls.remove(callid);
					Future ret = callfutures.remove(callid);
					ret.setResult(null);
				}
			}
		});
		ret.setTerminationCommand(new ITerminationCommand()
		{
			public void terminated(Exception reason)
			{
				rbsubscribers.remove(ret);
			}
			
			public boolean checkTermination(Exception reason)
			{
				return true;
			}
		});
		rbsubscribers.add(ret);
		return ret;
	}
	
	/**
	 *  Notify all subscribers of an event.
	 */
	protected IFuture notifySubscribers(ARulebaseEvent event, Set evs)
	{
		Future ret = new Future();
		
		if(!rbsubscribers.isEmpty())
		{
			ARulebaseEvent re = event;
			
			for(SubscriptionIntermediateFuture sub: rbsubscribers)
			{
				evs.add(Integer.valueOf(re.getId()));
				if(!sub.addIntermediateResultIfUndone(re))
				{
					rbsubscribers.remove(sub);
				}
				re = re.createCopy();
			}
		}
		else
		{
			ret.setResult(null);
		}
		
		return ret;
	}
	
	/**
	 *  Notify the current state.
	 */
	protected void notifyCurrentState(SubscriptionIntermediateFuture sub)
	{
		for(IRule rule: rulebase.getRules())
		{
			if(!sub.addIntermediateResultIfUndone(new RuleAddedEvent(-1, rule)))
			{
				rbsubscribers.remove(sub);
			}
		}
	}
}