Sunday, October 12, 2014

Drools 6 integration with Spring MVC 4- Part 1

Drools is great, Drools 6 is even better but its sad that there are very little documentation and tutorials when it comes to integration of Drools 6 with the most used java MVC framework, Spring.

So here it goes Drools 6.1 integration with Spring 4



The example project source code can be found here on Github

Part 1

We will be using the fire alarm drools example found here for this demo. We will build a Spring MVC 4 application, configured completely with JavaConfig, to monitor fire alarms and to control sprinklers for configured rooms. We will use Drools to write the rule logic for the application.

Note: Basic knowledge of Spring MVC and Drools is expected to follow the below.

1. Pom.xml - Maven config

Lets setup the project first, Here is the maven configuration for the project, refer the full pom.xml in the downloaded source code.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>com.technorage</groupId>
 <artifactId>demo</artifactId>
 <name>Demo</name>
 <version>1.0.0-SNAPSHOT</version>
 <packaging>war</packaging>
 <description>Drools Demo Project.</description>
 <properties>
  <!-- Generic properties -->
  <java.version>1.7</java.version>
  <!-- Web -->
  <jsp.version>2.2</jsp.version>
  <jstl.version>1.2</jstl.version>
  <servlet.version>3.0.1</servlet.version>
  <!-- Spring -->
  <spring-framework.version>4.0.0.RELEASE</spring-framework.version>
  <!-- Drools -->
  <drools.version>6.1.0.Final</drools.version>
  <!-- Logging -->
  <logback.version>1.0.13</logback.version>
  <slf4j.version>1.7.5</slf4j.version>
  <!-- Test -->
  <junit.version>4.11</junit.version>
 </properties>
 <dependencies>
  <!-- Custom drools-tools jar -->
  <dependency>
   <groupId>com.technorage</groupId>
   <artifactId>drools-tools</artifactId>
   <version>1.0.0-SNAPSHOT</version>
  </dependency>
  <!-- Spring MVC -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>${spring-framework.version}</version>
  </dependency>
  <!-- Other Web dependencies -->
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>jstl</artifactId>
   <version>${jstl.version}</version>
  </dependency>
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>javax.servlet-api</artifactId>
   <version>${servlet.version}</version>
   <scope>provided</scope>
  </dependency>
  <dependency>
   <groupId>javax.servlet.jsp</groupId>
   <artifactId>jsp-api</artifactId>
   <version>${jsp.version}</version>
   <scope>provided</scope>
  </dependency>
  <dependency>
   <groupId>javax</groupId>
   <artifactId>javaee-api</artifactId>
   <version>7.0</version>
  </dependency>
  <dependency>
   <groupId>commons-io</groupId>
   <artifactId>commons-io</artifactId>
   <version>2.4</version>
  </dependency>
  <dependency>
   <groupId>commons-beanutils</groupId>
   <artifactId>commons-beanutils</artifactId>
   <version>1.8.3</version>
  </dependency>
  <dependency>
   <groupId>commons-httpclient</groupId>
   <artifactId>commons-httpclient</artifactId>
   <version>3.1</version>
  </dependency>
  <dependency>
   <groupId>commons-codec</groupId>
   <artifactId>commons-codec</artifactId>
   <version>1.8</version>
  </dependency>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-validator</artifactId>
   <version>5.0.3.Final</version>
  </dependency>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-validator-annotation-processor</artifactId>
   <version>5.0.3.Final</version>
  </dependency>
  <!-- Spring and Transactions -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-tx</artifactId>
   <version>${spring-framework.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-expression</artifactId>
   <version>${spring-framework.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-beans</artifactId>
   <version>${spring-framework.version}</version>
  </dependency>
  <!-- Drools -->
  <dependency>
   <groupId>org.drools</groupId>
   <artifactId>drools-core</artifactId>
   <version>${drools.version}</version>
  </dependency>
  <dependency>
   <groupId>org.drools</groupId>
   <artifactId>drools-compiler</artifactId>
   <version>${drools.version}</version>
  </dependency>
  <!-- Logging with SLF4J & LogBack -->
  <dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-api</artifactId>
   <version>${slf4j.version}</version>
   <scope>compile</scope>
  </dependency>
  <dependency>
   <groupId>ch.qos.logback</groupId>
   <artifactId>logback-classic</artifactId>
   <version>${logback.version}</version>
   <scope>runtime</scope>
  </dependency>
  <!-- Test Artifacts -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-test</artifactId>
   <version>${spring-framework.version}</version>
   <scope>test</scope>
  </dependency>
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>${junit.version}</version>
   <scope>test</scope>
  </dependency>
 </dependencies>
 <build>
  <resources>
 ...
  </resources>
  <plugins>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-install-plugin</artifactId>
    <version>2.4</version>
    <executions>
     <execution>
      <phase>initialize</phase>
      <goals>
       <goal>install-file</goal>
      </goals>
      <configuration>
       <groupId>com.technorage</groupId>
       <artifactId>drools-tools</artifactId>
       <version>1.0.0-SNAPSHOT</version>
       <packaging>jar</packaging>
       <file>${basedir}/lib/drools-tools-1.0.0-SNAPSHOT.jar</file>
      </configuration>
     </execution>
    </executions>
   </plugin>
   ... 
  </plugins>
  <pluginManagement>
  ....
  </pluginManagement>
 </build>
</project>



We are using a custom utility jar in the lib folder for drools configuration (Source for the same can be found in the Github repo here). In the pom.xml notice how we include it as dependency after installing it using maven-install-plugin in the plugins section of the POM

<dependencies>
  <!-- Custom drools-tools jar -->
  <dependency>
   <groupId>com.technorage</groupId>
   <artifactId>drools-tools</artifactId>
   <version>1.0.0-SNAPSHOT</version>
  </dependency>
  ...
 </dependencies>
 <build>
  <resources>
 ...
  </resources>
  <plugins>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-install-plugin</artifactId>
    <version>2.4</version>
    <executions>
     <execution>
      <phase>initialize</phase>
      <goals>
       <goal>install-file</goal>
      </goals>
      <configuration>
       <groupId>com.technorage</groupId>
       <artifactId>drools-tools</artifactId>
       <version>1.0.0-SNAPSHOT</version>
       <packaging>jar</packaging>
       <file>${basedir}/lib/drools-tools-1.0.0-SNAPSHOT.jar</file>
      </configuration>
     </execution>
    </executions>
   </plugin>
   ... 
  </plugins>
  <pluginManagement>
  ....
  </pluginManagement>
 </build>

Once the project is setup using maven, update the the project from the Eclipse maven menu to update the workspace dependencies.

2. Project Structure

The entire project structure will look like this


3. Web app config

Since we are doing an XML less setup lets start with the web configuration. Find the WebInitializer.java class under src --> web --> config

public class WebInitializer implements WebApplicationInitializer {

@Override
public void onStartup(ServletContext container) {
  // Create the dispatcher servlet's Spring application context
  AnnotationConfigWebApplicationContext dispatcherServlet = new AnnotationConfigWebApplicationContext();
  dispatcherServlet.register(DemoWebConfig.class);

  // Register and map the dispatcher servlet
  ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher",
      new DispatcherServlet(dispatcherServlet));
  dispatcher.setLoadOnStartup(1);
  dispatcher.addMapping("/");
}
}

We will configure the dispatcher servlet to load DemoWebConfig class where we will enable webMVC and import other config classes as below.

@Configuration
// Marks this class as configuration
@Import(DemoServicesConfig.class) // This is just an empty config class, Drools config is loaded here in DemoKieConfig
// Specifies which package to scan
@ComponentScan("com.technorage.demo")
// Enables Spring's annotations
@EnableWebMvc
public class DemoWebConfig extends WebMvcConfigurerAdapter {

@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
  configurer.enable();
}

@Bean
public InternalResourceViewResolver jspViewResolver() {
  InternalResourceViewResolver bean = new InternalResourceViewResolver();
  bean.setPrefix("/WEB-INF/views/");
  bean.setSuffix(".jsp");
  return bean;
}
...
}

