Sunday, 6 June 2010

Madura Rules Part 2

My previous post covered how to specify rules for the Madura Rules engine. This post covers how to add Madura Rules to your application.

First, we are assuming that your application is already using Madura Objects and that all we need to do here is build on that.

Add something like this to your ant build script:

<taskdef name="xjr" classname="nz.co.senanque.generate.XJR">
    <classpath>
      <fileset dir="${basedir}/temp/lib" includes="*.jar" />
    </classpath>
</taskdef>

<target name="generateRules">
    <xjr destdir="${basedir}/generated"
    rules="${basedir}/test/nz/co/senanque/rulesparser/ObjectTest.txt"
    schema="${basedir}/sandbox.xsd"
    packageName="nz.co.senanque.objecttestrules"
    xsdpackageName="nz.co.senanque.madura.sandbox"/>
</target>

Some explanation: this defines an ant task in the taskdef tag. We're assuming you copied the MaduraRules jar file and its dependencies into the ${basedir}/temp/lib dir.

Then we've defined a target which generates the rules. The rules file, schema file and target package for the generated Java must be specified. The xsdpackageName should be the same as the package you specified in your XJC when you generated the Java for your business objects. You can omit this if you the two packages are the same.

That will generate a class for each rule. But that would not be all that smart (translating a text file into Java is done by your Java compiler all the time). No, the rules are translated into Java but the engine decides which rules are relevant to fire, and the smart bit is in the engine.

Now let's add some things to your Spring file.

<context:component-scan base-package="nz.co.senanque.objecttestrules"/>
<context:annotation-config/>

Make sure the base-package name agrees with the packageName in your XJR command. This ensures that Spring scans that package looking for rules to gather up and use in this...

<bean id="MaduraRulesPlugin" class="nz.co.senanque.rules.RulesPlugin" init-method="init">
  <property name="operations">
    <bean class="nz.co.senanque.rules.OperationsImpl"/>
  </property>
  <property name="decisionTableDocument">
    <bean class="nz.co.senanque.madura.spring.XMLSpringFactoryBean">
      <property name="fileLocation" value="/choices.xml"/>
    </bean>
  </property>
  <property name="constantsDocument">
    <bean class="nz.co.senanque.madura.spring.XMLSpringFactoryBean">
      <property name="fileLocation" value="/choices.xml"/>
    </bean>
  </property>
</bean>

All the properties here are optional and we cover them later. The important thing for now is that the MaduraRulesPlugin is injected into the validationEngine plugin (ie Madura Objects) like this:

<bean id="validationEngine" class="nz.co.senanque.validationengine.ValidationEngineImpl">
  <property name="plugins">
    <list>
      <ref bean="MaduraRulesPlugin"/>
    </list>
  </property>
  ...
</bean>

Yes, you can wire multiple plugins into Madura Objects. They can do whatever you like, actually. Madura Rules is a rules engine, but you might like to add a pricing engine or something more exotic in there. The only thing to watch out for is that you must not cross map fields to more than one engine because the engines cannot see each other's updates.

That's the job done. We defined some rules in the text file, generated them with XJR and then wired the engine into our Spring file. Those rules are now active, and your application code doesn't have to know anything about them. So, of course, you can change the rules at any time without changing your application code.

In my next post on this I will cover those extra properties and some more about what the rules can do.
Post a Comment