Drools is a business rule management system (BRMS). It based on rules engine, using an implementation of the Rete algorithm.
Drools supports the JSR-94 standard for its business rule engine and enterprise framework for the construction, maintenance, and enforcement of business policies in an organization, application, or service.
More about Drools is here.
A business process or workflow describes the order in which a series of steps need to be executed, using a flow chart. This makes it much easier to describe a complex composition of various tasks. Processes are especially useful in describing state-based, long-running processes. Drools Flow allows end users to specify, execute and monitor (a part of) their business logic using these processes. The Drools Flow process framework is easily embeddable into any Java application (as a simple Java component) or can run standalone in a server environment.
This article is about practical implementation of rule flow with Spring. In my case flow include validation step at beginning that validates objects, then executes several tasks. Execution of task depends on rules define in flow descriptor. Flow itself defined in flow file (.rf).
Simple flow diagram:
Before flow could execute we need to perform certain action as create session, load flow and validation (.drl) files, setup globals and work items for flow. To simplify controls and manage this action I create integration classes.
1. Process Descriptor
package com.company.workflow.integration; import java.util.HashMap; import java.util.Map; import org.drools.builder.ResourceType; import org.springframework.core.io.Resource; public class ProcessDescriptor { private final Map resourceMap; private Map<String, Object> globals = new HashMap<String, Object>(); private Map<String, Object> workItems = new HashMap<String, Object>(); private Boolean validation = true; public ProcessDescriptor(final Map<Resource, ResourceType> resourceMap) { this.resourceMap = resourceMap; } public Map <Resource, ResourceType> getResourceMap() { return resourceMap; } public Map<String, Object> getGlobals() { return globals; } public void setGlobals(final Map<String, Object> globals) { this.globals = globals; } public Boolean getValidation() { return validation; } public void setValidation(final Boolean validation) { this.validation = validation; } public Map<String, Object> getWorkItems() { return workItems; } public void setWorkItems(final Map<String, Object> workItems) { this.workItems = workItems; } }
2. KnowledgeSessionLookupImpl.java
package com.company.workflow.integration; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import org.drools.KnowledgeBase; import org.drools.KnowledgeBaseConfiguration; import org.drools.KnowledgeBaseFactory; import org.drools.builder.KnowledgeBuilder; import org.drools.builder.KnowledgeBuilderFactory; import org.drools.builder.ResourceType; import org.drools.io.ResourceFactory; import org.drools.runtime.StatefulKnowledgeSession; import org.drools.runtime.process.WorkItemHandler; import org.drools.runtime.process.WorkItemManager; import org.springframework.core.io.Resource; import com.company.workflow.services.ReportFactory; import com.company.workflow.services.ValidationReport; import com.company.workflow.services.impl.DefaultReportFactory; import com.company.workflow.workingitems.BaseWorkItemHandler; public class KnowledgeSessionLookupImpl implements KnowledgeSessionLookup { private KnowledgeBase knowledgeBase; private Map<String, ProcessDescriptor> processConfig = new HashMap<String, ProcessDescriptor>(); private Map<String, WorkItemHandler> systemHandlers = new HashMap<String, WorkItemHandler>(); public KnowledgeSessionLookupImpl(final Map<String, ProcessDescriptor> config) { processConfig = config; } public void init() { } public StatefulKnowledgeSession loadSession(final int sessionId) { final StatefulKnowledgeSession session = knowledgeBase.newStatefulKnowledgeSession(); return session; } public StatefulKnowledgeSession newSession(final String processName) throws IOException { final KnowledgeBuilder builder = KnowledgeBuilderFactory.newKnowledgeBuilder(); final ProcessDescriptor proc = processConfig.get(processName); if (proc == null) { throw new RuntimeException("No process descriptor found for [" + processName + "] process"); } if ((proc.getResourceMap() == null) || proc.getResourceMap().isEmpty()) { throw new RuntimeException("Empty list of flow and rules for [" + processName + "] process"); } for (final Entry<Resource, ResourceType> entry : proc.getResourceMap().entrySet()) { builder.add(ResourceFactory.newInputStreamResource(entry.getKey().getInputStream()), entry.getValue()); } if (builder.hasErrors()) { throw new RuntimeException(builder.getErrors().toString()); } final KnowledgeBaseConfiguration configuration = KnowledgeBaseFactory.newKnowledgeBaseConfiguration(); knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase(configuration); knowledgeBase.addKnowledgePackages(builder.getKnowledgePackages()); final StatefulKnowledgeSession session = knowledgeBase.newStatefulKnowledgeSession(); for (final Entry<String, Object> entry : proc.getGlobals().entrySet()) { session.setGlobal(entry.getKey(), entry.getValue()); } if (proc.getValidation()) { final ReportFactory reportFactory = new DefaultReportFactory(); session.setGlobal("reportFactory", reportFactory); final ValidationReport report = reportFactory.createValidationReport(); session.setGlobal("validationReport", report); } final WorkItemManager manager = session.getWorkItemManager(); for (final Entry<String, WorkItemHandler> entry : systemHandlers.entrySet()) { manager.registerWorkItemHandler(entry.getKey(), entry.getValue()); } for (final Entry<String, Object> entry : proc.getWorkItems().entrySet()) { final WorkItemHandler wi = (WorkItemHandler) entry.getValue(); manager.registerWorkItemHandler(entry.getKey(), wi); } return session; } public void setKnowledgeBase(final KnowledgeBase knowledgeBase) { this.knowledgeBase = knowledgeBase; } public KnowledgeBase getKnowledgeBase() { return knowledgeBase; } public void setSystemHandlers(final Map<String, WorkItemHandler> systemHandlers) { this.systemHandlers = systemHandlers; } }
3. ReportFactory .java
public interface ReportFactory { ValidationReport createValidationReport(); Message createMessage(Message.Type type, String messageKey, Object... context); }
4. Message .java
public interface Message { public enum Type { ERROR, WARNING } Type getType(); String getMessageKey(); List<Object> getContextOrdered(); }
5. ValidationReport .java
public interface ValidationReport { Set<Message> getMessages(); Set<Message> getMessagesByType(Message.Type type); boolean contains(String messageKey); boolean addMessage(Message message); boolean hasErrors(); boolean hasWarrnings(); }
6. ErrorMessage.java
public class ErrorMessage implements Message { private String messageKey = null; private List<Object> context = null; public ErrorMessage() { } public ErrorMessage(final String key, final List<Object> objs) { messageKey = key; context = objs; } public List<Object> getContextOrdered() { return context; } public String getMessageKey() { return messageKey; } public Type getType() { return Message.Type.ERROR; } @Override public String toString() { return messageKey; } }
7. WarrningMessage .java
public class WarrningMessage implements Message { private String messageKey = null; private List<Object> context = null; public WarrningMessage(String key, List<Object> objs){ messageKey=key; context = objs; } public List<Object> getContextOrdered() { return context; } public String getMessageKey() { return messageKey; } public Type getType() { return Message.Type.WARNING; } }
8. DefaultReportFactory.java
public class DefaultReportFactory implements ReportFactory { public Message createMessage(Type type, String messageKey, Object... context) { if( Type.ERROR.equals(type)) return new ErrorMessage( messageKey, Arrays.asList(context)); if( Type.WARNING.equals(type)) return new WarrningMessage( messageKey, Arrays.asList(context)); throw new IllegalArgumentException( "Wrong Message Type"); } public ValidationReport createValidationReport() { return new ValidationReport(); } }
9.WorkItem
Each work item handler should implement interface org.drools.runtime.process.WorkItemHandler.
SampleWorkItemHandler .java
import java.util.HashMap; import java.util.Map; import org.drools.runtime.process.WorkItem; import org.drools.runtime.process.WorkItemHandler; import org.drools.runtime.process.WorkItemManager; public abstract class SampleWorkItemHandler implements WorkItemHandler { @Override public void abortWorkItem(final WorkItem workItem, final WorkItemManager manager) { // Actions on abort } @Override public void executeWorkItem(final WorkItem workItem, final WorkItemManager manager) { // Method implementation //... //If need to update object in flow protected Map<String, Object> results = new HashMap<String, Object>(); manager.completeWorkItem(workItem.getId(), results); //.. // if not //manager.completeWorkItem(workItem.getId(), null); } }
10. Deployment Context.xml
<bean name="workItemBusinessOperation_1" class="com.company.workflow.workingitems.SampleWorkItemHandler"> <property name="property_wi_1" ref="property_wi_1" /> </bean> <bean name="workItemBusinessOperation_2" class="com.company.workflow.workingitems.OtherWorkItemHandler"> <property name="property_wi_2" ref="property_wi_2" /> </bean> ... <bean name="workflowProcess1" class="com.company.workflow.integration.ProcessDescriptor" scope="prototype"> <constructor-arg> <map> <entry key="classpath:rules/validate1.drl" value="DRL" /> <entry key="classpath:flow/processFlow1.rf" value="DRF" /> </map> </constructor-arg> <property name="workItems"> <map> <entry key="businessOperation_1"> <ref bean="workItemBusinessOperation_1" /> </entry> <entry key="businessOperation_2"> <ref bean="workItemBusinessOperation_2" /> </entry> ... </map> </property> </bean> <bean id="knowledgeSessionLookup" init-method="init" class="com.company.workflow.integration.KnowledgeSessionLookupImpl"> <constructor-arg> <map> <entry key="workflowProcess1"> <ref bean="workflowProcess1" /> </entry> ... </map> </constructor-arg> <property name="systemHandlers"> <map> <entry key="NotificationWorkItemHandler"> <ref bean="NotificationWorkItemHandler"/> </entry> </map> </property> </bean> <bean name="workflowProcessFactory" class="com.company.workflow.integration.WorkflowProcessFactory"> <constructor-arg ref="knowledgeSessionLookup" /> </bean>
—————————————–
See nex post about WorkflowProcessFactory and AOP way to integrate drools into application.
0 Responses
Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.
You must be logged in to post a comment.