4. Drools config

The drools configuration is loaded by the DemoKieConfig class imported in DemoServicesConfig class as below

@Configuration
public class DemoKieConfig {

 @Bean(name = "demoKieServices")
 public KieServicesBean kieServices() throws KieBuildException {
   DroolsResource[] resources = new DroolsResource[] { new DroolsResource(
       "rules/demo-rules.drl", ResourcePathType.CLASSPATH, ResourceType.DRL) };
   KieServicesBean bean = new DefaultKieServicesBean(resources);
   return bean;
 }
 
 @Bean(name = "demoKieContainer")
 public KieContainerBean kieContainer(KieServicesBean kieServices) {
   KieContainerBean bean = new DefaultKieContainerBean(kieServices);
   return bean;
 }
}

The drools session will be created using the KieServicesBean and KieContainerBean injected by spring as per above config. The knowledge base is built using the drl files provided in the resources directory as configured above.

Now lets move on and use the drools session in a spring service class.


@Service
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON, proxyMode = ScopedProxyMode.INTERFACES)
public class DemoRuleServiceImpl<T> implements DemoRuleService<T>, Serializable {

private KieSessionBean kieSession;
private TrackingAgendaEventListener agendaEventListener;
private TrackingWorkingMemoryEventListener workingMemoryEventListener;
private Map<String, Room> name2room = new HashMap<String, Room>();
private Map<String, FactHandle> fact2fire = new HashMap<String, FactHandle>();
private FactFinder<Alarm> findAlarms = new FactFinder<>(Alarm.class);
private FactFinder<Sprinkler> findSprinklers = new FactFinder<>(Sprinkler.class);

@Autowired
public DemoRuleServiceImpl(@Qualifier("demoKieContainer") KieContainerBean kieContainer,@Qualifier("demoKieServices") KieServicesBean kieServices) {

  kieSession = new DefaultKieSessionBean(kieServices, kieContainer);
  agendaEventListener = new TrackingAgendaEventListener();
  workingMemoryEventListener = new TrackingWorkingMemoryEventListener();
  kieSession.addEventListener(agendaEventListener);
  kieSession.addEventListener(workingMemoryEventListener);
}

@Override
public Collection<Alarm> addFire(String[] fires) {

  for (String fire : fires) {
    if (!fact2fire.containsKey(fire)) {
      Fire roomFire = new Fire(name2room.get(fire));
      FactHandle roomFireHandle = kieSession.insert(roomFire);
      fact2fire.put(fire, roomFireHandle);
    }
  }
  kieSession.fireAllRules();
  Collection<Alarm> result = findAlarms.findFacts(kieSession);
  return result;
}

@Override
public Collection<Alarm> checkForFire() {

  Collection<Alarm> result = findAlarms.findFacts(kieSession);
  return result;
}
.... other methods
}

