Drools Camel server

The Drools Camel server is a module that allows remote knowledge base execution. Camel is an integration framework that is based on known Enterprise Integration Patterns. Drools Camel server supports both, web service (SOAP) and Representational State Transfer (REST) protocols (http://en.wikipedia.org/wiki/Representational_State_Transfer). The communication with the server can be done in JSON (JSON is a lightweight data-interchange format and can be found at http://www.json.org/) or XML formats as well as others.

Interest rate calculation example

The drools-camel module can be found within the droolsjbpm-integration-distribution-5.5.0.Final.zip downloadable artifact. It is meant to be integrated with other applications. To demonstrate its usage, we'll build a simple web application for calculating interest rates. We'll use the decision table from Chapter 5, Creating Human-readable Rules. We'll then build a lightweight client that will communicate with the server through the REST interface. Thanks to REST the client won't need any Drools libraries for rules execution (not even a JVM). To demonstrate this, we'll build it using Ruby language.

The server

The server will consist of a couple of configuration files and will have few dependencies on other artifacts (see the downloadable source that comes with this book for the complete listing).

The drools-camel server is based on two main projects: Apache Camel for routing requests and Apache CXF that provides various protocol and formats support (it is mainly used for web services).

Let's start with web.xml. We'll define a servlet context listener that will initialize the Spring application context on application startup. Next, we'll register a servlet that will handle all requests:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns="http://java.sun.com/xml/ns/javaee"
 xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
 id="WebApp_ID" version="3.0">
  <display-name>camelServer</display-name>
  <context-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  <listener>
    <listener-class>
      org.springframework.web.context.ContextLoaderListener
    </listener-class>
  </listener>
  <servlet>
    <display-name>CXF Servlet</display-name>
    <servlet-name>CXFServlet</servlet-name>
    <servlet-class>
      org.apache.cxf.transport.servlet.CXFServlet
    </servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>CXFServlet</servlet-name>
    <url-pattern>/kservice/*</url-pattern>
  </servlet-mapping>
</web-app>

Code listing 7: The camelServer web application setup (the web.xml file)

As can be seen, all requests will be handled under the /kservice/* URL. Next comes the applicationContext.xml Spring file where we'll define the knowledge base and a session:

<drools:grid-node id="node1"/>

<drools:kbase id="interestCalculationKB" node="node1">
  <drools:resources>
    <drools:resource type="DTABLE"
      source="classpath:interest calculation.xls">      
      <drools:decisiontable-conf input-type="XLS"
        worksheet-name="Sheet1" />
    </drools:resource>
  </drools:resources>  
</drools:kbase>

<drools:ksession id="interestCalcSession" type="stateless"
  kbase="interestCalculationKB" node="node1"/>

<import resource="classpath:camel-server.xml" />

Code listing 8: Excerpt of the Spring setup file – knowledge base setup (the applicationContext.xml file)

We'll ignore the grid-node declaration. It is mandatory but it is used for grid deployments that won't be covered here. Note the use of the drools:decisiontable-conf element. As we know decision tables require a bit more configuration than other resources, for example, we need to specify the type if it is an XLS-based or CSV-based decision table. In the example we just saw, but we're also specifying the name of the worksheet that we want to use.

The import file camel-server.xml defines routes that make interestCalcSession available as a REST service:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:cxf="http://camel.apache.org/schema/cxf"
       xmlns:jaxrs="http://cxf.apache.org/jaxrs"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://camel.apache.org/schema/cxf http://camel.apache.org/schema/cxf/camel-cxf.xsd
       http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
    ">
  <import resource="classpath:META-INF/cxf/cxf.xml" />
  <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
  
  <cxf:rsServer id="rsServer" address="/rest"
     serviceClass="org.drools.jax.rs.CommandExecutorImpl">
       <cxf:providers>
           <bean class="org.drools.jax.rs.CommandMessageBodyReader"/>
       </cxf:providers>
  </cxf:rsServer>  
  
  <bean id="droolsPolicy"
    class="org.drools.camel.component.DroolsPolicy" />  
    
  <camelContext id="camel"
    xmlns="http://camel.apache.org/schema/spring">    
    <route>
       <from uri="cxfrs://bean://rsServer"/>
       <policy ref="droolsPolicy">
         <unmarshal ref="json" />
         <to uri="drools:node1/interestCalcSession" />
         <marshal ref="json" />
       </policy>
    </route>
  </camelContext>  
</beans>

Code listing 9: Route setup (the camelServer.xml file)

Firstly, we're importing some standard CXF configuration files that are needed. Then we set up the server endpoint, rsServer, under the rest path. The server is implemented by the CommandExecutorImpl class, which is part of the drools-camel module. The droolsPolicy bean is there to make Apache Camel Drools aware. Finally, a route is defined that takes requests from the rsServer endpoint, un-marshals it with json un-marsheller, and passes it into the interestCalcSession session; the response is then marshaled with json and sent back as a response to the client.

When we deploy this application and connect to it (http://localhost:8080/camelServer/kservice/), we should see a web page like the following screenshot:

The server

Figure 2: Drools camel server running

As you can see in the screenshot, the server is informing us that it is ready to process requests.

The client

Our client will query for an interest rate on a student account with a balance of 1,000 EUR. Since this client will be very lightweight, a dynamic, interpreted language such as Ruby (for more information about Ruby please refer to http://www.ruby-lang.org/en/) is ideal for this kind of task. Please consult Ruby's manual about how to install this interpreter. We'll also require an additional library for Ruby called json that will allow us to communicate using the JSON protocol.

The full client source code is as follows:

require 'net/http'
require 'json'
http = Net::HTTP.new('localhost', 8080)
path = "/camelServer/kservice/rest/execute"
headers = {
  "Content-Type" => "text/plain"
}
post_data = {"batch-execution" => {                     
  "commands" => [
    {
      "insert" => {
        "out-identifier" => "account",
        "return-object" => true,
        "object" => {
          "droolsbook.decisiontables.bank.model.Account" => {
            "type" => "STUDENT",
            "balance" => "1000",
            "currency" => "EUR"
          }  
        }
      }
    },
    {
      "fire-all-rules" => ""
    }
  ]  
}}
resp, data = http.post(path, post_data.to_json, headers)
answer = JSON.parse(data)
puts answer["execution-results"]["results"]["result"]["value"]["droolsbook.decisiontables.bank.model.Account"]["interestRate"]

Code listing 10: Ruby client (the interest_request.rb file)

The first few lines until post_data ... are straightforward. We'll be connecting to the server at the specified URL and setting some headers. The post_data hash data structure holds the request information that we'll be sending. There are two commands. The first is an insert command of a fact named as account of class droolsbook.decisiontables.bank.model.Account. Its type property is set to STUDENT, balance to 1000, and currency to EUR. Please note that Account.currency is of type String. The type property is an enum and balance is BigDecimal. The second command is a fireAllRules command. Note that this command is optional since we're dealing with a stateless session.

The next line with .. http.post .. sends this request to the server and waits for the response. Note that the data within the post_data variable are converted to JSON format by performing post_data.to_json. The response is stored within the data variable. Since it is in a JSON format, we have to convert it into a Ruby structure by performing JSON.parse(data). Finally, the client displays calculated interestRate by performing puts answer["execution-results"]["results"]["result"]["value"]["droolsbook.decisiontables.bank.model.Account"]["interestRate"]. The response contains execution-results, which is an array of results; these are our commands that we've sent in that had out-identifier set. The structure of these out commands is the same as the structure of input commands. Please see the Drools documentation for the full listing of available commands.

We can run this client with the ruby interest_request.rb script. The output should be 1.00, which is the correct interest rate for this account.

When the server receives the request, it must create facts/global objects from it that can be inserted into the knowledge session. After the rules are executed, it needs to convert these facts/globals into JSON/XML format. Drools Camel server uses the XStream library to perform this marshaling/un-marshaling. As we've seen that it has no problem with types such as String, enum, or BigDecimal. This library can even deal with much more complex custom data structures. Please consult its manual for more information at http://xstream.codehaus.org/. For example, if we'd like to set the startDate and endDate values that are of type DateMidnight, for our account fact, we'd have to map them as complex objects together with specifying their type. I've found quite useful the XStream ability to convert ordinary objects to JSON or XML and print the result out. It'll give you an idea about how to represent even more complex objects.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.23.127.197