Drools Integration Modules: Spring Framework and Apache Camel

0
160
14 min read

(For more resources on Drools, see here.)

Setting up Drools using Spring Framework

In this recipe, you will see how to configure the Drools business rules engine using the Spring framework, using the integration module specially created to configure the Drools beans with XML.

How to do it…

Carry out the following steps in order to configure a Drools project using the Spring Framework integration:

  1. Add the following dependency in your Maven project by adding this XML code snippet in the pom.xml file:

    <dependency> <groupId>org.drools</groupId> <artifactId>drools-spring</artifactId> <version>5.2.0.Final</version> </dependency>

  2. Once the drools-spring module and the Spring Framework dependencies are added into your project, it’s time to write the rules that are going to be included in the knowledge base:

    package drools.cookbook.chapter07 import drools.cookbook.chapter07.model.Server import drools.cookbook.chapter07.model.Virtualization rule "check minimum server configuration" dialect "mvel" when $server : Server(processors < 2 || memory<=1024 || diskSpace<=250) then System.out.println("Server "" + $server.name + "" was rejected by don't apply the minimum configuration."); retract($server); end rule "check available server for a new virtualization" dialect "mvel" when $virtualization : Virtualization($virtMemory : memory, $virtDiskSpace : diskSpace) $server : Server($memory : memory, $diskSpace : diskSpace, virtualizations !=null) Number((intValue + $virtMemory) < $memory) from accumulate (Virtualization($vmemory : memory) from $server.virtualizations, sum($vmemory)) Number((intValue + $virtDiskSpace) < $diskSpace) from accumulate(Virtualization($vdiskSpace : diskSpace) from $server.virtualizations, sum($vdiskSpace)) then $server.addVirtualization($virtualization); retract($virtualization); end

  3. Then a Spring Application Context XML file has to be created to configure the Drools beans with the following code

    <?xml version="1.0" encoding="UTF-8"?> <beans xsi_schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://drools.org/schema/drools-spring org/drools/container/spring/drools-spring-1.2.0.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd"> <drools:grid-node id="node1" /> <drools:resource id="resource1" type="DRL" source="classpath:drools/cookbook/chapter07/rules.drl" /> <drools:kbase id="kbase1" node="node1"> <drools:resources> <drools:resource ref="resource1" /> </drools:resources> </drools:kbase> <drools:ksession id="ksession1" type="stateful" kbase="kbase1" node="node1" /> <drools:ksession id="ksession2" type="stateless" kbase="kbase1" node="node1" /> </beans>

  4. After all these three steps, you are ready to load the XML file using the Spring Framework API and obtain the instantiated beans to interact with the knowledge sessions:

    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); applicationContext.start(); StatefulKnowledgeSession ksession1= (StatefulKnowledgeSession)applicationContext .getBean("ksession1"); Server debianServer = new Server("debian-1", 4, 2048, 250, 0); ksession1.insert(debianServer); ksession1.fireAllRules(); applicationContext.stop();>

  5. In your Maven project, add the following dependencies in the pom.xml file to use JPA persistence using the Spring Framework:

    public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPath mlApplicationContext("applicationContext.xml"); applicationContext.start(); StatefulKnowledgeSession ksession1 = (StatefulKnowledgeSession) applicationContext.getBean("ksession1"); Server debianServer = new Server("debian-1", 4, 2048, 250, 0); ksession1.insert(debianServer); ksession1.fireAllRules(); applicationContext.stop(); }

  6. Implement the java.io.Serializable interface to the objects of your domain model that will be persisted.
  7. Create a persistence.xml file inside the resources/META-INF folder to configure the persistence unit. In this recipe, we will use an embedded H2 database for testing purposes, but you can configure it for any relational database engine:

    <?xml version="1.0" encoding="UTF-8"?> <persistence version="1.0" xsi_schemaLocation="http://java.sun.com/xml/ns/persistence http: java.sun.com/xml/ns/persistence/persistence_1_0.xsd http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/ xml/ns/persistence/orm_1_0.xsd"> <persistence-unit name="drools.cookbook.spring.jpa" transaction- type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <class>org.drools.persistence.info.SessionInfo</class> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" /> <property name="hibernate.max_fetch_depth" value="3" /> <property name="hibernate.hbm2ddl.auto" value="create" /> <property name="hibernate.show_sql" value="false" /> </properties> </persistence-unit> </persistence>

  8. Now, we have to create an XML file named applicationContext.xml in the resources folder, in which we are going to define the beans needed to configure the JPA persistence and the Drools beans:

    <?xml version="1.0" encoding="UTF-8"?> <beans xsi_schemaLocation="http://www.springframework.org/schema/ beans http://www.springframework.org/schema/beans/spring- beans-2.0.xsdhttp://drools.org/schema/drools-spring org/drools/ container/spring/drools-spring-1.2.0.xsd http://camel.apache.org/schema/spring http://camel.apache.org/ schema/spring/camel-spring.xsd"> <bean id="dataSource" class="org.springframework.jdbc.datasource. DriverManagerDataSource"> <property name="driverClassName" value="org.h2.Driver" /> <property name="url" value="jdbc:h2:tcp://localhost/Drools" /> <property name="username" value="sa" /> <property name="password" value="" /> </bean> <bean id="entityManagerFactory" class="org.springframework.orm. jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="persistenceUnitName" value="drools.cookbook. spring.jpa" /> </bean> <bean id="txManager" class="org.springframework.orm.jpa. JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <drools:grid-node id="node1" /> <drools:kstore id="kstore1" /> <drools:resource id="resource1" type="DRL" source="classpath:drools/cookbook/chapter07/rules.drl" /> <drools:kbase id="kbase1" node="node1"> <drools:resources> <drools:resource ref="resource1" /> </drools:resources> </drools:kbase> <drools:ksession id="ksession1" type="stateful" kbase="kbase1" node="node1"> <drools:configuration> <drools:jpa-persistence> <drools:transaction-manager ref="txManager" /> <drools:entity-manager-factory ref="entityManagerFactory" /> </drools:jpa-persistence> </drools:configuration> </drools:ksession> </beans>

  9. Finally, we have to write the following code in a new Java class file, or in an existing one, in order to interact with the Stateful knowledge session and persist this state into the H2 database without further actions:

    public void startApplicationContext() { ClassPathXmlApplicationContext applicationContext =newClassPathXmlApplicationContext("/applicationContext.xml"); applicationContext.start(); StatefulKnowledgeSession ksession1 = (StatefulKnowledgeSession) applicationContext.getBean("ksession1"); int sessionId = ksession1.getId(); Server debianServer = new Server("debianServer", 4, 2048, 1222, 0); ksession1.insert(debianServer); ksession1.fireAllRules(); ksession1.dispose(); Environment env = KnowledgeBaseFactory.newEnvironment(); env.set(EnvironmentName.ENTITY_MANAGER_FACTORY, applicationContext.getBean("entityManagerFactory")); env.set(EnvironmentName.TRANSACTION_MANAGER, applicationContext.getBean("txManager")); Virtualization virtualization = new Virtualization( "dev","debian", 512, 30); KnowledgeStoreService kstore = (KnowledgeStoreService) applicationContext.getBean("kstore1"); KnowledgeBase kbase1 = (KnowledgeBase)applicationContext. getBean("kbase1"); ksession1 = kstore .loadStatefulKnowledgeSession(sessionId, kbase1, null, env); ksession1.insert(virtualization); ksession1.fireAllRules(); applicationContext.stop(); }

