Drools on AppFuse
Recently got a chance to adopt rule engine into project to allow abstraction of business rules from the service layer, that way, business rules can be maintained in it's own space. I pick
Drools as the underlying rule engine because it is intuitive, simple, and robust JSR94 compliant rule engine, plus it's open source. Below are steps to integrate this powerful rule engine into
AppFuse. I use the
Spring Modules distribution to make it easier for the integration since all the plumbing is done nicely with
Spring.
1. Add required libraries:
a. springmodules-jsr94-0.2.jar
b. /jsr94
c. /drools
d. /janino
Refer to
How to add a Library into AppFuse for information on how to add new library into AppFuse.
2. Drop the Spring ApplicationContext for Drools configuration,
applicationContext-rules.xml
, into
[appfuse]\web\WEB-INF\
:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="ruleServiceProvider"
class="org.springmodules.jsr94.factory.DefaultRuleServiceProviderFactoryBean">
<property name="provider">http://drools.org/</value></property>
<property name="providerClass"><value>org.drools.jsr94.rules.RuleServiceProviderImpl</value>
</property>
</bean>
<bean id="ruleRuntime"
class="org.springmodules.jsr94.factory.RuleRuntimeFactoryBean">
<property name="serviceProvider"><ref local="ruleServiceProvider"/></property>
</bean>
<bean id="ruleAdministrator"
class="org.springmodules.jsr94.factory.RuleAdministratorFactoryBean">
<ref local="ruleServiceProvider"/></property>
</bean>
<bean id="ruleSource" class="org.springmodules.jsr94.rulesource.DefaultRuleSource">
<property name="ruleRuntime"><ref local="ruleRuntime"/></property>
<property name="ruleAdministrator"><ref local="ruleAdministrator"/></property>
<property name="source"><value>/WEB-INF/authorizedUsers.drl</value></property>
<property name="bindUri"><value>authorizedUsers</value></property>
</bean>
<bean id="rulesService" class="org.appfuse.service.impl.RulesServiceDrools">
<property name="ruleSource"><ref local="ruleSource"/></property>
</bean>
</beans>
The file,
authorizedUsers.drl
, is the ruleset, and
RulesServiceDrools
is the Drools rule implementation we have in this sample setup.
3. The signature for the ruleset,
authorizedUsers.drl
, in
[appfuse]\web\WEB-INF\
:
<?xml version="1.0" encoding="UTF-8"?>
<rule-set
name="Get authorized users"
description="Rules to retrieve authorized users"
xmlns="http://drools.org/rules"
xmlns:java="http://drools.org/semantics/java"
xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xs:schemaLocation="http://drools.org/rules rules.xsd
http://drools.org/semantics/java java.xsd">
<rule name="Enabled users">
<parameter identifier="user">
<class>org.appfuse.model.User</class>
</parameter>
<java:condition> user.isEnabled() != true </java:condition>
<java:consequence> drools.retractObject(user); </java:consequence>
</rule>
<rule name="Valid credentials">
<parameter identifier="user">
<class>org.appfuse.model.User</class>
</parameter>
<java:condition>
user.isCredentialsExpired() == true || user.getRoles().size() == 0
</java:condition>
<java:consequence>
drools.retractObject(user);
</java:consequence>
</rule>
</rule-set>
Two rules are defined in the ruleset: Enabled users and Valid credentials. Both rules check for condition, if met, the user is removed from list. Basically, the two rules mean:
if (!user.isEnabled() && (user.isCredentialsExpired()
|| user.getRoles().size() == 0))
userList.remove(user);
4. The interface for the rules service,
RulesService
, is simple enough:
public interface RulesService {
public List getAuthorizedUsers(List users);
}
And the associated implementation,
RulesServiceDrools
:
public class RulesServiceDrools extends Jsr94RuleSupport implements RulesService {
public final static String ACTIVE_USERS_URI="authorizedUsers";
public List getAuthorizedUsers(List users) {
return executeStateless(ACTIVE_USERS_URI, users);
}
}
I couldn't get the Spring Jsr94Template to work properly. Ideally, the output list from Drools should be access via Spring template:
public List getAuthorizedUsers(List users) {
List outputList = getTemplate().executeStateless(ACTIVE_USERS_URI,null,
new StatelessRuleSessionCallback() {
public Object execute(StatelessRuleSession session)
throws InvalidRuleSessionException, RemoteException {
return session.executeRules(users);
}
}
);
return outputList;
}
5. The
Tapestry action code to get the list of users:
public abstract class AuthorizedUsers extends BasePage implements PageRenderListener {
public abstract UserManager getUserManager();
public abstract void setUserManager(UserManager manager);
public abstract RulesService getRulesService();
public abstract void setRulesService(RulesService svr);
public void pageBeginRender(PageEvent event) {
List authorizedUsers = getRulesService().getAuthorizedUsers(getUserManager().getUsers(null));
...
}
}
That's pretty much it to setup Drools in AppFuse and starts ruling away.