Red Hat Training

A Red Hat training course is available for JBoss Enterprise SOA Platform

Chapter 12. Modes and Methods

12.1. Sequential Mode

Using sequential mode in JBoss Rules allows you to utilize the engine in a more simplified way. It allows for rules to be used without needing to be re-evaluated if a user is working with a stateless session and no more data can be asserted or modified after the initial data set.

12.2. Sequential Mode Options

These are some of the options you can choose to utilize when using the sequential mode:
  1. Order the Rules by salience and position in the ruleset (by setting a sequence attribute on the rule terminal node).
  2. Create an array (one element for each possible rule activation). Element position indicates firing order.
  3. Turn off all node memories, except the right-input Object memory.
  4. Disconnect the Left Input Adapter Node propagation and let the Object and the Node be referenced in a Command object. This is added to a list in the Working Memory for later execution.
  5. Assert all objects. When all assertions are finished and the right-input node memories are populated, you can check the Command list and execute each in turn.
  6. All resulting Activations should be placed in the array, based upon the determined sequence number of the Rule. Record the first and last populated elements, to reduce the iteration range.
  7. Iterate the array of Activations, executing populated element in turn.
  8. If there is a maximum number of allowed rule executions, exit the network evaluations early to fire all the rules in the array.

12.3. Activating Sequential Mode

Procedure 12.1. Task

  1. Start a stateless session.
  2. The sequential mode will be turned off by default. To turn it on, call RuleBaseConfiguration.setSequential(true). Alternatively, set the rulebase configuration property drools.sequential to true.
  3. To allow sequential mode to fall back to a dynamic agenda, call setSequentialAgenda with SequentialAgenda.DYNAMIC.
  4. Optionally, set the JBossRules.sequential.agenda property to sequential or dynamic.

12.4. The CommandFactory

The CommandFactory object allows for commands to be executed on stateless sessions. Upon its conclusion, the factory will execute fireAllRules() before disposing the session.

12.5. Supported CommandFactory Options

All of these options are compatible with the CommandFactory:
  • FireAllRules
  • GetGlobal
  • SetGlobal
  • InsertObject
  • InsertElements
  • Query
  • StartProcess
  • BatchExecution

12.6. The Insert Command

InsertObject will insert a single object with an optional "out" identifier. InsertElements will iterate an Iterable, inserting each of the elements. This allows a Stateless Knowledge Session to process or execute queries in any order.

12.7. Insert Command Example

StatelessKnowledgeSession ksession = kbase.newStatelessKnowledgeSession();
ExecutionResults bresults =
  ksession.execute( CommandFactory.newInsert( new Car( "sedan" ), "sedan_id" ) );
Sedan sedan = bresults.getValue( "sedan_id" );

12.8. The Execute Method

The execute method is used to execute commands one at a time. It always returns an ExecutionResults instance, which allows access to any command results if they specify an out identifier such as stilton_id.

12.9. Execute Method Example