How it works…

In order to use the Spring Framework integration in your project, First you have to add the drools-spring module to it. In a Maven project, you can do it by adding the following code snippet in your pom.xml file:

<dependency> <artifactId>org.drools</artifactId> <groupId>drools-spring</groupId> <version>5.2.0.Final</version> </dependency>

This dependency will transitively include the required Spring Framework libraries in the Maven dependencies. Currently, the integration is done using the 2.5.6 version, but it should work with the newest version as well.

Now, we are going to skip the rule authoring step because it’s a very common task and you really should know how to do it at this point, and we are going to move forward to the beans configuration.

As you know, the Spring Framework configuration is done through an XML file where the beans are defined and injected between them, and to make Drools declaration easy the integration module provides a schema and custom parsers. Before starting the bean configuration, the schema must be added into the XML namespace declaration, otherwise the Spring XML Bean Definition Reader is not going to recognize the Drools tags and some exceptions will be thrown. In the following code lines, you can see the namespace declarations that are needed before you start writing the bean definitions:

<?xml version="1.0" encoding="UTF-8"?> <beans xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://drools.org/schema/drools-spring org/drools/container/spring/drools-spring-1.2.0.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd"> <!—- define your beans here --> </beans>

After this, the drools beans can be added inside the XML configuration file using the friendly tags:

