Red Hat Training

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

Chapter 5. Validation

5.1. Rules in Smooks

In Smooks, rules are a general concept, not something specific to a particular cartridge.
You can configure and reference a RuleProvider from other components.

Note

The only cartridge that uses rules functionality is the validation cartridge.
Rules are centrally defined through ruleBases. A single Smooks configuration can refer to many ruleBase definitions. A rulesBase configuration has a name, a rule src and a rule provider.
The format of the rule source is entirely dependent on the provider implementation. The only requirement is that the individual rules be uniquely named (within the context of a single source) so they can be referenced.

5.2. Configuring Rules in Smooks

Here is an example ruleBase configuration:
<?xml version="1.0"?>
<smooks-resource-list  xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"  xmlns:rules="http://www.milyn.org/xsd/smooks/rules-1.0.xsd">
 
    <rules:ruleBases>
        <rules:ruleBase name="regexAddressing" src="/org/milyn/validation/address.properties" provider="org.milyn.rules.regex.RegexProvider" />
        <rules:ruleBase name="order" src="/org/milyn/validation/order/rules/order-rules.csv" provider="org.milyn.rules.mvel.MVELProvider"/>
    </rules:ruleBases>
 
</smooks-resource-list>

5.3. Mandatory Configurations for the rules:ruleBase Configuration Element

  • name: this is used by other components to refer to this rule.
  • src: this can be a file or anything else that is meaningful to the RuleProvider.
  • provider: This is the actual provider implementation. In the configuration above, there is one RuleProvider that uses regular expressions but you can specify multiple ruleBase elements and have as many RuleProviders as you need. .

5.4. Rule Providers

Rule providers implement the org.milyn.rules.RuleProviderinterface.
Smooks comes pre-configured to support two RuleProvider implementations:
  • RegexProvider
  • MVELProvider
You can also create your own RuleProvider implementations.

5.5. The RegexProvider

The RegexProvider utilises regular expressions. It allows you to define low-level rules specific to the format of selected data fields in the message being filtered. For example, it may be applied to a particular field to validate the syntax to make sure the right e-mail address is being used.

5.6. Configuring a Regex ruleBase

  1. Use this example code to configure a Regex ruleBase:
    <?xml version="1.0"?>
    <smooks-resource-list  xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"  xmlns:rules="http://www.milyn.org/xsd/smooks/rules-1.0.xsd">
     
        <rules:ruleBases>
            <rules:ruleBase name="customer" src="/org/milyn/validation/order/rules/customer.properties" provider="org.milyn.rules.regex.RegexProvider"/>
        </rules:ruleBases>
     
    </smooks-resource-list>
    
  2. Define the Regex expressions in a standard .properties file format. The following customer.properties Regex rule definition file example shows you how:
    # Customer data rules...
    customerId=[A-Z][0-9]{5}
    customerName=[A-Z][a-z]*, [A-Z][a-z]
    

5.7. The MVEL Provider

The MVEL provider allows you to define rules as MVEL expressions. These expressions are executed over the contents of the Smooks Javabean context. You should to bind data from the message being filtered into Java objects in the Smooks bean context.
MVEL allows you to define more complex rules on message fragments. (Such as "Is the product in the targeted order item fragment within the age eligibility constraints of the customer specified in the order header details?")