StatelessKnowledgeSession ksession = kbase.newStatelessKnowledgeSession();
Command cmd = CommandFactory.newInsertElements( Arrays.asList( Object[] { 
                  new Car( "sedan" ),
                  new Car( "hatchback" ),
                  new Car( "convertible" ),
              });
ExecutionResults bresults = ksession.execute( cmd );

12.10. The BatchExecution Command

The BatchExecution command allows you to execute multiple commands at once. It represents a composite command that is created from a list of commands. Execute will iterate over the list and execute each command in turn. This means you can insert some objects, start a process, call fireAllRules and execute a query, all in a single execute(...) call.

12.11. The FireAllRules Command

The FireAllRules command disables the automatic execution of rules at the end. It is a type of manual override function.

12.12. Out Identifiers

Commands support out identifiers. Any command that has an out identifier set on it will add its results to the returned ExecutionResults instance.

12.13. Out Identifier Example

This example will use the BatchExecution command to show how out identifiers work:
StatelessKnowledgeSession ksession = kbase.newStatelessKnowledgeSession();

List cmds = new ArrayList();        
cmds.add( CommandFactory.newInsertObject( new Car( "red", 1), "red") );
cmds.add( CommandFactory.newStartProcess( "process cars" ) );
cmds.add( CommandFactory.newQuery( "cars" ) );
ExecutionResults bresults = ksession.execute( CommandFactory.newBatchExecution( cmds ) );
Car red = ( Car ) bresults.getValue( "red" );
QueryResults qresults = ( QueryResults ) bresults.getValue( "cars" );
In the above example multiple commands are executed, two of which populate the ExecutionResults. The query command defaults to use the same identifier as the query name, but it can also be mapped to a different identifier.

12.14. Execution XML Examples

A custom XStream marshaler can be used with the JBoss Rules Pipeline to achieve XML scripting, which is perfect for services. Here are two examples of this:
BatchExecution XML:
<batch-execution>
   <insert out-identifier='outRed'>
      <org.drools.Car>
         <type>red</type>
         <price>25000</price>
         <oldPrice>0</oldPrice>
      </org.drools.Car>
   </insert>
</batch-execution>
ExecutionResults XML:
<execution-results>
   <result identifier='outBlue'>
      <org.drools.Car>
         <type>Blue</type>
         <oldPrice>25</oldPrice>        
         <price>30000</price>
      </org.drools.Car>
   </result>
</execution-results>

12.15. Execution Marshalling Examples

This is an example of BatchExecution marshalled to XML
<batch-execution>
  <insert out-identifier="sedan">
    <org.drools.Car>
      <type>sedan</type>
      <price>1</price>
      <oldPrice>0</oldPrice>
    </org.drools.Car>
  </insert>
  <query out-identifier='cars2' name='carsWithParams'>
    <string>hatchback</string>
    <string>sedan</string>
  </query>
</batch-execution>
The CommandExecutor returns an ExecutionResults, and this is handled by the pipeline code snippet as well. A similar output for the <batch-execution> XML sample above would be:
<execution-results>
  <result identifier="sedan">
    <org.drools.Car>
      <type>sedan</type>
      <price>2</price>
    </org.drools.Car>
  </result>        
  <result identifier='cars2'>
    <query-results>
      <identifiers>
        <identifier>car</identifier>
      </identifiers>
      <row>
        <org.drools.Car>
          <type>hatchback</type>
          <price>2</price>
          <oldPrice>0</oldPrice>
        </org.drools.Car>
      </row>
      <row>
        <org.drools.Car>
          <type>hatchback</type>
          <price>1</price>
          <oldPrice>0</oldPrice>
        </org.drools.Car>
      </row>
    </query-results>
  </result>
</execution-results>

12.16. Batch-execution and Command Examples

  1. There is currently no XML schema to support schema validation. This is the basic format. The root element is <batch-execution> and it can contain zero or more commands elements:
    <batch-execution>
    ...
    </batch-execution>
    
  2. The insert element features an "out-identifier" attribute so the inserted object will be returned as part of the result payload:
    <batch-execution>
       <insert out-identifier='userVar'>
          ...
       </insert>
    </batch-execution>
    
  3. It's also possible to insert a collection of objects using the <insert-elements> element. This command does not support an out-identifier. The org.domain.UserClass is just an illustrative user object that XStream would serialize:
    <batch-execution>
       <insert-elements>
          <org.domain.UserClass>
             ...
          </org.domain.UserClass>
          <org.domain.UserClass>
             ...
          </org.domain.UserClass>
          <org.domain.UserClass>
             ...
          </org.domain.UserClass>
       </insert-elements>
    </batch-execution>
    
  4. The <set-global> element sets a global for the session:
    <batch-execution>
       <set-global identifier='userVar'>
          <org.domain.UserClass>
             ...
          </org.domain.UserClass>
       </set-global>
    </batch-execution>
    
  5. <set-global> also supports two other optional attributes: out and out-identifier. A true value for the boolean out will add the global to the <batch-execution-results> payload, using the name from the identifier attribute. out-identifier works like out but additionally allows you to override the identifier used in the <batch-execution-results> payload:
    <batch-execution>
       <set-global identifier='userVar1' out='true'>
          <org.domain.UserClass>
             ...
          </org.domain.UserClass>
       </set-global>
       <set-global identifier='userVar2' out-identifier='alternativeUserVar2'>
          <org.domain.UserClass>
             ...
          </org.domain.UserClass>
       </set-global>
    </batch-execution>
    
  6. There is a <get-global> element without contents. It only has an out-identifier attribute. There is no need for an out attribute because retrieving the value is the sole purpose of a <get-global> element:
    <batch-execution>
       <get-global identifier='userVar1' />
       <get-global identifier='userVar2' out-identifier='alternativeUserVar2'/>
    </batch-execution>
    
  7. The query command supports both parameter and parameterless queries. The name attribute is the name of the query to be called, and the out-identifier is the identifier to be used for the query results in the <execution-results> payload:
    <batch-execution>
       <query out-identifier='cars' name='cars'/>
       <query out-identifier='cars2' name='carsWithParams'>
          <string>red</string>
          <string>blue</string>
       </query>
    </batch-execution>
    
  8. The <start-process> command accepts optional parameters:
    <batch-execution>
       <startProcess processId='org.drools.actions'>
          <parameter identifier='person'>
             <org.drools.TestVariable>
                <name>John Doe</name>
             </org.drools.TestVariable>
          </parameter>
       </startProcess>
    </batch-execution
    
  9. The signal event command allows you to identify processes:
    <signal-event process-instance-id='1' event-type='MyEvent'>
       <string>MyValue</string>
    </signal-event>
    
  10. The complete work item command notifies users when a process is completed:
    <complete-work-item id='" + workItem.getId() + "' >
       <result identifier='Result'>
          <string>SomeOtherString</string>
       </result>
    </complete-work-item>
    
  11. The abort work item command lets you cancel a process while it is running:
    <abort-work-item id='21' />
    

12.17. The MarshallerFactory

The MarshallerFactory is used to marshal and unmarshal Stateful Knowledge Sessions.

12.18. Marshaller Example

This is what a marsheller looks like in practice:
// ksession is the StatefulKnowledgeSession
// kbase is the KnowledgeBase
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Marshaller marshaller = MarshallerFactory.newMarshaller( kbase );
marshaller.marshall( baos, ksession );
baos.close();

12.19. Marshalling Options

Table 12.1. ** table title **

Option Description
ObjectMarshallingStrategy This interface provides implementations for marshalling and allows for greater flexibility.
SerializeMarshallingStrategy
This is the default strategy for calling the Serializable or Externalizable methods on a user instance.
IdentityMarshallingStrategy
This strategy creates an integer id for each user object and stores them in a Map, while the id is written to the stream.
When unmarshalling it accesses the IdentityMarshallingStrategy map to retrieve the instance. This means that if you use the IdentityMarshallingStrategy, it is stateful for the life of the Marshaller instance and will create ids and keep references to all objects that it attempts to marshal.

12.20. IdentityMarshallingStrategy Example

This is the code for using the IdentityMarshallingStrategy:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectMarshallingStrategy oms = MarshallerFactory.newIdentityMarshallingStrategy()
Marshaller marshaller =
  MarshallerFactory.newMarshaller( kbase, new ObjectMarshallingStrategy[]{ oms } );
marshaller.marshall( baos, ksession );
baos.close();

12.21. The ObjectMarshallingStrategyAcceptor

The ObjectMarshallingStrategyAcceptor is the interface that each Object Marshalling Strategy contains. The Marshaller has a chain of strategies. When it attempts to read or write a user object, it uses the ObjectMarshallingStrategyAcceptor to determine if they are to be used for marshalling the user object.

12.22. The ClassFilterAcceptor Implementation

The ClassFilterAcceptor implementation allows strings and wild cards to be used to match class names. The default is "*.*".

12.23. IdentityMarshallingStrategy with Acceptor Example

This is an example of using the IdentityMarshallingStrategy with the acceptor. Note that the acceptance checking order is in the natural order of the supplied array:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectMarshallingStrategyAcceptor identityAcceptor =
  MarshallerFactory.newClassFilterAcceptor( new String[] { "org.domain.pkg1.*" } );
ObjectMarshallingStrategy identityStrategy =
  MarshallerFactory.newIdentityMarshallingStrategy( identityAcceptor );
ObjectMarshallingStrategy sms = MarshallerFactory.newSerializeMarshallingStrategy();
Marshaller marshaller =
  MarshallerFactory.newMarshaller( kbase,
                                   new ObjectMarshallingStrategy[]{ identityStrategy, sms } );
marshaller.marshall( baos, ksession );
baos.close();

12.24. Persistence and Transactions in JBoss Rules

Long-term out of the box persistence with Java Persistence API (JPA) is possible with JBoss Rules. You will need to have some implementation of the Java Transaction API (JTA) installed. For development purposes you can use the Bitronix Transaction Manager as it's simple to set up and works embedded. For production use, JBoss Transactions is recommended.

12.25. Transaction Example

This is what performing a transaction looks like:
Environment env = KnowledgeBaseFactory.newEnvironment();
env.set( EnvironmentName.ENTITY_MANAGER_FACTORY,
         Persistence.createEntityManagerFactory( "emf-name" ) );
env.set( EnvironmentName.TRANSACTION_MANAGER,
         TransactionManagerServices.getTransactionManager() );
          
// KnowledgeSessionConfiguration may be null, and a default will be used
StatefulKnowledgeSession ksession =
  JPAKnowledgeService.newStatefulKnowledgeSession( kbase, null, env );
int sessionId = ksession.getId();
 
UserTransaction ut =
  (UserTransaction) new InitialContext().lookup( "java:comp/UserTransaction" );
ut.begin();
ksession.insert( data1 );
ksession.insert( data2 );
ksession.startProcess( "process1" );
ut.commit();

12.26. Using a JPA

Procedure 12.2. Task

  1. Make sure the environment is set with both the EntityManagerFactory and the TransactionManager.
  2. Launch the JPA from your GUI or command line.
  3. Use the id to load a previously persisted Stateful Knowledge Session. If rollback occurs the ksession state is also rolled back, you can continue to use it after a rollback.

12.27. Loading a StatefulKnowledgeSession with JPA

This is the code for loading a StatefulKnowledgeSession implementing the JPA:
StatefulKnowledgeSession ksession =
  JPAKnowledgeService.loadStatefulKnowledgeSession( sessionId, kbase, null, env );

12.28. Configuring JPA

To enable persistence several classes must be added to your persistence.xml, as in the example below:
<persistence-unit name="org.drools.persistence.jpa" transaction-type="JTA">
   <provider>org.hibernate.ejb.HibernatePersistence</provider>
   <jta-data-source>jdbc/BitronixJTADataSource</jta-data-source>       
   <class>org.drools.persistence.session.SessionInfo</class>
   <class>org.drools.persistence.processinstance.ProcessInstanceInfo</class>
   <class>org.drools.persistence.processinstance.ProcessInstanceEventInfo</class>
   <class>org.drools.persistence.processinstance.WorkItemInfo</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="update" />
         <property name="hibernate.show_sql" value="true" />
         <property name="hibernate.transaction.manager_lookup_class"
                      value="org.hibernate.transaction.BTMTransactionManagerLookup" />
   </properties>
</persistence-unit>

12.29. Configuring JTA DataSource

This is the code for configuring the JTA DataSource:
PoolingDataSource ds = new PoolingDataSource();
ds.setUniqueName( "jdbc/BitronixJTADataSource" );
ds.setClassName( "org.h2.jdbcx.JdbcDataSource" );
ds.setMaxPoolSize( 3 );
ds.setAllowLocalTransactions( true );
ds.getDriverProperties().put( "user", "sa" );
ds.getDriverProperties().put( "password", "sasa" );
ds.getDriverProperties().put( "URL", "jdbc:h2:mem:mydb" );
ds.init();

12.30. JNDI Properties

Bitronix also provides a simple embedded JNDI service, ideal for testing. To use it add a jndi.properties file to your META-INF and add the following line to it:
java.naming.factory.initial=bitronix.tm.jndi.BitronixInitialContextFactory

12.31. KnowledgeBase Namespaces

This is a list of namespaces you can attach to the KnowledgeBase for building purposes:
  • deftemplate
  • defrule
  • deffunction
  • and/or/not/exists/test conditional elements
  • Literal, Variable, Return Value and Predicate field constraints