Webcam Chat QuickBooks Advice international calling cards international phone cards
JavaBeat Certifications Certifications Kits Articles Tutorials Tips QNA Book Store Interview Questions SCJP 1.5 SCJP 1.6 SCWCD 5.0 SCBCD 5.0 SCEA SCJA Feeds
Kaspersky Anti-Virus 2011
Submit Your Blog Feedback Request Article Print Email

Drools JBoss Rules 5.0 - Human-readable Rules

Author : PacktPub
Topic : jboss books 
Pages :
Hibernate Books | Spring Books | JSF Books | Java Books
Title : Drools JBoss Rules 5.0 - Human-readable Rules
Publisher : PacktPub
Topic : jboss
Related : Hibernate, Spring, Struts, ejb
Javabeat : Tips, Java / J2EE Tutorials, Certifications

Drools JBoss Rules 5.0 Developer’s Guide

Business rules and processes can help your business by providing a level of agility and flexibility. As a developer, you will be largely responsible for implementing these business rules and processes effectively, but implementing them systematically can often be difficult due to their complexity. Drools, or JBoss Rules, makes the process of implementing these rules and processes quicker and handles the complexity, making your life a lot easier!

This book guides you through various features of Drools, such as rules, ruleflows, decision tables, complex event processing, Drools Rete implementation with various optimizations, and others. It will help you to set up the Drools platform and start creating your own business. It's easy to start developing with Drools if you follow our real-world examples that are intended to make your life easier.

Starting with an introduction to the basic syntax that is essential for writing rules, the book will guide you through validation and human-readable rules that define, maintain, and support your business agility. As a developer, you will be expected to represent policies, procedures, and constraints regarding how an enterprise conducts its business; this book makes it easier by showing you the ways in which it can be done.

A real-life example of a banking domain allows you to see how the internal workings of the rules engine operate. A loan approval process example shows the use of the Drools Flow module. Parts of a banking fraud detection system are implemented with Drools Fusion module, which is the Complex Event Processing part of Drools. This in turn, will help developers to work on preventing fraudulent users from accessing systems in an illegal way.

Finally, more technical details are shown on the inner workings of Drools, the implementation of the ReteOO algorithm, indexing, node sharing, and partitioning.

What This Book Covers

Chapter 1: This chapter introduces the reader to the domain of business rules and business processes. It talks about why the standard solutions fail at implementing complex business logic. It shows a possible solution in the form of a declarative programming model. The chapter talks about advantages and disadvantages of Drools. A brief history of Drools is also mentioned.

Chapter 2: This chapter shows us the basics of working with the Drools rule engine—Drools Expert. It starts with a simple example that is explained step-by-step. It begins with the development environment setup, writing a simple rule, and then executing it. The chapter goes through some necessary keywords and concepts that are needed for more complex examples.

Chapter 3: This chapter introduces the reader to a banking domain that will be the basis for examples later in this book. The chapter then goes through an implementation of a decision service for validating this banking domain. A reporting model is designed that holds reports generated by this service.

Chapter 4: This chapter shows how Drools can be used for carrying out complex data transformation tasks. It starts with writing some rules to load the data, continues with the implementation of various transformation rules, and finally puts together the results of this transformation. The chapter shows how we can work with a generic data structure such as a map in Drools.

Chapter 5: The focus of this chapter is on rules that are easy to read and change. Starting with domain specific languages, the chapter shows how to create a data transformation specific language. Next, it focuses on decision tables as another more user-friendly way of representing business rules. An interest rate calculation example is shown. Finally, the chapter introduces the reader to Drools Flow module as a way of managing the rule execution order.

Chapter 6: This chapter talks about executing the validation decision service in a stateful manner. The validation results are accumulated between service calls. This shows another way of interacting with a rule engine. Logical assertions are used to keep the report up-todate. Various ways of serializing a stateful session are discussed.

Chapter 7: This chapter talks about Drools Fusion—another cornerstone of the Drools platform is about writing rules that react to various events. The power of Drools Fusion is shown through a banking fraud detection system. The chapter goes through various features such as events, type declarations, temporal operators, sliding windows, and others.

Chapter 8: This chapter goes into more detail about the workflow aspect of the Drools platform. It is showed through a loan approval service that demonstrates the use of various nodes in a flow. Among other things, the chapter talks about implementing a custom work item, human task, or a sub-flow.

Chapter 9: The purpose of this chapter is to show you how to integrate Drools in a real web application. We'll go through design and implementation of persistence, business logic, and presentation layers. All of the examples written so far will be integrated into this application.