5.8. Configuring an MVEL ruleBase

  1. To configure an MVEL ruleBase, see the code below:
    <?xml version="1.0"?>
    <smooks-resource-list  xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"  xmlns:rules="http://www.milyn.org/xsd/smooks/rules-1.0.xsd">
     
        <rules:ruleBases>
            <rules:ruleBase name="order" src="/org/milyn/validation/order/rules/order-rules.csv" provider="org.milyn.rules.mvel.MVELProvider"/>
        </rules:ruleBases>
     
    </smooks-resource-list>
    
  2. You must store your MVEL rules in CSV files. The easiest way to edit these files is through a spreadsheet application such as LibreOffice Calc or Gnumeric. Each rule record contains a rule name and an MVEL expression.
  3. If you wish to create comment and header rows, prefix the first field with a hash (#) character.

5.9. The Smooks Validation Cartridge

The Smooks validation cartridge works with the rules cartridge to provide rules-based fragment validation.
This allows you to perform detailed validation on message fragments. As with everything in Smooks, the validation functionality is available across all supported data formats. This means you can perform strong validation on not just XML data, but also on EDI, JSON, CSV and so on.
Validation configurations are defined by the http://www.milyn.org/xsd/smooks/validation-1.0.xsd configuration namespace.
Smooks supports a number of different rule provider types and these can all be used by the Validation Cartridge. Each of these provide a different level of validation but they are all configured in exactly the same way. The Smooks Validation Cartridge sees a rule provider as an abstract resource that it can aim at message fragments in order to validate them.

5.10. Configuring Validation Rules

To configure a validation rule you need to specify the following:
  • executeOn: this is the fragment on which the rule is to be executed.
  • excecuteOnNS: this is the fragment namespace to which the executeOn belongs.
  • name: this is the name of the rule to be applied. This is a composite rule name that refers to a ruleBase and ruleName combination in a dot delimited format (in other words ruleBaseName.ruleName).
  • onFail: this determines the severity of a failed match.
Here is a sample validation rule configuration:
    <validation:rule executeOn="order/header/email" name="regexAddressing.email" onFail="ERROR" />

5.11. Configuring Validation Exceptions

  1. You can set a maximum number of validation failures per Smooks filter operation. (An exception will be thrown if this maximum is exceeded.) Validations configured with OnFail.FATAL will always throw an exception and stop processing.
    To configure the maximum validation failures, add this code to your Smooks configuration:
    <params>
        <param name="validation.maxFails">5</param>
    </params>
    
  2. The onFail attribute in the validation configuration specifies what action is to be taken. This determines how validation failures are to be reported. To utilize it, modify the following options to suit your needs:
    • OK: Use this to save the validation as "okay". By calling ValidationResults.getOks all validation warnings will be returned. This option is useful for content-based routing.
    • WARN: Use this to save the validation as a warning. By calling ValidationResults.getWarnings all validation warnings will be returned.
    • ERROR: Use this to save the validation as an error. By calling ValidationResults.getErrors you will return all validation errors.
    • FATAL: Use this to throw a ValidationException as soon as a validation failure occurs. If you call ValidationResults.getFatal you will see the fatal validation failure.

5.12. Rule Bases

  • Use a composite rule name in the following format for a rule base:
    <ruleProviderName>.<ruleName>
    
    • ruleProviderName identifies the rule provider and maps to the name attribute in the ruleBase element.
    • ruleName identifies a specific rule the rule provider knows about. This could be a rule defined in the src file.

5.13. Smooks.filterSource

The Smooks.filterSource captures the validation results. When the filterSource method returns, the ValidationResult instance will contain all validation data.
This code shows how to make Smooks perform message fragment validation:
ValidationResult validationResult = new ValidationResult();
 
smooks.filterSource(new StreamSource(messageInStream), new StreamResult(messageOutStream), validationResult);
 
List<OnFailResult> errors = validationResult.getErrors();
List<OnFailResult> warnings = validationResult.getWarnings();
As you can see, individual warning and error validation results are made available from the ValidationResult object in the form of OnFailResult instances, each of which provide details about an individual failure.
The Validation Cartridge also allows you to specify localized messages relating to validation failures. You can define these messages in standard Java ResourceBundle files (which use the .properties format).

Note

The base name of the validation message bundle is derived from the rule source ("src") by dropping the rule source file extension and adding an extra folder named i18n. For example, if you have an MVEL ruleBase source of /org/milyn/validation/order/rules/order-rules.csv, the corresponding validation message bundle base name will be /org/milyn/validation/order/rules/i18n/order-rules.

5.14. The Validation Cartridge and Messages

The validation cartridge lets you apply FreeMarker templates to the localized messages, allowing messages to contain contextual data from the bean context, as well as data about the actual rule failure. You must prefix FreeMarker-based messages with ftl: and reference the contextual data using standard FreeMarker notation. The beans from the bean context can be referenced directly, while you can refer to the RuleEvalResult and rule failure path through the ruleResult and path beans.
Here is an example that uses RegexProvider rules which shows how Smooks can be used to perform validation of message fragment data:
customerId=ftl:Invalid customer number '${ruleResult.text}' at '${path}'.  Customer number must match pattern '${ruleResult.pattern}'.

5.15. Types of Validation

Smooks performs two types of validation using two different kinds of validation rules:
  • message field value/format validation using regular expressions defined in a .properties file RuleBase. This, for example, can be to validate a field as being a valid e-mail address.
  • business rules validation using MVEL expressions defined in a .csv file RuleBase. This can, for example, be validating that the total price of an order item on an order (price * quantity) does not breach some predefined business rule.

5.16. Running Validation Rules

  • To run validaton rules, go to the example root folder and execute:
    1. mvn clean install
    2. mvn exec:java

5.17. RuleBase Example

In this example, there is an XML message containing a collection of order items. (This functionality works similarly for all other data formats supported by Smooks.):
<Order>
	<header>
        <orderId>A188127</orderId>
        <username>user1</username>
        <name>
            <firstname>Bob</firstname>
            <lastname>Bobington</lastname>
        </name>
        <email>bb@awesomemail.com</email>
        <state>Queensland</state>
    </header>
    <order-item>
        <quantity>1</quantity>
        <productId>364b</productId>
        <title>A Great Movie</title>
        <price>29.95</price>
    </order-item>
    <order-item>
        <quantity>2</quantity>
        <productId>299</productId>
        <title>A Terrible Movie</title>
        <price>29.95</price>
    </order-item>
</Order>

5.18. Message Data Validation

  1. When processing an order message, you should perform a number of validations. First, check that the supplied username follows a format of an upper case character, followed by five digits (for example, S12345 or G54321). To perform this validation, you should use regular expression.
  2. Next, check that the supplied e-mail address is in a valid format. Use a regular expression to check it.
  3. Confirm that each order item's productId field follows a format of exactly three digits (such as 123). Use a regular expression to do this.
  4. Finally, you need to confirm that the total for each order item does not exceed 50.00 (price * quantity is not greater than 50.00). Perform this validation using an MVEL expression.

5.19. Using an MVEL Expression

  1. To use an MVEL expression on a rule set, divide the Regex rules and place them in two separate .properties files.
  2. Drop these rules into the example rules directory.
  3. Put the MVEL expression in a .csv file, also in the rules directory.
    The customer-related Regex rules that go in the customer.properties file look like this:
    # Customer data rules...
    customerId=[A-Z][0-9]{5}
     
    # Email address...
    email=^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$
    
  4. Insert the product-related Regex rule in the product.properties file:
    # Product data rules...
    productId=[0-9]{3}
    
  5. Insert the MVEL expression for performing the order item total check into the order-rules.csv file.

    Note

    The easiest way to edit a .csv file is through using a spreadsheet application like LibeOffice Calc or Gnumeric.
  6. Create resource bundle .properties files for each of the rule source files.

    Note

    The names of these files are derived from the names of their corresponding rule files.
    The message bundle for the rules defined in rules/customer.properties is located in the rules/i18n/customer.properties file:
    customerId=ftl:Invalid customer number '${ruleResult.text}' at '${path}'.  
    <!--  Customer number must begin with an uppercase character, followed by 5 digits. -->
    email=ftl:Invalid email address '${ruleResult.text}' at '${path}'.  
    <!--  Email addresses match pattern '${ruleResult.pattern}'. -->
    
    The message bundle for the rule defined in rules/product.properties is located in the rules/i18n/product.properties file:
    # Product data rule messages...
    productId=ftl:Invalid product ID '${ruleResult.text}' at '${path}'. 
    <!--  Product ID must match pattern '${ruleResult.pattern}'. -->
    
    The message bundle for the rule defined in rules/order-rules.csv is located in the rules/i18n/order-rules.properties file:
    # <!--  Order item rule messages. The "orderDetails" and "orderItem" beans are populated by Smooks bindings - see config in following section. -->
    order_item_total=ftl:Order ${orderDetails.orderId} 
    <!-- contains an order item for product ${orderItem.productId} with a quantity of ${orderItem.quantity} and a unit price of ${orderItem.price}. This exceeds the permitted per order item total. -->
    
  7. Apply this validation to the rules:
    <?xml version="1.0"?>
    <smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
                          xmlns:rules="http://www.milyn.org/xsd/smooks/rules-1.0.xsd"
                          xmlns:validation="http://www.milyn.org/xsd/smooks/validation-1.0.xsd"
                          xmlns:jb="http://www.milyn.org/xsd/smooks/javabean-1.2.xsd">
     
        <params>
            <!-- Generate a ValidationException if we get more than 5 validation failures... -->
            <param name="validation.maxFails">5</param>
        </params>
     
        <!-- Define the ruleBases that are used by the validation rules... -->
        <rules:ruleBases>
            <!-- Field value rules using regex... -->
            <rules:ruleBase name="customer" src="rules/customer.properties" provider="org.milyn.rules.regex.RegexProvider"/>
            <rules:ruleBase name="product" src="rules/product.properties" provider="org.milyn.rules.regex.RegexProvider"/>
     
            <!-- Order business rules using MVEL expressions... -->
            <rules:ruleBase name="order" src="rules/order-rules.csv" provider="org.milyn.rules.mvel.MVELProvider"/>
        </rules:ruleBases>
     
        <!-- Capture some data into the bean context - required by the business rule validations... -->
        <jb:bean beanId="orderDetails" class="java.util.HashMap" createOnElement="header">
            <jb:value data="header/*"/>
        </jb:bean>
        <jb:bean beanId="orderItem" class="java.util.HashMap" createOnElement="order-item">
            <jb:value data="order-item/*"/>
        </jb:bean>
     
        <!-- Target validation rules... -->
        <validation:rule executeOn="header/username" name="customer.customerId" onFail="ERROR"/>
        <validation:rule executeOn="email" name="customer.email" onFail="WARN"/>
        <validation:rule executeOn="order-item/productId" name="product.productId" onFail="ERROR"/>
     
        <validation:rule executeOn="order-item" name="order.order_item_total" onFail="ERROR"/>
     
    </smooks-resource-list>
    
  8. Execute from the example's Main class using this code:
    protected static ValidationResult runSmooks(final String messageIn) throws IOException, SAXException, SmooksException {
        // Instantiate Smooks with the config...
        final Smooks smooks = new Smooks("smooks-config.xml");
     
        try {
            // Create an exec context - no profiles....
            final ExecutionContext executionContext = smooks.createExecutionContext();
            final ValidationResult validationResult = new ValidationResult();
     
            // Configure the execution context to generate a report...
            executionContext.setEventListener(new HtmlReportGenerator("target/report/report.html"));
     
            // Filter the input message...
            smooks.filterSource(executionContext, new StringSource(messageIn), validationResult);
     
            return validationResult;
        }
        finally {
            smooks.close();
        }
    }