Commits

Marcin Grzejszczak committed 3143f5b

[Drools_Decision_Table_Camel] Initial commit of the Drools Decision Table integrated with Camel and Spring project

Comments (0)

Files changed (15)

Drools/Decision table/pom.xml

+<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>pl.grzejszczak.marcin</groupId>
+	<artifactId>drools-decision-table</artifactId>
+	<version>0.0.1-SNAPSHOT</version>
+
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+		<maven.compiler.source>1.6</maven.compiler.source>
+		<maven.compiler.target>1.6</maven.compiler.target>
+		<spring.version>3.1.1.RELEASE</spring.version>
+        <drools.version>5.4.0.Final</drools.version>
+        <jaxb.version>2.2.6</jaxb.version>
+    </properties>
+	<repositories>
+		<repository>
+			<id>spring-release</id>
+			<url>http://maven.springframework.org/release</url>
+		</repository>
+	</repositories>
+
+	<dependencies>
+		<!-- Spring -->
+		<dependency>
+			<groupId>org.springframework</groupId>
+			<artifactId>spring-core</artifactId>
+			<version>${spring.version}</version>
+		</dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+            <version>${spring.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>13.0.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-test</artifactId>
+            <version>${spring.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.drools</groupId>
+            <artifactId>drools-camel</artifactId>
+            <version>${drools.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.xml.bind</groupId>
+            <artifactId>jaxb-impl</artifactId>
+            <version>${jaxb.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.xml.bind</groupId>
+            <artifactId>jaxb-xjc</artifactId>
+            <version>${jaxb.version}</version>
+        </dependency>
+		<dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <version>1.6.6</version>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.10</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+            <version>2.6</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <version>2.5.1</version>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>jaxb2-maven-plugin</artifactId>
+                <version>1.5</version>
+                <executions>
+                    <execution>
+                        <id>xjc</id>
+                        <goals>
+                            <goal>xjc</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <packageName>pl.grzejszczak.marcin.drools.decisiontable.model</packageName>
+                    <schemaDirectory>${project.basedir}/src/main/resources/xsd</schemaDirectory>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

Drools/Decision table/src/main/java/pl/grzejszczak/marcin/drools/decisiontable/converter/ProductTypeConverter.java

+package pl.grzejszczak.marcin.drools.decisiontable.converter;
+
+import org.apache.camel.Converter;
+import org.drools.command.Command;
+import org.drools.command.CommandFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import pl.grzejszczak.marcin.drools.decisiontable.model.Product;
+
+import java.util.List;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: mgrzejszczak
+ * Date: 30.01.13
+ * Time: 21:42
+ */
+@Converter
+public class ProductTypeConverter {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ProductTypeConverter.class);
+
+    @Converter
+    public static Command toCommandFromList(List<Product> productList) {
+        LOGGER.debug("Executing ProductTypeConverter's toCommandFromList method");
+        return CommandFactory.newInsertElements(productList);
+    }
+
+    @Converter
+    public static Command toCommand(Product product) {
+        LOGGER.debug("Executing ProductTypeConverter's toCommand method");
+        return CommandFactory.newInsert(product);
+    }
+}

Drools/Decision table/src/main/java/pl/grzejszczak/marcin/drools/decisiontable/service/FinancialService.java

+package pl.grzejszczak.marcin.drools.decisiontable.service;
+
+import pl.grzejszczak.marcin.drools.decisiontable.model.Product;
+import pl.grzejszczak.marcin.drools.decisiontable.model.User;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: mgrzejszczak
+ * Date: 03.02.13
+ * Time: 18:45
+ */
+public interface FinancialService {
+    boolean processOrder(User user, Product product);
+}

Drools/Decision table/src/main/java/pl/grzejszczak/marcin/drools/decisiontable/service/FinancialServiceImpl.java

+package pl.grzejszczak.marcin.drools.decisiontable.service;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import pl.grzejszczak.marcin.drools.decisiontable.model.Product;
+import pl.grzejszczak.marcin.drools.decisiontable.model.User;
+
+import static pl.grzejszczak.marcin.drools.decisiontable.model.DecisionType.ACCEPTED;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: mgrzejszczak
+ * Date: 03.02.13
+ * Time: 18:45
+ */
+@Component("financialServiceImpl")
+public class FinancialServiceImpl implements FinancialService {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(FinancialServiceImpl.class);
+
+    @Override
+    public boolean processOrder(User user, Product product) {
+        if(ACCEPTED.equals(user.getDecision())){
+            LOGGER.debug("User has been approved - processing the order...");
+            return true;
+        }
+
+        LOGGER.debug("Sorry, user has been rejected...");
+        return false;
+    }
+}

Drools/Decision table/src/main/java/pl/grzejszczak/marcin/drools/decisiontable/service/ProductService.java

+package pl.grzejszczak.marcin.drools.decisiontable.service;
+
+import pl.grzejszczak.marcin.drools.decisiontable.model.User;
+import pl.grzejszczak.marcin.drools.decisiontable.model.Product;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: mgrzejszczak
+ * Date: 14.01.13
+ */
+public interface ProductService {
+    void runProductLogic(User user, Product product);
+}

Drools/Decision table/src/main/java/pl/grzejszczak/marcin/drools/decisiontable/service/ProductServiceImpl.java

+package pl.grzejszczak.marcin.drools.decisiontable.service;
+
+import org.apache.camel.CamelContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import pl.grzejszczak.marcin.drools.decisiontable.model.Product;
+import pl.grzejszczak.marcin.drools.decisiontable.model.User;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: mgrzejszczak
+ * Date: 14.01.13
+ */
+@Component("productServiceImpl")
+public class ProductServiceImpl implements ProductService {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ProductServiceImpl.class);
+
+    @Autowired
+    CamelContext camelContext;
+
+    @Autowired
+    FinancialService financialService;
+
+    @Override
+    public void runProductLogic(User user, Product product) {
+        LOGGER.debug("Running product logic - first acceptance Route, then discount Route");
+        camelContext.createProducerTemplate().sendBody("direct:acceptanceRoute", newArrayList(user, product));
+        camelContext.createProducerTemplate().sendBody("direct:discountRoute", newArrayList(user, product));
+        financialService.processOrder(user, product);
+    }
+
+}

Drools/Decision table/src/main/resources/META-INF/services/org/apache/camel/TypeConverter

+pl.grzejszczak.marcin.drools.decisiontable.converter.ProductTypeConverter

Drools/Decision table/src/main/resources/applicationContext.xml

+<?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:camel="http://camel.apache.org/schema/spring"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
+                           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+                           http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring-2.8.0.xsd">
+
+    <import resource="classpath:drools-context.xml"/>
+    <!-- Show Spring where to search for the beans (in which packages) -->
+    <context:component-scan base-package="pl.grzejszczak.marcin.drools.decisiontable" />
+
+    <camel:camelContext id="camelContext">
+        <camel:route id="acceptanceRoute">
+            <camel:from uri="direct:acceptanceRoute"/>
+            <camel:to uri="drools:node1/usersKSession"/>
+        </camel:route>
+        <camel:route id="discountRoute">
+            <camel:from uri="direct:discountRoute"/>
+            <camel:to uri="drools:node1/productsKSession"/>
+        </camel:route>
+    </camel:camelContext>
+
+</beans>

Drools/Decision table/src/main/resources/drools-context.xml

+<?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:drools="http://drools.org/schema/drools-spring"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+		http://drools.org/schema/drools-spring http://drools.org/schema/drools-spring.xsd">
+
+    <!-- Execution Node identifier that is registered in the CamelContext -->
+    <drools:grid-node id="node1"/>
+
+    <drools:kbase id="productsKBase" node="node1">
+        <drools:resources>
+            <drools:resource type="DTABLE" source="classpath:rules/product_table.xls"/>
+        </drools:resources>
+    </drools:kbase>
+
+    <drools:ksession id="productsKSession" name="productsKSession" type="stateless" kbase="productsKBase" node="node1"/>
+
+    <drools:kbase id="usersKBase" node="node1">
+        <drools:resources>
+            <drools:resource type="DTABLE" source="classpath:rules/user_table.xls"/>
+        </drools:resources>
+    </drools:kbase>
+
+    <drools:ksession id="usersKSession" name="usersKSession" type="stateless" kbase="usersKBase" node="node1"/>
+
+</beans>

Drools/Decision table/src/main/resources/log4j.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+
+	<!-- Appenders -->
+	<appender name="console" class="org.apache.log4j.ConsoleAppender">
+		<param name="Target" value="System.out" />
+		<layout class="org.apache.log4j.PatternLayout">
+			<param name="ConversionPattern" value="%c:%L %m%n" />
+		</layout>
+	</appender>
+	
+	<!-- Application Loggers -->
+	<logger name="pl.grzejszczak.marcin">
+		<level value="DEBUG" />
+	</logger>
+
+	<logger name="org.springframework">
+		<level value="ERROR" />
+	</logger>
+
+    <logger name="org.apache.camel">
+        <level value="ERROR" />
+    </logger>
+
+    <logger name="org.drools">
+        <level value="ERROR" />
+    </logger>
+
+
+    <!-- Root Logger -->
+	<root>
+		<priority value="INFO" />
+		<appender-ref ref="console" />
+	</root>
+	
+</log4j:configuration>

Drools/Decision table/src/main/resources/rules/product_table.xls

Binary file added.

Drools/Decision table/src/main/resources/rules/user_table.xls

Binary file added.

Drools/Decision table/src/main/resources/xsd/product.xsd

+<?xml version="1.0" encoding="UTF-8"?>
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+    <xsd:include schemaLocation="user.xsd"/>
+
+    <xsd:element name="Product">
+        <xsd:complexType>
+            <xsd:sequence>
+                <xsd:element name="Name" type="xsd:string"/>
+                <xsd:element name="Type" type="ProductType"/>
+                <xsd:element name="Price" type="xsd:double"/>
+                <xsd:element name="CountryOfOrigin" type="CountryType"/>
+                <xsd:element name="AdditionalInfo" type="xsd:string"/>
+                <xsd:element name="Quantity" type="xsd:int"/>
+            </xsd:sequence>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:simpleType name="ProductType">
+        <xsd:restriction base="xsd:string">
+            <xsd:enumeration value="MEDICAL"/>
+            <xsd:enumeration value="ELECTRONIC"/>
+        </xsd:restriction>
+    </xsd:simpleType>
+
+</xsd:schema>

Drools/Decision table/src/main/resources/xsd/user.xsd

+<?xml version="1.0" encoding="UTF-8"?>
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+    <xsd:include schemaLocation="product.xsd"/>
+
+    <xsd:element name="User">
+        <xsd:complexType>
+            <xsd:sequence>
+                <xsd:element name="UserName" type="xsd:string"/>
+                <xsd:element name="UserAge" type="xsd:int"/>
+                <xsd:element name="UserCountry" type="CountryType"/>
+                <xsd:element name="Decision" type="DecisionType"/>
+                <xsd:element name="DecisionDescription" type="xsd:string"/>
+            </xsd:sequence>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:simpleType name="CountryType">
+        <xsd:restriction base="xsd:string">
+            <xsd:enumeration value="PL"/>
+            <xsd:enumeration value="USA"/>
+            <xsd:enumeration value="GER"/>
+            <xsd:enumeration value="SWE"/>
+            <xsd:enumeration value="UK"/>
+            <xsd:enumeration value="ESP"/>
+        </xsd:restriction>
+    </xsd:simpleType>
+
+    <xsd:simpleType name="DecisionType">
+        <xsd:restriction base="xsd:string">
+            <xsd:enumeration value="ACCEPTED"/>
+            <xsd:enumeration value="REJECTED"/>
+        </xsd:restriction>
+    </xsd:simpleType>
+
+</xsd:schema>

Drools/Decision table/src/test/java/pl/grzejszczak/marcin/drools/decisiontable/service/drools/ProductServiceImplTest.java

+package pl.grzejszczak.marcin.drools.decisiontable.service.drools;
+
+import org.apache.commons.lang.builder.ReflectionToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import pl.grzejszczak.marcin.drools.decisiontable.model.*;
+import pl.grzejszczak.marcin.drools.decisiontable.service.ProductService;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: mgrzejszczak
+ * Date: 03.02.13
+ * Time: 16:06
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration("classpath:applicationContext.xml")
+public class ProductServiceImplTest {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ProductServiceImplTest.class);
+
+    @Autowired
+    ProductService objectUnderTest;
+
+    @Test
+    public void testRunProductLogicUserPlUnderageElectronicCountryPLQuantity10() throws Exception {
+        int initialPrice = 1000;
+        int userAge = 6;
+        int quantity = 10;
+
+        User user = createUser("Smith", CountryType.PL, userAge);
+        Product product = createProduct("Electronic", initialPrice, CountryType.PL, ProductType.ELECTRONIC, quantity);
+
+        printInputs(user, product);
+
+        objectUnderTest.runProductLogic(user, product);
+
+        printInputs(user, product);
+
+        assertTrue(product.getPrice() == initialPrice);
+        assertEquals(DecisionType.REJECTED, user.getDecision());
+    }
+
+    @Test
+    public void testRunProductLogicUserPlAge19ElectronicCountryPLQuantity1() throws Exception {
+        int initialPrice = 1000;
+        int userAge = 19;
+        int quantity = 1;
+
+        User user = createUser("Smith", CountryType.PL, userAge);
+        Product product = createProduct("Electronic", initialPrice, CountryType.PL, ProductType.ELECTRONIC, quantity);
+
+        printInputs(user, product);
+
+        objectUnderTest.runProductLogic(user, product);
+
+        printInputs(user, product);
+
+        assertTrue(product.getPrice() == initialPrice);
+        assertEquals(DecisionType.ACCEPTED, user.getDecision());
+    }
+
+    @Test
+    public void testRunProductLogicUserPlAge19ElectronicCountryPLQuantity10() throws Exception {
+        int initialPrice = 1000;
+        int userAge = 19;
+        int quantity = 8;
+
+        User user = createUser("Smith", CountryType.PL, userAge);
+        Product product = createProduct("Electronic", initialPrice, CountryType.PL, ProductType.ELECTRONIC, quantity);
+
+        printInputs(user, product);
+
+        objectUnderTest.runProductLogic(user, product);
+
+        printInputs(user, product);
+        double expectedDiscount = 0.1;
+
+        assertTrue(product.getPrice() == initialPrice * (1 - expectedDiscount));
+        assertEquals(DecisionType.ACCEPTED, user.getDecision());
+    }
+
+    @Test
+    public void testRunProductLogicUserUsaAge19ElectronicCountryPLQuantity10() throws Exception {
+        int initialPrice = 1000;
+        int userAge = 19;
+        int quantity = 8;
+
+        User user = createUser("Smith", CountryType.USA, userAge);
+        Product product = createProduct("Electronic", initialPrice, CountryType.PL, ProductType.ELECTRONIC, quantity);
+
+        printInputs(user, product);
+
+        objectUnderTest.runProductLogic(user, product);
+
+        printInputs(user, product);
+
+        assertTrue(product.getPrice() == initialPrice);
+        assertEquals(DecisionType.REJECTED, user.getDecision());
+    }
+
+    @Test
+    public void testRunProductLogicUserUsaAge22MedicalCountrySWEQuantity4() throws Exception {
+        int initialPrice = 1000;
+        int userAge = 22;
+        int quantity = 4;
+
+        User user = createUser("Smith", CountryType.USA, userAge);
+        Product product = createProduct("Some name", initialPrice, CountryType.SWE, ProductType.MEDICAL, quantity);
+
+        printInputs(user, product);
+
+        objectUnderTest.runProductLogic(user, product);
+
+        printInputs(user, product);
+
+        assertTrue(product.getPrice() == initialPrice);
+        assertEquals(DecisionType.ACCEPTED, user.getDecision());
+    }
+
+    @Test
+    public void testRunProductLogicUserUsaAge22MedicalCountrySWEQuantity8() throws Exception {
+        int initialPrice = 1000;
+        int userAge = 22;
+        int quantity = 8;
+
+        User user = createUser("Smith", CountryType.USA, userAge);
+        Product product = createProduct("Some name", initialPrice, CountryType.SWE, ProductType.MEDICAL, quantity);
+
+        printInputs(user, product);
+
+        objectUnderTest.runProductLogic(user, product);
+
+        printInputs(user, product);
+        double expectedDiscount = 0.25;
+
+        assertTrue(product.getPrice() == initialPrice * (1 - expectedDiscount));
+        assertEquals(DecisionType.ACCEPTED, user.getDecision());
+    }
+
+    private void printInputs(User user, Product product) {
+        LOGGER.debug(ReflectionToStringBuilder.reflectionToString(user, ToStringStyle.MULTI_LINE_STYLE));
+        LOGGER.debug(ReflectionToStringBuilder.reflectionToString(product, ToStringStyle.MULTI_LINE_STYLE));
+    }
+
+    private User createUser(String name, CountryType countryType, int userAge){
+        User user = new User();
+        user.setUserName(name);
+        user.setUserCountry(countryType);
+        user.setUserAge(userAge);
+        return user;
+    }
+
+    private Product createProduct(String name, double price, CountryType countryOfOrigin, ProductType productType, int quantity){
+        Product product = new Product();
+        product.setPrice(price);
+        product.setCountryOfOrigin(countryOfOrigin);
+        product.setName(name);
+        product.setType(productType);
+        product.setQuantity(quantity);
+        return product;
+    }
+
+}