Chapter 10: The focus of this chapter is to give you an idea about the various ways of testing your business logic. Starting with unit testing, integration testing through acceptance testing that will be shown with the help of the Business Rules Management Server—Guvnor, this chapter provides useful advice on various troubleshooting techniques.

Chapter 11: This chapter shows integration with the Spring Framework. It describes how we can make changes to rules and processes while the application runs. It shows how to use an external build tool such as Ant to compile rules and processes. It talks about the rule execution server that allows us to execute rules remotely. It briefly mentions support of various standards.

Chapter 12: This chapter goes under the hood of the Drools rule engine. By understanding how the technology works, you'll be able to write more efficient rules and processes. It talks about the ReteOO algorithm, node sharing, node indexing, and rule partitioning for parallel execution.

Human-readable Rules

Business rules implementations presented so far were aimed mostly at developers. However, it is sometimes needed that these rules are readable and understandable by the business analysts. Ideally, they should be able to change the rules or even write new ones. An important aspect of business rules is their readability and user friendliness. Looking at a rule, you should immediately have an idea of what it is about. In this chapter, we'll look at Domain Specific Languages (DSLs), decision tables, and rule flows to create human-readable rules.

Domain Specific Language

The domain in this sense represents the business area (for example, life insurance or billing). Rules are expressed with the terminology of the problem domain. This means that domain experts can understand, validate, and modify these rules more easily.

You can think of DSL as a translator. It defines how to translate sentences from the problem-specific terminology into rules. The translation process is defined in a .dsl file. The sentences themselves are stored in a .dslr file. The result of this process must be a valid .drl file.

Building a simple DSL might look like:


	[condition][]There is a Customer with firstName
	{name}=$customer : Customer(firstName == {name})
	[consequence][]Greet Customer=System.out.println("Hello " +
	$customer.getFirstName());

