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

Saturday, September 27, 2014

What the Web!!!

What the Web!! Yes, that is what came to my mind (pun intended). I have been working on web technologies for quite some time now and I have to say that it has reached all new heights  lately.

The growth of web technologies was not quick, it was slow and subtle in the beginning. From 1991 to 2000 it was stabilizing slowly, 2000 to 2007 saw the advent of Ajax but still there was no dramatic changes in the way people saw web technologies, average Internet traffic was the same from 1991(Thanks to IE for that). The fun began towards the end of 2007, sadly that's when the last Netscape browser came out and gladly Chrome was unleashed. 2008 onward was the exponential growth of web technologies (Was the browser war between Chrome and Firefox to be blamed?)

The link provides a nice representation of this Evolution of web


Today web technologies are no more looked down as sidekicks, they are the hero now and with the crossover to server side by Node.js web technologies are slowly replacing traditional Javaish technologies everywhere.

The penetration is way beyond client side web. With JavaScript as the lead protagonist, today JavaScript is being used at full swing in;

  • Traditional websites (HTML, CSS, JS, JQuery, Bootstrap, etc.)
  • Web apps (HTML, CSS, JS, Jquery, Ajax, Angular, Backbone, Ember, Bootstrap, etc.)
  • Client side applications-Chrome apps, Firefox plugins, windows apps, etc. (HTML, CSS, JS, Jquery, Ajax, Angular, Backbone, Ember, Bootstrap, etc.)
  • Mobile Applications -Hybrid Native apps, Responsive Mobile web Apps (HTML, CSS, JS, Cordova, Jquery, Ajax, Angular, Backbone, Ember, Bootstrap, etc.)
  • Desktop applications- Packaged apps running in Node
  • Server side (Node, Express, Tower, etc.)
  • DB ( MongooseCouchDB, etc.)
  • Robotics (Arduino, Node, etc.)
Today Javascript is becoming more and more powerful, with each ECMAScript version release. It could be said that it is becoming more LISP like in nature and is being adopted in wide use cases.

Most noteworthy for the sudden increase in the popularity of web technologies should be attributed to NodeJS and how it changed the way Javascript was used and perceived to be, this led to lots of spin-of frameworks and libraries. Node makes it possible to build javascript apps for server side, desktop and even for creating your own army of robots.

Similarly the hybrid mobile app revolution was brought in by Phonegap a.k.a Cordova, which is slowly closing the gap between native apps and hybrid html apps. Mobile UI frameworks like Jquery mobile, Sencha touch, Ionic, Bootstrap,  etc., with the power of cordova is helping web developers world wide to expand their horizon to the lucrative mobile application marketplace to compete head on with the likes of native android and iOS apps.

Another noteworthy improvement is in the client side JavaScript frameworks and libraries. Jquery started revolutionizing client side scripting which soon paved way for other libraries and frameworks like Bootstrap, AngularJS, Backbone,  Knockout, Ember, etc. , to name a few. Today it is entirely possible and practical to write an end to end JavaScript web application using a JavaScript webserver and host it on a nodeJs server.

All of the above mentioned frameworks and libraries need a post of its own to do justice, most of them are bread and butter of most developers and hence I would be writing a series on the newer and revolutionary ones soon.
Follow Us

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