<drools: /> tags: <?xml version="1.0" encoding="UTF-8"?> <beans xsi_schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://drools.org/schema/drools-spring org/drools/container/spring/drools-spring-1.2.0.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd"> <drools:grid-node id="node1" /> <drools:resource id="resource1" type="DRL" source="classpath:drools/cookbook/chapter07/rules.drl" /> <drools:kbase id="kbase1" node="node1"> <drools:resources> <drools:resource ref="resource1" /> </drools:resources> </drools:kbase> <drools:ksession id="ksession1" type="stateful" kbase="kbase1" node="node1" /> </beans>

As you can see, there is only one stateful knowledge session bean configured by using the tag with a ksession1 ID. This ksession1 bean was injected with a knowledge base and a grid node so that the Drools Spring beans factories, which are provided by the integration module, can instantiate it.

Once the drools beans are configured, it’s time to instantiate them using the Spring Framework API, as you usually do:

public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPath mlApplicationContext("applicationContext.xml"); applicationContext.start(); StatefulKnowledgeSession ksession1 = (StatefulKnowledgeSession) applicationContext.getBean("ksession1"); Server debianServer = new Server("debian-1", 4, 2048, 250, 0); ksession1.insert(debianServer); ksession1.fireAllRules(); applicationContext.stop(); }

In the Java main method, a ClassPathXmlApplicationContext object instance is used to load the bean definitions, and once they are successfully instantiated they are available to be obtained using the getBean(beanId) method . At this point, the Drools beans are instantiated and you can start interacting with them as usual by just obtaining their references.

As you saw in this recipe, the Spring framework integration provided by Drools is pretty straightforward and allows the creation of a complete integration, thanks to its custom tags and simple configuration.

See also

For more information about the Drools bean definitions, read the Spring Integration in the official documentation available at http://www.jboss.org/drools/documentation.

Configuring JPA to persist our knowledge with Spring Framework

How to do it…

Carry out the following steps in order to configure the Drools JPA persistence using the Spring module integration:

How it works…

Before we start declaring the beans that are needed to persist the knowledge using JPA, we have to add some dependencies into our project configuration, especially the ones used by the Spring Framework. These dependencies were already described in the first step of the previous section, so we can safely continue with the remaining steps.

Once the dependencies are added into the project, we have to implement the java.io.Serializable interface in the classes of our domain model that will be persisted.

After this, we have to create a persistence unit configuration by using the default persistence.xml file located in the resources/META-INF directory of our project. This persistence unit is named drools.cookbook.spring.jpa and uses the Hibernate JPA implementation. Also, it is configured to use an H2 Java database, but in your real environment, you should supply the appropriate configuration. Next, you will see the persistence unit example, with the annotated SessionInfo entity that will be used to store the session data, which is ready to be used with Drools:

<?xml version="1.0" encoding="UTF-8"?> <persistence version="1.0" xsi_schemaLocation="http://java.sun.com/xml/ns/persistence http:// java.sun.com/xml/ns/persistence/persistence_1_0.xsd http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/ persistence/orm_1_0.xsd"> <persistence-unit name="drools.cookbook.spring.jpa" transaction- type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <class>org.drools.persistence.info.SessionInfo</class> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" /> <property name="hibernate.max_fetch_depth" value="3" /> <property name="hibernate.hbm2ddl.auto" value="create" /> <property name="hibernate.show_sql" value="false" /> </properties> </persistence-unit> </persistence>