Drools service and container is injected in the constructor to create KieSession, the agendaEventListener and workingMemoryEventListener are utilities for tracking and logging rules activation. FactFinder class is used to retrive Facts from active KieSession for update and to retreat. All these utility classes and beans are loaded from the drools-tools.jar and can be reused as it is for any drools project.

Now the configured service can be autowired from any spring MVC controllers.

Read on for the fire alarm web demo part 2
Posted By: Unknown

Drools 6 integration with Spring MVC 4- Part 1

Share:

Post a Comment

Facebook
Blogger

19 comments :

  1. Well Said, you have furnished the right information that will be useful to anyone at all time. Thanks for sharing your Ideas.
    java training in tambaram | java training in velachery

    java training in omr | oracle training in chennai

    java training in annanagar | java training in chennai

    ReplyDelete
  2. I really like your blog. You make it interesting to read and entertaining at the same time. I cant wait to read more from you.
    Blueprism training institute in Chennai

    Blueprism online training

    ReplyDelete
  3. Thank you for an additional great post. Exactly where else could anybody get that kind of facts in this kind of a ideal way of writing? I have a presentation next week, and I’m around the appear for this kind of data.
    angularjs Training in chennai

    angularjs-Training in tambaram

    angularjs-Training in sholinganallur

    angularjs-Training in velachery

    angularjs Training in bangalore

    ReplyDelete
  4. Where is part 2 ?
    and how to run this project .. plz help

    ReplyDelete
  5. Well Said, you have furnished the right information that will be useful to anyone at all time. Thanks for sharing your Ideas.keep share some more coding..it may help us lot
    AngularJS training in chennai | AngularJS training in anna nagar | AngularJS training in omr | AngularJS training in porur | AngularJS training in tambaram | AngularJS training in velachery

    ReplyDelete

Follow Us

© None! Feel free to copy content :) | Home | QuizMaster Theme Designed by Blogger Templates