Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
jadex.rules.eca.RuleSystem Maven / Gradle / Ivy
Go to download
Small Java annotation-based event-condition-action rule engine.
package jadex.rules.eca;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import jadex.commons.IResultCommand;
import jadex.commons.Tuple2;
import jadex.commons.beans.PropertyChangeEvent;
import jadex.commons.future.DelegationResultListener;
import jadex.commons.future.Future;
import jadex.commons.future.FutureHelper;
import jadex.commons.future.IFuture;
import jadex.commons.future.IIntermediateFuture;
import jadex.commons.future.IIntermediateResultListener;
import jadex.commons.future.IResultListener;
import jadex.commons.future.IntermediateFuture;
import jadex.rules.eca.annotations.Action;
import jadex.rules.eca.annotations.Condition;
import jadex.rules.eca.propertychange.PropertyChangeManager;
/**
* The rule system is the main entry point. It contains the rulebase
* with all rules and knows about the observed objects.
*/
public class RuleSystem
{
//-------- attributes --------
/** The rulebase. */
protected IRulebase rulebase;
/** The rules generated for an object. */
protected IdentityHashMap[]>> rules;
/** The context for rule action execution. */
protected Object context;
/** The logger for rule warnings. */
protected Logger logger;
/** The PropertyChangeManager to add/remove handlers and manage events */
protected PropertyChangeManager pcman;
/** The execution mode (direct vs queue). */
protected boolean queueevents = true;
/** Flag to check if currently in processAllEvents (hack?). Required to avoid nested processAllEvents() call in addEvent() */
protected boolean processall;
//-------- constructors --------
/**
* Create a new rule system.
*/
public RuleSystem(Object context)
{
this(context, null, true);
}
/**
* Create a new rule system.
*/
public RuleSystem(Object context, Logger logger, boolean queueevents)
{
this.context = context;
this.logger = logger;
this.rulebase = new Rulebase();
this.rules = new IdentityHashMap[]>>(); // objects may change
this.pcman = PropertyChangeManager.createInstance();
this.queueevents = queueevents;
}
//-------- methods --------
/**
* Get the rulebase.
* @return The rule base.
*/
public IRulebase getRulebase()
{
return rulebase;
}
/**
* Process the next event by
* - finding rules that are sensible to the event type
* - evaluate the conditions of these conditions
* - fire actions of triggered rules.
*/
public IIntermediateFuture processEvent()
{
final IntermediateFuture ret = new IntermediateFuture();
if(pcman.hasEvents())
{
IEvent event = pcman.removeEvent(0);
// if(event.getType().toString().indexOf("factchanged.myself")!=-1)
// System.out.println("sdgfsdgf");
// if(event.getType().getType(0).indexOf("factadded")!=-1)// && event.getType().getType(1).indexOf("mybean")!=-1)
// && event.getType().getType(1).indexOf("Ambu")!=-1)
// System.out.println("proc ev: "+event);
List> rules = rulebase.getRules(event.getType());
if(rules!=null)
{
IRule>[] rs = rules.toArray(new IRule>[rules.size()]);
processRules(rs, 0, event, ret).addResultListener(new IResultListener()
{
public void resultAvailable(Void result)
{
ret.setFinished();
}
public void exceptionOccurred(Exception exception)
{
ret.setException(exception);
}
});
}
else
{
// System.out.println("found no rules for: "+event.getType());
ret.setFinished();
}
}
else
{
ret.setFinished();
}
return ret;
}
/**
* Process a given rule set.
*/
protected IFuture processRules(final IRule>[] rules, final int i, final IEvent event, final IntermediateFuture res)
{
final Future ret = new Future();
if(i>()
{
public void resultAvailable(Tuple2 result)
{
if(result.getFirstEntity().booleanValue())
{
// System.out.println("Rule triggered: "+rules[i]+", "+event);
IFuture fut = (IFuture)rules[i].getAction().execute(event, (IRule)rules[i], context, result.getSecondEntity());
if(fut instanceof IIntermediateFuture)
{
((IIntermediateFuture)fut).addResultListener(new IIntermediateResultListener()
{
public void intermediateResultAvailable(Object result)
{
RuleIntermediateEvent ev = new RuleIntermediateEvent(rules[i].getName(), result);
res.addIntermediateResult(ev);
}
public void finished()
{
processRules(rules, i+1, event, res).addResultListener(new DelegationResultListener(ret));
}
public void resultAvailable(Collection result)
{
RuleEvent ev = new RuleEvent(rules[i].getName(), result);
res.addIntermediateResult(ev);
processRules(rules, i+1, event, res).addResultListener(new DelegationResultListener(ret));
}
public void exceptionOccurred(Exception exception)
{
exception.printStackTrace();
processRules(rules, i+1, event, res).addResultListener(new DelegationResultListener(ret));
}
});
}
else
{
fut.addResultListener(new IResultListener()
{
public void resultAvailable(Object result)
{
RuleEvent ev = new RuleEvent(rules[i].getName(), result);
res.addIntermediateResult(ev);
processRules(rules, i+1, event, res).addResultListener(new DelegationResultListener(ret));
}
public void exceptionOccurred(Exception exception)
{
exception.printStackTrace();
processRules(rules, i+1, event, res).addResultListener(new DelegationResultListener(ret));
}
});
}
}
else
{
processRules(rules, i+1, event, res).addResultListener(new DelegationResultListener(ret));
}
}
public void exceptionOccurred(Exception exception)
{
ret.setResult(null);
}
});
}
else
{
ret.setResult(null);
}
}
else
{
ret.setResult(null);
}
return ret;
}
// /**
// *
// */
// protected IFuture processRules(final IRule>[] rules, final int i, final IEvent event, final IntermediateFuture res)
// {
// final Future ret = new Future();
//
// if(i cres = rules[i].getCondition()==null? ICondition.TRUE: rules[i].getCondition().evaluate(event);
// if(cres.getFirstEntity().booleanValue())
// {
// IFuture fut = (IFuture)rules[i].getAction().execute(event, (IRule)rules[i], context, cres.getSecondEntity());
//
// fut.addResultListener(new IResultListener()
// {
// public void resultAvailable(Object result)
// {
// RuleEvent ev = new RuleEvent(rules[i].getName(), result);
// res.addIntermediateResult(ev);
// processRules(rules, i+1, event, res).addResultListener(new DelegationResultListener(ret));
// }
//
// public void exceptionOccurred(Exception exception)
// {
// exception.printStackTrace();
// processRules(rules, i+1, event, res).addResultListener(new DelegationResultListener(ret));
// }
// });
// }
// else
// {
// processRules(rules, i+1, event, res).addResultListener(new DelegationResultListener(ret));
// }
// }
// else
// {
// ret.setResult(null);
// }
//
// return ret;
// }
// /**
// * Process events until the event queue is empty or max
// * events have been processed.
// */
// public IFuture processAllEvents()
// {
//// return processAllEvents(-1);
//
// final Future ret = new Future();
//
// final int[] opencalls = new int[1];
//
// while(pcman.hasEvents())
// {
// opencalls[0]++;
//
// processEvent().addResultListener(new IResultListener>()
// {
// Exception ex = null;
// public void resultAvailable(Collection result)
// {
// proceed();
// }
//
// public void exceptionOccurred(Exception exception)
// {
// ex = exception;
// proceed();
// }
//
// protected void proceed()
// {
// // When all events have been processed and no opencalls
// if(--opencalls[0]==0 && pcman.getSize()==0)
// {
// if(ex==null)
// {
// ret.setResult(null);
// }
// else
// {
// ret.setException(ex);
// }
// }
// }
// });
// }
//
// return ret;
// }
/**
* Process events until the event queue is empty or max
* events have been processed.
*/
public IFuture processAllEvents()
{
// System.out.println("PAE events: "+pcman.getEvents());
// Simulate microplansteps by executing all effects immediately (hack: allow configuration sync/async)
// Notify listeners before(!) checking next rules/events -> keep sensible execution order
FutureHelper.notifyStackedListeners();
if(pcman.hasEvents())
{
boolean first = !processall;
processall = true;
final Future ret = new Future();
try
{
processEvent().addResultListener(new IResultListener>()
{
// Exception ex = null;
public void resultAvailable(Collection result)
{
// System.out.println("processAllEvents.PAE start");
processAllEvents().addResultListener(new DelegationResultListener(ret));
}
public void exceptionOccurred(Exception exception)
{
exception.printStackTrace();
// ex = exception;
// System.out.println("processAllEvents.PAE start");
processAllEvents().addResultListener(new DelegationResultListener(ret));
}
});
}
finally
{
if(first)
{
processall = false;
if(!ret.isDone())
{
// Simulate microplansteps by executing all effects immediately (hack: allow configuration sync/async)
FutureHelper.notifyStackedListeners();
if(logger!=null && !ret.isDone())
{
logger.warning("Asyncronous rule execution.");
}
}
}
}
// System.out.println("processAllEvents.PAE end: "+ret.isDone());
return ret;
}
else
{
// System.out.println("processAllEvents.PAE end: done");
return IFuture.DONE;
}
}
// /**
// * Process events until the event queue is empty or max
// * events have been processed.
// * @return True if was aborted due to reaching max events.
// */
// public boolean processAllEvents(int max)
// {
// int i=0;
//
// for(i=0; pcman.hasEvents() && (max==-1 || i addEvent(IEvent event)
{
// if(event.getType().toString().indexOf("factchanged.myself")!=-1)
// System.out.println("added: "+event.getType()+" "+event.getContent());
// if(event.getType().getTypes().length==1)
// System.out.println("herer: "+event.getType());
// if(event.getType().getType(0).indexOf("goaloption")!=-1 && event.getType().getType(1).indexOf("Treat")!=-1
// && event.getType().getType(1).indexOf("Ambu")!=-1)
// System.out.println("add event: "+event);
// if(event.getType().getType(0).indexOf("factadded")!=-1 && event.getType().getType(1).indexOf("wastebins")!=-1)
// System.out.println("add event: "+event);
final IFuture ret;
pcman.addEvent(event);
if(!queueevents && !processall)
{
// If actions add further events they will be processed as well
// ret = new Future();
// processEvent().addResultListener(new ExceptionDelegationResultListener, Void>(ret)
// {
// public void customResultAvailable(Collection result)
// {
// ret.setResult(null);
// }
// });
// This works also if the mode is changed during execution and some events are in the queue
// execute rulesystem immediately to ensure that variable values are not changed afterwards
// System.out.println("addEvent.PAE start: "+event);
ret = processAllEvents();
}
else
{
// ret = (Future)IFuture.DONE;
ret = IFuture.DONE;
}
return ret;
}
/**
* Test if at least one event is available.
*/
public boolean isEventAvailable()
{
return pcman.hasEvents();
}
/**
* Get the queueevents.
* @return The queueevents
*/
public boolean isQueueEvents()
{
return queueevents;
}
/**
* The queueevents to set.
* @param queueevents The queueevents to set
*/
public void setQueueEvents(boolean queueevents)
{
this.queueevents = queueevents;
}
/**
* Monitor an object to the rule engine.
* - Extracts conditions
* - Extracts actions
* - Creates rules from condition/action pairs
* and adds them to the rulebase.
* - Subscribes for events
*/
public Object observeObject(final Object object, boolean bean, boolean hasrules,
IResultCommand, PropertyChangeEvent> eventadder)
{
// Create proxy object if eventcreators are present
Object proxy = object;
// if(object.getClass().getName().indexOf("Wastebin")!=-1)
// System.out.println("sdfsdff");
Class> clazz = object.getClass();
if(bean && !(object instanceof Class))
{
pcman.addPropertyChangeListener(object, eventadder);
}
if(hasrules)
{
final Map> eventcreators = new HashMap>();
final Map> rules = new HashMap>();
// Analyze the dynamic or static methods of the object (static if object is a class)
// todo: what about using constructors of classes
if(!(object instanceof Class))
{
while(!clazz.equals(Object.class))
{
Method[] methods = clazz.getDeclaredMethods();
for(int i=0; i)object).getDeclaredMethods();
for(int i=0; i> it=rules.values().iterator(); it.hasNext(); )
{
Rule> rule = it.next();
if(rule.getAction()==null || rule.getCondition()==null
|| rule.getEvents()==null || rule.getEvents().size()==0)
{
throw new RuntimeException("Rule is incomplete: "+rule.getName());
}
rulebase.addRule(rule);
}
//todo: fixme
// if(eventcreators.size()>0)
// {
// ProxyFactory pf = new ProxyFactory(object);
// pf.addAdvice(new MethodInterceptor()
// {
// public Object invoke(MethodInvocation mi) throws Throwable
// {
// Object ret = mi.getMethod().invoke(mi.getThis(), mi.getArguments());
// IResultCommand creator = (IResultCommand)eventcreators.get(mi.getMethod());
// if(creator!=null)
// {
// Event event = (Event)creator.execute(null);
// addEvent(event);
// // System.out.println("created event: "+event);
// }
// return ret;
// }
// });
// proxy = pf.getProxy();
// }
this.rules.put(object, new Tuple2(proxy, rules.values().toArray(new IRule[rules.size()])));
// Recusrively call observe object on all direct monitored fields.
// todo: do we want this?
// if(!(object instanceof Class))
// {
// clazz = object.getClass();
// Field[] fields = clazz.getDeclaredFields();
// for(int i=0; i, PropertyChangeEvent> eventadder)
{
if(object==null)
return;
// if(object.getClass().getName().indexOf("Wastebin")!=-1)
// System.out.println("sdfsdff");
pcman.removePropertyChangeListener(object, eventadder);
Tuple2[]> tup = rules.remove(object);
if(tup!=null)
{
IRule>[] rls = tup.getSecondEntity();
for(int i=0; i clazz = object.getClass();
// Field[] fields = clazz.getDeclaredFields();
// for(int i=0; i> eventcreators,
Map> rules)
{
if(method.isAnnotationPresent(jadex.rules.eca.annotations.Event.class))
{
jadex.rules.eca.annotations.Event event = method.getAnnotation(jadex.rules.eca.annotations.Event.class);
final String type = event.value();
FetchFieldCommand com = new FetchFieldCommand(object, type);
eventcreators.put(method, com);
}
else if(method.isAnnotationPresent(Condition.class))
{
Condition cond = method.getAnnotation(Condition.class);
final String name = cond.value();
final Method m = method;
Rule> rule = rules.get(name);
if(rule==null)
{
rule = new Rule(name);
rules.put(name, rule);
}
// Find event types
Annotation[][] paramannos = m.getParameterAnnotations();
List events = new ArrayList();
for(int j=0; j rule = rules.get(name);
if(rule==null)
{
rule = new Rule(name);
rules.put(name, rule);
}
rule.setAction(new MethodAction(object, m));
}
}
}
/**
* Creates a new event based on a field name and value.
*/
class FetchFieldCommand implements IResultCommand
{
/** The object. */
protected Object object;
/** The name. */
protected String name;
/**
* Create a new FetchFieldCommand.
*/
public FetchFieldCommand(Object object, String name)
{
this.object = object;
this.name = name;
}
/**
* Execute the command.
*
* Fetches the field value and return an event with
* type = field name
* content = field value
*/
public IEvent execute(Object args)
{
try
{
Field f = object.getClass().getDeclaredField(name);
f.setAccessible(true);
Object content = f.get(object);
return new Event(name, content);
}
catch(Exception e)
{
throw new RuntimeException(e);
}
}
}