Now, we are ready to declare the beans that are needed to enable the JPA persistence with an XML file, where the most important section is the declaration of the Spring DriverManagerDataSource and LocalContainerEntityManagerFactoryBean beans , which are very descriptive and can be configured with the parameters of your data engine. Also, one of the most important declarations is the KnowledgeStoreService bean, using the tag, that will be primarily used to load the persistence knowledge session:

<?xml version="1.0" encoding="UTF-8"?> <beans xsi_schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://drools.org/schema/drools-spring org/drools/container/spring/ drools-spring-1.2.0.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/ spring/camel-spring.xsd"> <bean id="dataSource" class="org.springframework.jdbc.datasource. DriverManagerDataSource"> <property name="driverClassName"value="org.h2.Driver" /> <property name="url"value="jdbc:h2:tcp://localhost/Drools" /> <property name="username" value="sa" /> <property name="password" value="" /> </bean> <bean id="entityManagerFactory" class="org.springframework.orm.jpa. LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="persistenceUnitName" value="drools.cookbook.spring.jpa" /> </bean> <bean id="txManager" class="org.springframework.orm.jpa. JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <drools:grid-node id="node1" /> <drools:kstore id="kstore1" /> <drools:resource id="resource1" type="DRL" source="classpath:drools/ cookbook/chapter07/rules.drl" /> <drools:kbase id="kbase1" node="node1"> <drools:resources> <drools:resource ref="resource1" /> </drools:resources> </drools:kbase> <drools:ksession id="ksession1" type="stateful" kbase="kbase1" node="node1"> <drools:configuration> <drools:jpa-persistence> <drools:transaction-manager ref="txManager" /> <drools:entity-manager-factory ref="entityManagerFactory" /> </drools:jpa-persistence> </drools:configuration> </drools:ksession> </beans>

After the bean definitions, we can start writing the Java code needed to initialize the Spring Framework application context and interact with the defined beans. After loading the application context by using a ClassPathXmlApplicationContext object, we have to obtain the stateful knowledge session to insert the facts into the working memory, and also obtain the ID of the knowledge session to recover it later:

ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("/applicationContext.xml"); applicationContext.start(); StatefulKnowledgeSession ksession1 = (StatefulKnowledgeSession) applicationContext.getBean("ksession1"); int sessionId = ksession1.getId(); Server debianServer = new Server("debianServer", 4, 2048, 1222, 0); ksession1.insert(debianServer); ksession1.fireAllRules(); ksession1.dispose();

Once we are done interacting with the knowledge session and inserting facts, firing the rules, and so on, these can be disposed. They can be restored later using the KnowledgeStoreService bean , but we have to create a new org.drools.runtime.Environment object to set the EntityManager and TransactionManager used in the persistence process before trying to load the persisted knowledge session. The org.drools.runtime.Environment object can be created as follows:

Environment env = KnowledgeBaseFactory.newEnvironment(); env.set(EnvironmentName.ENTITY_MANAGER_FACTORY, applicationContext.getBean("entityManagerFactory")); env.set(EnvironmentName.TRANSACTION_MANAGER, applicationContext.getBean("txManager")); Virtualization virtualization = new Virtualization("dev", "debian", 512, 30);

Finally, with the Environment object created, we can obtain the KnowledgeStoreService bean together with the KnowledgeSession bean and the StatefulKnowledgeSession ID to load the stored state and start to interact with it as we do usually:

KnowledgeStoreService kstore = (KnowledgeStoreService) applicationContext.getBean("kstore1"); KnowledgeBase kbase1 = (KnowledgeBase) applicationContext. getBean("kbase1"); ksession1 = kstore.loadStatefulKnowledgeSession(sessionId, kbase1, null, env); ksession1.insert(virtualization); ksession1.fireAllRules(); applicationContext.stop();

As you saw in this recipe, the knowledge session persistence is totally transparent to the user and automatic without any extra steps to save the state. By following these steps you can easily integrate JPA persistence using Hibernate, or any other vendor’s JPA implementation, in order to save the current state of the knowledge session using the Spring Framework Integration.

LEAVE A REPLY

Please enter your comment!
Please enter your name here