Code listing 1: Simple DSL file simple.dsl.


	The code listing above contains only two lines (each begins with [).
	However, because the lines are too long, they are wrapped effectively
	creating four lines. This will be the case in most of the code listings.
	
	When you are using the Drools Eclipse plugin to write this DSL, enter the
	text before the first equal sign into the field called Language expression,
	the text after equal sign into Rule mapping, leave the object field blank
	and select the correct scope.

The previous DSL defines two DSL mappings. They map a DSLR sentence to a DRL rule. The first one translates to a condition that matches a Customer object with the specified first name. The first name is captured into a variable called name. This variable is then used in the rule condition. The second line translates to a greeting message that is printed on the console. The following .dslr file can be written based on the previous DSL:


	package droolsbook.dsl;
	import droolsbook.bank.model.*;
	expander simple.dsl
	rule "hello rule"
	when
		There is a Customer with firstName "David"
	then
		Greet Customer
	end

Code listing 2: Simple .dslr file (simple.dslr) with rule that greets a customer with name David.

As can be seen, the structure of a .dslr file is the same as the structure of a .drl file. Only the rule conditions and consequences are different. Another thing to note is the line containing expander simple.dsl. It informs Drools how to translate sentences in this file into valid rules. Drools reads the simple.dslr file and tries to translate/expand each line by applying all mappings from the simple.dsl file (it does it in a single pass process, line-by-line from top to bottom). The order of lines is important in a .dsl file. Please note that one condition/consequence must be written on one line, otherwise the expansion won't work (for example, the condition after the when clause, from the rule above, must be on one line).

When you are writing .dslr files, consider using the Drools Eclipse plugin. It provides a special editor for .dslr files that has an editing mode and a read-only mode for viewing the resulting .drl file. A simple DSL editor is provided as well.

The result of the translation process will look like the following screenshot:

This translation process happens in memory and no .drl file is physically stored. We can now run this example. First of all, a knowledge base must be created from the simple.dsl and simple.dslr files. The process of creating a package using a DSL is as follows (only the package creation is shown, the rest is the same as we've seen in Chapter 2, Basic Rules):

KnowledgeBuilder acts as the translator. It takes the .dslr file, and based on the .dsl file, creates the DRL. This DRL is then used as normal (we don't see it; it's internal to KnowledgeBuilder). The implementation is as follows:


	private KnowledgeBase createKnowledgeBaseFromDSL()
	throws Exception {
		KnowledgeBuilder builder =
		KnowledgeBuilderFactory.newKnowledgeBuilder();
		builder.add(ResourceFactory.newClassPathResource(
			"simple.dsl"), ResourceType.DSL);
		builder.add(ResourceFactory.newClassPathResource(
			"simple.dslr"), ResourceType.DSLR);
		if (builder.hasErrors()) {
			throw new RuntimeException(builder.getErrors()
				.toString());
		}
		KnowledgeBase knowledgeBase = KnowledgeBaseFactory
			.newKnowledgeBase();
		knowledgeBase.addKnowledgePackages(
			builder.getKnowledgePackages());
		return knowledgeBase;
	}

Code listing 3: Creating knowledge base from .dsl and .dslr files.

The .dsl and subsequently the .dslr files are passed into KnowledgeBuilder. The rest is similar to what we've seen before.

DSL as an interface

DSLs can be also looked at as another level of indirection between your .drl files and business requirements. It works as shown in the following figure:

The figure above shows DSL as an interface (dependency diagram). At the top are the business requirements as defined by the business analyst. These requirements are represented as DSL sentences (.dslr file). The DSL then represents the interface between DSL sentences and rule implementation (.drl file) and the domain model. For example, we can change the transformation to make the resulting rules more efficient without changing the language. Further, we can change the language, for example, to make it more user friendly, without changing the rules. All this can be done just by changing the .dsl file.

DSL for validation rules

The first three implemented object/field required rules from Chapter 2, Basic Rules, can be rewritten as:

  • If the Customer does not have an address, then Display warning message
  • If the Customer does not have a phone number or it is blank, then Display error message
  • If the Account does not have an owner, then Display error message for Account

We can clearly see that all of them operate on some object (Customer/Account), test its property (address/phone/owner), and display a message (warning/error) possibly with some context (account). Our validation.dslr file might look like the following code:


	expander validation.dsl
	rule "address is required"
		when
			The Customer does not have address
		then
			Display warning
	end
	rule "phone number is required"
		when
			The Customer does not have phone number or it is blank
		then
			Display error
	end
	rule "account owner is required"
		when
			The Account does not have owner
		then
			Display	error for Account
		end

Code listing 4: First DSL approach at defining the required object/field rules (validation.dslr file).

The conditions could be mapped like this:


	[condition][]The {object} does not have {field}=${object} : {object}(
	{field} == null )

Code listing 5: validation.dsl.

This covers the address and account conditions completely. For the phone number rule, we have to add the following mapping at the beginning of the validation.dsl file:


	[condition][] or it is blank = == "" ||

Code listing 6: Mapping that checks for a blank phone number.

As it stands, the phone number condition will be expanded to:


	$Customer : Customer( phone number == "" || == null )

Code listing 7: Unfinished phone number condition.

To correct it, phone number has to be mapped to phoneNumber. This can be done by adding the following at the end of the validation.dsl file:


	[condition][]phone number=phoneNumber

Code listing 8: Phone number mapping.

The conditions are working. Now, let's focus on the consequences. The following mapping will do the job:


	[consequence][]Display {message_type} for {object}={message_type}(
	kcontext, ${object} );
	[consequence][]Display {message_type}={message_type}( kcontext );

Code listing 9: Consequence mappings.

The three validation rules are now being expanded to the same .drl representation as we've seen in Chapter 2.

File formats

Before we go further, we'll examine each file format in more detail.

DSL file format

A l ine in a .dsl file has the following format:


	[<scope>][<Type>]<language expression>=<rule mapping>

Code listing 10: The format of one line in a .dsl file.

As we've already seen, an example of a line in DSL file might look like this:


	[condition][droolsbook.bank.model.Customer]The Customer does not have
	address=Customer(address == null)

Code listing 11: Sample line from DSL file (note that it is just one line that has been wrapped).

The scope can have the following values:

  • condition: Specifies that this mapping can be used in the condition part of a rule.
  • consequence: Specifies that this mapping can be used in the consequence part of a rule.
  • *: Specifies that this mapping can be used in both the condition and the consequence part of a rule.
  • keyword: This mapping is applied to the whole file (not just the condition or the consequence part). Used mainly when writing DSLs in languages other than English or to hide the package/import/global statements at the beginning of the file behind a business friendly sentence.

Type can be used to further limit the scope of the mapping. Scope and Type are used by the Drools Eclipse plugin to provide auto-completion when writing .dslr files (when pressing Ctrl + Space, only relevant choices are offered). This is especially useful with the multiple constraints feature (refer to the section, DSL for multiple constraints in a condition).

DSL supports comments by starting the line with the hash character, #. For example:


	#this is a comment in a .dsl file

RL file format

As a side note, in a .drl file, it is valid to write the whole rule on a single line. This allows us to write more complex DSLs because one sentence in .dslr file can be translated into multiple conditions—even the whole rule. For example, these are valid rules on a single line:


	rule "addressRequired" when Customer( address == null ) then
	warning(kcontext); end

Code listing 12: addressRequired rule on one line.

Make sure that you add spaces between Drools keywords. Another more complex example of a rule on one line:


	rule "studentAccountCustomerAgeLessThan" when Customer( eval (year
	sPassedSince(dateOfBirth) >= 27) ) and $account : Account( type ==
	Account.Type.STUDENT ) then error(kcontext, $account); System.out.
	println("another statement"); end

Code listing 13: studentAccountCustomerAgeLessThan rule on one line.

The preceding rule contains two conditions and two Java statements in the consequence block. There is also an optional and keyword between the conditions to make it more readable.

DSLR file format

A . dslr file contains the sentences written using the DSL. The .dslr file is very similar to the .drl file. One thing to note is that by prepending a line with a '>', we can turn off the expander for the line. This allows us to write a hybrid .dslr file that contains traditional DRL rules and DSL rules. For example, if we are not yet sure how to map some complex rule, we can leave it in its original .drl file format.

DSL for multiple constraints in a condition

We' ll go through more complex DSLs. Let's look at a standard condition for example:


	Account( owner != null, balance > 100, currency == "EUR" )

Code listing 14: Condition that matches some account.

It is difficult to write DSL that will allow us to create conditions with any subset of constraints from the code listing above (without writing down all possible permutations). The '-' feature comes to the rescue:


	[condition][]There is an Account that=$account : Account( )
	[condition][]-has owner=owner != null
	[condition][]-has balance greater than {amount}=balance > {amount}
	[condition][]-has currency equal to {currency}=currency == {currency}

Code listing 15: DSL using the '-' feature. This can create seven combinations of the constraints.

When the DSL condition starts with '-', the DSL parser knows that this constraint should be added to the last condition (in a .dslr file). With the preceding DSL, the following condition can be created:


	There is an Account that
	- has currency equal to "USD"
	"has balance greater than 2000"

Code listing 16: Condition using the '-' feature (in a .dslr file).

The '-' feature increases the fl exibility of the resulting language. It works just fine for simple cases involving only one pair of brackets. In case of multiple brackets in the condition, Drools always adds the constraint to the last pair of brackets. This may not always be what we want. We have to find a different way of specifying multiple constraints in a condition. We can also write our DSL in the following manner:


	[condition][]There is an Account that {constraints} = Account(
	{constraints} )
	[condition][]has {field} equal to {value}={field} == {value}
	[condition][]and has {field} equal to {value}=, {field} == {value}

Code listing 17: Flexible DSL that can be expanded to a condition with two field constraints.

With this DSL, the following DSLR can be written:


	There is an Account that has owner equal to null and has balance equal
	to 100

Code listing 18: DSLR that describes an account with two constraints.

If we want to have more conditions, we can simply duplicate the last line in the DSL. Remember? Translation is a single pass process.

Named capture groups

Som etimes, when a more complex DSL is needed, we need to be more precise at specifying what a valid match is. We can use named capture groups with regular expressions to give us the needed precision. For example:


	{name:[a-zA-Z]+}

Code listing 19: Name that matches only characters.


	Regular expressions (java.util.regex.Pattern) can be used not
	only for capturing variables but also within the DSL. For example, in
	order to carry out case insensitive matching. If we look at the DSL from
	code listing 15, the users should be allowed to type Account, account,
	ACCOUNT, or even aCcount in their .dslr files. This can be done by
	enabling the embedded case insensitive fl ag expression—(?i):
	[condition][]There is an (?i:account) that ....

	Another useful example is sentences that are sensitive to
	gender—(s)?he to support "he" and "she", and so on.

	In order to make the sentences space insensitive, Drools automatically
	replaces all spaces with \s+. Each \s+ matches one or more spaces. For
	example, the following line in a .dslr file will be successfully expanded
	by the DSL from code listing 15:
	There is an Account that ....

DSL for data transformation rules

We' ll now implement DSL for the data transformation rules from Chapter 4, Data Transformation. We'll reuse our rule unit tests to verify that we don't change the functionality of the rules but only their representation. The unit test class will be extended and the method for creating KnowledgeBase will be overridden to use the .dsl file and .dslr file as inputs. Rule names will stay the same. Let's start with the twoEqualAddressesDifferentInstance rule:


	rule twoEqualAddressesDifferentInstance
	when
		There is legacy Address-1
		There is legacy Address-2
		- same as legacy Address-1
	then
		remove legacy Address-2
		Display WARNING for legacy Address-2
	end

Code listing 20: Rule for removing redundant addresses (dataTransformation.dslr file).

The conditions can be implemented with the following DSL:


	[condition][] legacy {object}-{id} = {object}-{id}
	[condition][] There is {object}-{id} = ${object}{id} : Map( this["_
	type_"] == "{object}" )
	[condition][]- same as {object}-{id} = this == ${object}{id}, eval(
	${object}1 != ${object}2 )

Code listing 21: DSL for conditions (dataTransformation.dsl file).

The first mapping is a simple translation rule, where we remove the word legacy. The next mapping captures a map with its type. The last mapping includes the equality test with the object identity test. Mapping for consequences is as follows:


	[consequence][] legacy {object}-{id} = ${object}{id}
	[consequence][]Display {message_type_enum} for {object}=validationRepo
	rt.addMessage(reportFactory.createMessage(Message.Type.{message_type_
	enum}, kcontext.getRule().getName(), {object}));
	[consequence][]remove {object} = retract( {object} );

Code listing 22: DSL for consequences.

The first mapping just removes the word legacy. The second mapping adds a message to validationReport. Finally, the last mapping removes an object from the knowledge session. This is all we need for the twoEqualAddressesDifferentInstance rule.

As you can see, we started with the sentence in the domain specific language (code listing 1) and then we've written the transformation to refl ect the rules (from Chapter 4). In reality, this is an iterative process. You'll modify the .dslr and .dsl files until you are happy with the results. It is also a good idea to write your rules in standard .drl first and only then try to write a DSL for them.

We 'll move to the next rule, addressNormalizationUSA:


	rule addressNormalizationUSA
		when
			There is legacy Address-1
			- country is one of "US", "U.S.", "USA", "U.S.A"
		then
			for legacy Address-1 set country to USA
	end

Code listing 23: DSLR rule for normalizing address country field.

The rule just needs another constraint type:


	[condition][]- country is one of {country_list} = this["country"] in
	({country_list})

Code listing 24: Another condition mapping.

The consequence is defined with two mappings. The first one will translate the country to an enum and the second will then perform the assignment.


	[consequence][]set country to {country}=set country to Address.
	Country.{country}
	[consequence][]for {object}set {field} to {value} = modify( {object} )
	\{ put("{field}", {value} ) \}

Code listing 25: Consequence mapping for the country normalization rule.

Please note that the curly brackets are escaped. Moreover, the original rule used mvel dialect. It is a good idea to write your rules using the same dialect. It makes the DSL easier. Otherwise, the DSL will have to be "dialect aware".

The other country normalization rule can be written without modifying the DSL. We'll now continue with unknownCountry rule:


	rule unknownCountry
	Apply after address normalizations
		when
			There is legacy Address-1
			- country is not normalized
		then
			Display ERROR for legacy Address-1
	end

Code listing 26: DSLR representation of the unknownCountry rule.

The whole sentence Apply after address normalizations is mapped as a keyword mapping:


	[keyword][] Apply after address normalizations = salience -10

Code listing 27: salience keyword mapping.

Now, we can use the other rule attributes to achieve the same goal just by changing the DSL.

Additional mapping that is needed:


	[condition][]- country is not normalized = eval(!($Address1.
	get("country") instanceof Address.Country))

Code listing 28: Another condition mapping.

In the condition mapping, the $Address1 is hard-coded. This is fine for the rules that we have.

As you can imagine, the rest of the rules follow similar principles.

What we have achieved by writing this DSL is better readability. A business analyst can verify the correctness of these rules more easily. We could push this further by defining a complete DSL that can represent any concept from the problem domain. The business analyst will then be able to express any business requirement just by editing the .dslr file.

Recommended Books

Submit Your Blog Feedback Request Article Print Email

Java / J2EE Tutorials

Spring Framework

Hibernate Framework

JSF Framework

Struts Framework

Java Server Pages(JSP)

Servlets

Java / J2EE Design Patterns

SCJP

SCEA


JavaBeat Website (2004-2009), India
javabeat | advertise | about us | useful resources
Copyright (2004 - 2009), JavaBeat