Using Liferay's Rules Engine

EE Only Feature

Liferay Portal Enterprise Edition provides an implementation of a JSR-94 compliant rules engine. This rules engine is provided as a Web Plugin and is based on the popular open source Drools project.

Why Use a Rules Engine?

If you are not familiar with rules engines, you may be wondering why you would want to use one. In most applications, complex rule processing often takes the form of nested if-else blocks of code which can be very difficult to decipher and to maintain. If rules change, a developer must work with a business user to define the new rules. The developer must then read through the existing logic to understand what is happening and make the necessary modifications. The changes must then be recompiled, tested, and redeployed. A rules engine provides a means to separate the rules or logic of an application from the remaining code. Separating these rules provides several distinct advantages.

  • A rule engine allows for a more declarative style of programming where the rules define what is happening, without describing how it is happening. This makes it much easier to read than nested ‘if-else’ blocks of code. It’s also easier to make changes without introducing bugs in your code.

  • The rules are written in a language that is easier for non-developers to understand. This makes it easier for business users to validate and even modify the rules without having to involve developers.

  • A rule engine allows for changes to be made to the rules without requiring that you recompile your application. If your code must pass through a strict deployment workflow, this can be a huge time saver and can also save a significant amount of money.

After all this, you may be interested in using Liferay’s rules engine, so let’s get started with it.

Installation

The Drools Web Plugin is available to Liferay Enterprise Edition customers through Liferay Marketplace. Its name is Drools EE, and you’ll find it categorized as a Utility app.

The Drools Web Plugin provides a rules engine implementation, but by itself it doesn’t provide any observable changes to the portal user interface or any additional functionality. To see the rules engine in action, you can download and install a Sample Drools Portlet that contains two rule definitions that illustrate how to leverage the rules engine in your custom code. The Sample Drools Portlet is available through the Customer Portal.

Let’s examine the sample portlet to see how it works.

Configuring the Sample Drools Portlet

Begin by downloading and installing the Sample Drools Portlet. The Sample Drools Portlet is available to Liferay Enterprise Edition customers through the customer portal. The name is sample-drools-portlet, and you’ll find it in the list of web plugins.

After installation is complete, add the portlet to a page. Initially, the portlet indicates the name of the currently logged in user and a message that says there are no results. To see results in the portlet we’ll need to create and tag assets in the site to which you added the portlet.

Log in as an administrative user and navigate to the Control Panel. Once in the Control Panel, add a new Web Content entry to your site. Before publishing the Web Content entry, tag the article with west coast symposium. While still in the control panel, navigate to My Account and select the Address link on the right side of the screen. Enter a Canadian, Mexican, or US based address and save the record. Now, navigate back to the liferay.com site and the Web Content should be displayed in the Sample Drools Portlet.

The default rule that’s being evaluated displays a list of assets based on the current user’s address. For example, if the current user’s country is set to Canada, Mexico, or the United States, the Sample Drools Portlet displays a list of assets that have been tagged with the west coast symposium tag.

Creating a DSL can make your rules even easier for

business users to create and maintain your applications rules but does require some additional work up front. For additional information on creating a DSL for your problem domain please refer to the Domain Specific Languages section of the official Drools Documentation at http://docs.jboss.org/drools/release/5.2.0.Final/drools-expert-docs/html/ch05.html#d0e6217.

To see examples of a rules definition file, access the following directory in the Sample Drools Portlet sample-drools-portlet/WEB-INF/src/com/liferay/sampledrools/dependencies. To see how rules work in action we’ll look at the rule defined in rules_user_address_content.drl.

At first glance, this .drl file looks a lot like a Java class file. This example starts with a comment describing the rule. Single line comments can begin with either a # or // and multi-line comments begin with /* and end with */.

## ## Rules ## ## This sample program will return personalized content

based on the user’s ## addresses set in the My Account section of the Control Panel. ## ## For example, suppose the current user has an address in the United States and ## is a member of the Liferay site. All assets within the Liferay site ## that are tagged with “West Coast Symposium” will be returned. ##

Following the comments is a package declaration. The package declaration is optional in a Drools, but if it appears, it must be at the beginning of the file. The package denotes a collection of rules. Unlike Java, the package name does not represent a folder structure; it’s only a unique namespace. The ; at the end of the package declaration and all other statements is optional.

package com.liferay.sampledrools.dependencies;

After the package declaration are a series of import statements. Import statements in the rule file are used the same as the import statements in Java classes. Classes that are imported can be used in the rules.

