{"id":146,"date":"2010-05-19T17:31:48","date_gmt":"2010-05-19T15:31:48","guid":{"rendered":"http:\/\/touk.pl\/blog\/?p=146"},"modified":"2023-03-20T12:44:21","modified_gmt":"2023-03-20T11:44:21","slug":"introducing-camel-drools-component","status":"publish","type":"post","link":"https:\/\/touk.pl\/blog\/2010\/05\/19\/introducing-camel-drools-component\/","title":{"rendered":"Introducing camel-drools component"},"content":{"rendered":"<h2 id=\"introduction-in-this-post-ill-try-to-introduce\">Introduction In this post I\u2019ll try to introduce<\/h2>\n<p><a href=\"http:\/\/camel.apache.org\">Apache Camel<\/a> component for <a href=\"http:\/\/www.jboss.org\/drools\/\">Drools<\/a> library \u2013 a great an widely used Business Rules Management System. When we decided to use Drools 5 inside Servicemix for some big project, it turned out that there is no production-ready solution that will meet out requirements. The servicemix-drools component is lacking several very important features, eg:<br \/>\n* StatefulSession database persistence for long-running processes,<br \/>\n* support for <a href=\"http:\/\/en.wikipedia.org\/wiki\/Complex_Event_Processing\">Complex Event Processing<\/a> (event-based rules),<br \/>\n* Apache Camel based deployment to ease rules consequence processing,<br \/>\n* Support for Drools unit testing framework. To satisfy those requirements, Maciek Pr\u00f3chniak created a set of utility classes, which helped us run Drools inside Camel route. Starting from this codebase, we did some refactoring, add few new features (eg. pluggable object persistance) and released camel-drools component on TouK Open Source Projects forge.<\/p>\n<h2 id=\"example-to-summarize-key-features-and-show-how-to-use-camel-drools-component-lets-try-to-implement-an-example-taken-from-drools-flow-documentation\">Example To summarize key features and show how to use camel-drools component, let\u2019s try to implement an example taken from Drools Flow <a href=\"https:\/\/www.drools.org\/learn\/documentation.html\">documentation<\/a>:<\/h2>\n<p>There is kind-of \u2018process\u2019 where first Task1 and Task2 are created and can be executed in parallel. Task3 needs to be executed after completion of both Task1 and Task2.<\/p>\n<h2 id=\"implementation-the-class-task-have-2-fields-a\">Implementation The class Task have 2 fields, a<\/h2>\n<p><code>name<\/code> and <code>completed<\/code> flag, we also need an <code>id<\/code> for session serialization:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\">public class Task implements Serializable {\r\n    private static final long serialVersionUID = -2964477958089238715L;    \r\n    private String name;\r\n    private boolean completed;\r\n\r\n    public Task(String name) {\r\n        this(name, false);\r\n    }\r\n\r\n    public Task(String name, boolean completed) {\r\n        this.name = name;\r\n        this.completed = completed;\r\n    }\r\n\r\n    public String getName() {\r\n        return name;\r\n    }\r\n\r\n    public boolean isCompleted() {\r\n        return completed;\r\n    }\r\n\r\n    public long getId() {\r\n        return name.hashCode();\r\n    }\r\n}<\/pre>\n<p>We also define another class representing the state of process, needed to fire rules in correct order. Using that model, we now can implement our ruleset, defined in<\/p>\n<p><em>task.drl<\/em> file:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">import org.apache.camel.component.drools.stateful.*\r\n\r\nglobal org.apache.camel.component.drools.CamelDroolsHelper helper\r\n\r\nrule \"init\"\r\nsalience 100\r\n    when\r\n        $s : State(name==\"start\")\r\n    then\r\n        insert(new Task(\"Task1\"));\r\n        insert(new Task(\"Task2\"));\r\n        retract($s);\r\nend\r\n\r\nrule \"all tasks completed\"\r\n    when\r\n        not(exists Task(completed==false))\r\n        not(exists State(name==\"end\"))\r\n    then\r\n        insert(new Task(\"Task3\"));\r\nend\r\n\r\nrule \"Task3 completed\"\r\nsalience 30\r\n    when \r\n        Task(name==\"Task3\", completed==true)\r\n    then\r\n        insert(new State(\"end\"));\r\n        helper.send(\"direct:completed\", \"completed\");\r\nend<\/pre>\n<p>In first rule \u2013 \u201cinit\u201d we insert two tasks and then retract state object from the session to avoid recursive execution of that rule. Rule \u201call tasks completed\u201d shows the power of Drools \u2013 we just declare that this rule is fired when \u201cthere are no incompleted tasks\u201d and don\u2019t have to specify which tasks. So this shows rather \u2018declarative\u2019 than \u2018imperative\u2019 way of development \u2013 we have much more expressiveness than just step-by-step actions which lead to some situation. The<\/p>\n<p><code>CamelDroolsHelper<\/code> is a wrapper for <code>ProducerTemplate<\/code> and can be used to send some message trough another Camel route as consequence of a rule. But how are Tasks mark as completed in Drools session? The idea is to expose session through Camel endpoint to allow insert or update objects, which are passed as body of exchanges:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\">public class TaskRouteBuilder extends RouteBuilder {\r\n    @Override\r\n    public void configure() throws Exception {\r\n        from(\"direct:drools\")\r\n            .setHeader(\"drools.key\", constant(new MultiKey(new String[] {\r\n                \"process-1\"\r\n            })))\r\n            .to(\"drools:task.drl?stateful=true\");\r\n        from(\"direct:completed\").to(\"log:test\");\r\n    }\r\n}<\/pre>\n<p>The Drools endpoint is described by<\/p>\n<p><code>\"drools:task.drl?stateful=true\"<\/code> URI. It loads definition of rules from <em>task.drl<\/em> file and runs endpoint in <em>stateful<\/em> mode (described next paragraph). When object is passed to this endpoint, it is inserted (or updated) to session and <code>fireAllRules()<\/code> method is called. Another important thing is \u201cdrools.key\u201d header \u2013 it is used to distinguish sessions between \u201cprocesses\u201d. E.g. when we have some customer-oriented rules, we want to group facts and events in session per customer \u2013 by some customer id. When the \u201cdrools.key\u201d is set to that id, sessions for different customers could be found and saved separately.<\/p>\n<h3 id=\"stateful-session-persistence-camel-drools-component-can-be-used-in-two-modes\">Stateful session persistence Camel-drools component can be used in two modes:<\/h3>\n<p><em>stateful *and *stateless<\/em>. The main difference between those is session persistence \u2013 only in stateful mode session is stored in database. So long duration event rules are correctly handled only in this mode \u2013 and this is what we used in this example. Let\u2019s look at Spring context definition:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">task_id<\/pre>\n<p>As you can see, there are some requirements for database objects to handle session persistence correctly \u2013 two tables: one for KnowlegdeStatefulSession and one for objects (facts and events) persistence. You can name them freely, just provide those names to<\/p>\n<p><code>sessionTable<\/code> and <code>objectTable<\/code> properties of sessionDAO. A sequence for id generation is also needed.<\/p>\n<h3 id=\"route-and-rules-testing-here-is-example-test-for-taskroutebuilder\">Route and rules testing Here is example test for TaskRouteBuilder:<\/h3>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\">@SuppressWarnings(\"unchecked\")\r\npublic class TaskRouteBuilderTest extends TaskRouteBuilder {\r\n\r\n    DefaultCamelContext ctx;\r\n    ProducerTemplate tpl;\r\n    MockSessionDAO dao;\r\n\r\n    @Before\r\n    public void makeContext() throws Exception {\r\n        ctx = new DefaultCamelContext();\r\n        ctx.addComponent(\"drools\", new DroolsComponent(ctx));\r\n        ApplicationContext appCtx = new ClassPathXmlApplicationContext(\r\n            new String[] {\r\n                \"camel-drools-context.xml\",\r\n                \"mock-dao-context.xml\"\r\n            });\r\n        dao = (MockSessionDAO) appCtx.getBean(\"sessionDAO\");\r\n        ctx.setRegistry(new ApplicationContextRegistry(appCtx));\r\n        ctx.addRoutes(this);\r\n        ctx.addRoutes(new RouteBuilder() {\r\n            @Override\r\n            public void configure() throws Exception {\r\n                from(\"direct:completed\").to(\"mock:test\");\r\n            }\r\n        });\r\n        ctx.start();\r\n        tpl = ctx.createProducerTemplate();\r\n    }\r\n\r\n    @Test\r\n    public void testUpdate() throws Exception {\r\n        Endpoint endpoint = ctx.getEndpoint(\"direct:drools\");\r\n        tpl.requestBody(endpoint, new State(\"start\"));\r\n        SessionWithIdentifier session = dao.getSession();\r\n        Assert.assertEquals(2, session.getSession().getFactHandles().size());\r\n        tpl.requestBody(endpoint, new Task(\"Task1\", true));\r\n        tpl.requestBody(endpoint, new Task(\"Task2\", true));\r\n        Assert.assertEquals(3, session.getSession().getFactHandles().size());\r\n        tpl.requestBody(endpoint, new Task(\"Task3\", true));\r\n\r\n        MockEndpoint mock = MockEndpoint.resolve(ctx, \"mock:test\");\r\n        mock.expectedMessageCount(1);\r\n        mock.setResultWaitTime(5000 L);\r\n        mock.assertIsSatisfied();\r\n    }\r\n}<\/pre>\n<p>In setup method some required initialization is done \u2013 camel-drools-context.xml file is loaded and MockSessionDao created. The test first starts process by passing State object with \u201cstart\u201d name to Drools session through Camel route. This should add Task1 and Task2 to session \u2013 and it\u2019s tested by counting the factHadles in session. Next, Task1 and Task2 are updated by making them completed, which should result in Task3 present in session \u2013 another factHandle. Last step is to complete Task3 and check that last rule is executed by assertions on MockEndpoint. You can download source code for this example and whole component from<\/p>\n<p><a href=\"http:\/\/top.touk.pl\/svn\/camel-components\/camel-drools\/branches\/camel-1.x\/\">here<\/a> \u2013 this is branch for Camel 1.x version, which we use in our project.<\/p>\n","protected":false},"excerpt":{"rendered":"Introduction In this post I\u2019ll try to introduce Apache Camel component for Drools library \u2013 a great an&hellip;\n","protected":false},"author":5,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11],"tags":[68],"class_list":{"0":"post-146","1":"post","2":"type-post","3":"status-publish","4":"format-standard","6":"category-development-design","7":"tag-java"},"_links":{"self":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts\/146","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/comments?post=146"}],"version-history":[{"count":51,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts\/146\/revisions"}],"predecessor-version":[{"id":15343,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts\/146\/revisions\/15343"}],"wp:attachment":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/media?parent=146"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/categories?post=146"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/tags?post=146"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}