import com.liferay.portal.kernel.util.KeyValuePair; import
com.liferay.portal.kernel.util.StringUtil; import
com.liferay.portal.model.Address; import com.liferay.portal.model.Contact;
import com.liferay.portal.model.User; import
com.liferay.portal.service.AddressLocalServiceUtil; import
com.liferay.portlet.asset.model.AssetEntry; import
com.liferay.portlet.asset.service.persistence.AssetEntryQuery; import
com.liferay.portlet.asset.service.AssetEntryLocalServiceUtil; import
com.liferay.portlet.asset.service.AssetTagLocalServiceUtil;

import java.util.ArrayList; import java.util.List;

The next line declares the dialect for the package. In this case, we will be using Java for any of the code expressions that we’ll encounter. The other possible value for dialect is MVEL. If necessary, the dialect can also be specified at the rule level to override the package level dialect.

dialect "java"

In this rule file, we have only a single function, which is listed next. Functions allow you to insert Java code that can be evaluated at run time into your rule file. Functions are commonly used as a as part of the consequence clause of the rule statement. Functions are similar to Java methods, but to define a function you use the function keyword. The function’s parameters and the return type are declared as they would be in a Java method. In this example, the getAssetEntries function returns a java.util.List object that contains AssetEntry objects based on the groupIds, classNameIds, and names provided in the function call.

function List getAssetEntries( long[] groupIds, long[] classNameIds,
String[] names) {
 
	long[] assetTagIds =
AssetTagLocalServiceUtil.getTagIds(groupIds, names);
 
	List<AssetEntry> assetEntries = new ArrayList<AssetEntry>();
 
	if (assetTagIds.length > 0) { AssetEntryQuery assetEntryQuery =
new AssetEntryQuery();
 		
		assetEntryQuery.setAnyTagIds(assetTagIds);
assetEntryQuery.setClassNameIds(classNameIds);
assetEntryQuery.setGroupIds(groupIds); assetEntryQuery.setVisible(true);
 
		assetEntries.addAll(AssetEntryLocalServiceUtil.getEntries(assetEntryQuery));
}

	return assetEntries; }

Alternatively, this function could’ve been written in a helper class and then imported using a function import. So if we had created a helper class called AddressContentHelper the import would look like this:

import function

com.liferay.sampledrools.dependencies.AddressContentHelper.getAsetEntries;

The last section of the rules file contains the actual rules. The syntax of a rule is very straightforward.

rule "name" attribute when condition then consequence end

The rule name is required and it must be unique within the package as declared above. Names with spaces must be enclosed in double quotes. It is considered a best practice to always enclose your rule names in double quotes.

rule "Initialize Rules"

The attributes section is optional. In our first rule, we have a single attribute called salience. The salience attribute is an integer value that acts as a priority weighting. The number can be positive or negative and defaults to the value of 0. Rules with a higher salience value are given a higher priority. It is considered a best practice to write attributes one to a line. In our example, the first rule is one that should be evaluated before any other so it is given a high salience value of 1000. None of our other rules have a salience attribute set, so they all default to a value of 0.

	salience 1000

The when keyword marks the conditional section of the rule. It is also referred to as the Left Hand Side (LHS). The when keyword is followed by zero or more condition elements that must be met before the consequence will be called. If there are no condition elements, then the condition is always evaluated as true.

The most common type of conditional element is a pattern. Patterns match against facts. Facts are statements that are known to be true. Facts can be inserted by the Java code that invokes the rules engine or they can be declared in the rule file itself.

In the first rule of our rule file (Initialize Rules), the only condition is that the rule must operate on a User object.

when user : User();

In more complex rules, the pattern may include constraints or may evaluate the properties of Java objects. For example, the second rule of this rule file is called Get European Symposium Content. This rule includes the following pattern which ensures that a user’s address contains the country name France, Germany, or Spain.

userAddress : Address(country.name in ("France", "Germany", "Spain"));

The consequence section of the rule follows the conditional section. It’s also known as the Right Hand Side (RHS) or action part of the rule. The consequence section begins with the keyword then and it is intended to modify the working memory data. Drools provides some convenience methods that make it easier to modify the working memory. In this rule, we use the insertLogical method which places a new object into the working memory and retracts it when there are no more facts supporting the truth of the current rule. After the consequence section of the rule, the rule is terminated with the keyword end.

then List<Address> userAddresses = AddressLocalServiceUtil.getAddresses(
user.getCompanyId(), Contact.class.getName(), user.getContactId());

	for (Address userAddress : userAddresses) {
insertLogical(userAddress); } end

Following the initial rule in our example, there are three additional rules that will be evaluated. Each of these rules evaluates the userAddress that was inserted into the working memory to determine what type of content should be displayed to the end user.

For additional documentation on the Drools rules language, please see the official Drools documentation at http://docs.jboss.org/drools/release/5.2.0.Final/drools-expert-docs/html/.

« Page CustomizationsSummary »
这篇文章有帮助吗?
0 人中有 0 人觉得有帮助