Java By Exception for Business Rules in ADF BC

Oracle ADF Business Components 11g has many new features for declarative business rules, allowing a "Java By Exception" approach. When can and should you use these declarative features, and when and how should you use Java to implement business rules in ADF BC?

 

What are Business Rules?

Let me first explain what I mean by business rules in this context.

Business rules are used to preserve data integrity, to prevent inconsistent data from being entered into the database. They are checks and/or data corrections you want to perform in your entity objects, triggered by an insert, update or delete of certain data.

The most common ones are validation rules or constraints: if the data changes do not satisfy a certain integrity rule, you return an error. For example: the End Date may not be before the Start Date. Another category of business rules is change event rules: if a certain change is made, you automatically want another change in the data to be made as well. For example: automatically converting a Code to uppercase (so that the integrity rule that a Code must be uppercase is implicitly maintained).

The white paper Business Rules in ADF BC defines business rules as follows (in the intro of section 6, "Business Rule Classification Scheme"):

A business rule is either

  • a restriction that applies to the state of the system, the change of the system state or the authorized use of the system,
  • or an automatic action that is triggered by a change to the system state
Photo credit: Naval Safety Center

My Preferred ADF BC 11g techniques for Business Rules

In Oracle JDeveloper 11g, ADF BC offers several new features for declarative validation rules support (see also the JDeveloper 11g New Features list):

  • Control when a validation rule is executed by specifying a conditional execution expression or triggering attributes (or by deferring the execution to transaction level) New!
  • Create parameterized error messages and warnings saved in external resource bundles New!
  • New validation rule types (in the below lists they are tagged with New! )

Let me describe the ADF BC techniques (with links to relevant sections in the Fusion Developer's Guide for ADF or to paragraphs below) that I refer to later in this post, in order of preference. In fact, I have two lists of preferred techniques, depending on the question: Should the rule be implemented for more than one entity?

Preferred Techniques for Rules that apply to Multiple Entities

If the answer is Yes, the rule should be implemented in more than one entity, then my preferred techniques are:

  1. Domain, see Section 34.1 Creating Custom, Validated Data Types Using Domains.
  2. Custom Validator, also known as Registered Rule, where you code a new validator type in Java, which you can then declaratively assign to an entity (or entity attribute). See How to create a Custom Validator.
  3. Overriding superclass method in the EntityImpl java class at the base class level (see Section 33.2 Creating a Layer of Framework Extensions, which you should do before creating any individual Entity Objects), using separate classes and interfaces for the actual business logic (see How to apply OO principles in EntityImpl).

Preferred Techniques for Rules that apply to Individual Entities

If the answer is No, the rule should be implemented in only one entity, then my preferred techniques are:

  1. Entity, Attribute, or Association Property, for example the Mandatory property of an attribute, or the History Column feature for tracking creation / last update info. You can even create your own History Type.
  2. Declarative Validation Rule that doesn't require any Groovy or Java coding (for more information see Section 7.3, Adding Validation Rules to Entity Objects and Attributes). If you need to postpone the calling of the rule to commit time, set "Defer execution to transaction level" when adding the rule to an entity or attribute.
    • Collection New! (validates based on aggregate values in a collection)
    • Compare (performs a logical comparison between the attribute and a value)
    • Key Exists New! (checks cache and database to see if value exists as a key in a certain entity, useful for logical Foreign Keys that are not checked in the database)
    • Length (compares the character or byte length of an attribute value to the specified size)
    • List (compares an attribute against a list of values that can be specified in several ways)
    • Range (tests for attribute values within specified minimum and maximum values)
    • Regular Expression (compares an attribute value against an expression mask)
    • UniqueKey New! (business-logic tier equivalent of a unique constraint in the database)
  3. Script Expression New! (declarative validation rule used for creating Groovy validation expressions). Applicable if the Groovy syntax and constructs, see Section 3.6 Overview of Groovy Support, are powerful enough to implement the rule, and the expression is limited in length.
  4. Method (declarative validation rule used to call any method defined by the entity object that accepts a parameter of the appropriate type and returns a boolean value, see Section 8.2 Implementing Validation and Business Rules Programmatically). If the method for this rule is more than a few lines of Java code, I prefer to implement the rule as a Custom Validator, so it can be implemented in a separate class with several methods.
  5. Custom Validator, also known as Registered Rule, where you code a new validator type in Java, which you can then declaratively assign to an entity (or entity attribute). See How to create a Custom Validator.
  6. Overriding superclass method in EntityImpl java class, at the level of an individual entity object (see Section 8.2 Implementing Validation and Business Rules Programmatically), using separate classes and interfaces for the actual business logic (see How to apply OO principles in EntityImpl).

Now these lists need a little explanation. Why do I prefer these, and when can or can't you use one of these techniques?

Why these techniques?

My preferences for certain techniques are based on the following assumptions:

  • XML-based is preferred to code-based (be it Groovy or Java code). There are several reasons for this:
    1. One benefit of using declarative validation (versus writing your own validation) is that the validation framework takes care of the complexities of batching validation exceptions, which frees you to concentrate on your application's specific validation rule logic. (Quote from Section 7.1 Introduction to Declarative Validation.)
    2. Another benefit of declarative validation, is the traceability of the rules. It is easier to see which rules are implemented in a certain entity. Just open an entity definition, go to the Validations, and see which declarative rules are listed. Even if not all rules can be listed there, it is a good starting point.
    3. In a later version of JDeveloper 11g, when MDS (Meta Data Services) is fully enabled, it will be possible to customize these XML-based business rules without having to change the source files (see chapter 33 of the draft developer's guide of a JDeveloper 11g Preview release).
    4. The recently started ADF Methodology Group discussed the term "Java By Exception" (see Extreme Reusability, an ADF development methodology proposed by Avrom Roy-Faderman). Applications are supposed to be entirely declarative, except for those few cases where declarative uses of the framework do not cover needed functionality. The idea is that you can harness the power of Java when you need it, and avoid the complexity at other times. At the ADF Methodology symposium, Steve Muench, very appropriately, pointed out that ADF, as well, is a "Java by exception" framework.
  • Generalize, Push Up, and Customize : Avrom's Extreme Reusability describes this practice as: Rather than writing a piece of Java code tailored to a highly particular need, consider the possibility of generalizing the code and pushing it up the class hierarchy to a custom framework class, allowing declarative customization of specific cases.
    I agree with this practice: if you expect that the functionality of the Java code is probably going to be needed in another place as well, possibly with a small variation, generalize it. This principle also implies that even if you can implement a rule with a declarative validator, but it needs to be done in more than one entity, then you still should create a generalized implementation so you avoid repeatedly entering the same properties like error message code, validator subtype, etc. Repetitive work increases the probability of mistakes, and the result is hard to maintain.
  • Use Object Oriented Design Principles when coding business rule logic in Java (I took these from the first chapter of the excellent book Head First Design Patterns):
    1. Encapsulate what varies, translated to: put the business rule logic in a separate class - see How to apply OO principles in EntityImpl
    2. Program to interfaces, not implementations, translated to: let the rule class implement an interface which you call from the EntityImpl - see How to apply OO principles in EntityImpl

    This avoids very large EntityImpl classes, in which it is hard to keep track of what was generated by ADF BC and what was added by you. If you follow these principles, there are only a few superclass methods that are likely to be customized (see Section 8.1 Introduction to Programmatic Business Rules).

    Cover of Head First Design Patterns book

When to use what technique?

The preferred techniques mentioned above may or may not be possible, depending on the type of business rule you need to implement. Besides the decision if the rule should be implemented for more than one entity or not, what matters as well is the complexity of the rule, and at which point in the ADF Entity Object's validation cycle it needs to be triggered.

Triggering moment

What you need to remember is that all techniques, except coding directly in the EntityImpl class, can only be called at the time a single Entity row is validated, or at the time all Entity rows are committed (the latter occurs if you defer the execution to transaction level, using a setting in the Entity "Add Validation Rule" dialog). That is the equivalent of overriding the entity superclass methods validateEntity or beforeCommit.

However, sometimes you need to trigger the rule from the superclass methods doDML, remove, or some other method. The Rule Classification Scheme of the Business Rules in ADF BC white paper can help: for the rule types "Delete", "Collection, no parent", "Collection within parent", "Change event with DML", and "Change Event without DML", it might not be possible to trigger them at validate or commit time.

Complexity of the rule

Furthermore, validation rules are often not suitable for change event rules (that trigger a data change in some other attribute or entity). It might interfere with the Validation Cycle (see Section 7.2 Understanding the Validation Cycle).

The complexity of the rule determines if it is possible to implement it using one of the most preferred techniques, like a Compare validator or a Range validator. If that is not possible, it might be possible to implement it using a Groovy Script validator. If that is not possible, it might be possible to implement it with a Method Validator with a small number of lines of Java code in the method, etcetera.

Algorithm to determine Best Rule Technique

If you apply all of these guidelines, you have the following summary algorithm for determining the best rule technique:

Algorithm for choosing ADF BC 11g Business Rule Technique

How to create a Custom Validator

See the Fusion Developer's Guide for Oracle Application Development Framework Section 34.10: Implementing Custom Validation Rules. Essentially you create a new Java class that extends AbstractValidator and implements the JboValidatorInterface. This forces you to implement a method validate(JboValidatorContext). You can specify custom rule properties for the things that vary if you need to implement this rule for more than one entity or attribute.

If the rule must be triggered at commit time as opposed to validate time, you must also implement the JboTransValidatorInterface. This forces you to implement a method validateMany in addition to validate, but in our test case it was never called, even if you set the "Defer execution to transaction level" when assigning the rule to an entity or attribute. Still, a logical implementation of this method would be to loop over all JboValidatorContexts and call validate for each:

    public void validateMany(ArrayList valCtxs) {
        JboValidatorContext evObj;
        for (int i = 0; i < valCtxs.size(); i++)
        {
           evObj = (JboValidatorContext) valCtxs.get(i);
           validate(evObj);
        }
    }

When you have written the Java class, you must register it as a rule in your Model project, and then you can declaratively assign it to one or more entities, possibly setting some entity-specific properties for the rule. Those properties can be exposed simply by creating getters and setters for them in the Java class.

Note: if the rule should be applied to all Entity Objects, without any specific properties to configure for each individual entity, then it is easier to use the technique of Overriding a method in EntityImpl, so you don't have to do any work in the individual entities.

Note 2: You could also use the JboValidatorInterface when the trigger time is not validateEntity or commit, but it requires instantiating a JboValidatorContext like this:

        JboValidatorInterface businessRule = new MyCustomValidator();
        JboValidatorContext entityContext =
            new JboValidatorContext(JboValidatorContext.TYP_ENTITY_OBJECT,
                                    this, getEntityDef().getFullName(), null,
                                    null, this);
        businessRule.validate(entityContext);

I find this less intuitive then the technique described below, and you don't have the added value that declarative rules have like traceability, bundling exceptions, and allowing changes through MDS. Besides, this codes gives a compiler warning, and it is not clear to me what the alternative for the deprecated constructor is:

   constructor JboValidatorContext(int, java.lang.Object, java.lang.String,
   oracle.jbo.AttributeDef, java.lang.Object, java.lang.Object) has been deprecated

It works fine, though, so if you like it you can use this approach instead of the one described in How to apply OO Principles in EntityImpl.

Thank you Steve Muench and Jan Kettenis for helping me to position this technique in 11g.

How to apply OO Principles in EntityImpl

This paragraph explains how I applied the Object Oriented Design Principles "Encapsulate what varies" and "Program to interfaces, not implementations", with the help of my colleague Gaurav Malhotra. I found that it helps separate out the rule implementation code from the Business Components classes, and clarifies the communication between the classes by using interfaces for different types of rules, if you want to use the technique of overriding a method in EntityImpl. In fact, the Custom Validator technique (see How to create a Custom Validator) also applies these OO principles.

Note: if the rule applies to multiple entities but not exactly the same for each entity, you can use Custom Properties in the individual entities to configure your business rule.

Take the rule code out of the EntityImpl class

One of the most complex rules we had in a large project involved a private helper class, several static final constants, and several helper methods that need to be called several times. So you can imagine that it is not a good idea to put all that code in the EntityImpl class, mixed in with the code for other rules and/or the ADF BC framework generated code.

Borrowing a phrase from Antonio García's description of the Strategy Pattern, it is much simpler to keep track of them if each behaviour (in our case business rule) is a separate class, and not buried in the body of some method. That is how I interpreted the principle "Encapsulate what varies".

Inject the Entity Row

Now, in the business rule logic we probably have to call several EntityImpl methods for the row that triggered the rule, like the entity's getters, or the superclass generic getAttribute method. How do you do that if the rule logic is in a separate class?

The answer is: inject the Entity row into the rule class, using a setEntity method. The rule class can then call entity methods by coding getEntity().someMethod(). This way you can also retrieve values of Custom Properties of Entities in case of generic rules that apply to multiple entities: getEntity().getEntityDef().getProperty(customPropertyName).

Define contract with caller using an Interface

Then, apply the "Program to Interfaces" principle by specifying an interface that defines which rule class methods should be callable from the EntityImpl class and which not. If you have multiple rules that have to be implemented in the EntityImpl, you can probably re-use the same interface for several rules.

An example of such an interface is the general-purpose EntityRule interface (note the use of a Java 5 generic method):

public interface EntityRule<T> {
    public void setEntity(T entity);
    public void process();
}

An implementation of this interface might be:

public class MyComplexRuleImpl implements EntityRule<MyEntityImpl>

You call the rule by coding in the appropriate method of your MyEntityImpl class:

     EntityRule myComplexRule = new MyComplexRuleImpl();
     myComplexRule.setEntity(this);
     myComplexRule.process();

Of course you can have multiple RuleImpl classes each representing a different Entity Rule, which can be called from different places in the EntityImpl.

Benefits of this technique

Using this approach you can give complex rules their own class, while still allowing calls to framework methods like the Entity's getters. In the rule class you can throw an exception if the validation fails. The use of an interface makes it clear how to call such a rule, without knowing the inner workings of the rule.

Comments:

I very much like this, particularly the "How to Apply OO Principles in EntityImpl" for rules that cannot fire during the validation cycle or at commit time (in other words, I think it's vastly superior to hand-instantiating a validation context, though I still prefer custom validators for no-side-effect validation). With an appropriate entity-rule factory method in a base entity object class, you could probably even figure out a way to call EntityRules declaratively at various times in the entity object lifecycle (say, after creation of a new row, or change of various attribute values). For regular validation, the value of business logic groups is also worth noting, particularly since you're talking about the Strategy pattern. Rather than a single complex rule with lots of if-else blocks, you can create various simpler rules, and attach them to units of a business logic group based on a (calculated, if the condition is more complex than "columnValue==literal") discriminator. The business logic units effectively correspond to validation strategies chosen based on the discriminator value.

Posted by Avrom on November 21, 2008 at 07:31 AM PST #

Sandra, What a beautiful piece of work. Complete, conscise and very well explained. A very useful overview. Did you get the Collection validations to work by the way? I believe I was not even successful in specifying a collection rule and had to resort to Groovy to achieve the desired effect. By the way: Did you notice how the Groovy expressions can look very much like RuleSLang? Best regards, Lucas

Posted by Lucas Jellema on November 21, 2008 at 02:47 PM PST #

@Avrom: Thank you for your compliments! And thanks for pointing out the Business Logic Groups, I had not really investigated that option yet. I can see how in some cases they can be very helpful. For those who are interested: more information can be bound in the Fusion Developer's Guide section 4.8. Defining Business Logic Groups. Good point about the rule factory in the Entity base class. I also thought about turning it into a real Strategy Pattern like you said: determining dynamically which rules to fire at which points in the cycle. I decided that in our current project it is not worth while, because there are only a few rules that end up being implemented with this technique. Though I agree that it can certainly be worth the effort in case you have a lot of those rules. kind regards, Sandra Muller

Posted by Sandra Muller on November 22, 2008 at 03:22 AM PST #

@Lucas: Thank you for your very nice compliments! I really appreciate it. I must admit that I did not actually implement any Collection validations. I tried to enter one, and only seemed to get values in the dropdown lists for Accessors and Attributes for composite associations. It seems to be a possible solution for the rule category Collection within Parent (from the white paper Business Rules in ADF BC), if it is a composite association (which is not always the case for these rules), but I cannot speak from experience yet. Regarding CDM RuleSLang: I had not made the link with Groovy expressions yet, but now that you mention it: Yes, it has similarities! For those who don't know RuleSLang: back when Lucas was a member of the Headstart team at Oracle, he wrote a CDM RuleSLang white paper about how it would be nice if rule analysis could be done in a (pseudo) language that would be unambiguous and easy to understand for everybody. Examples of CDM RuleSLang that were based on Object Constraint Language (OCL), which in turn is part of the UML standard:
context Project inv BR_PRJ005_TPL:
   self.start date <= self.end date
context Employee inv BR_EMP006_TPL:
   job = 'SALESMAN' implies commission isNotNull
So business rule analysts: if you express the rule requirements directly in Groovy-like syntax in terms of the attributes of an entity, you can avoid misinterpretations and you force yourself to think in a more exact way how the rule should be implemented! kind regards, Sandra

Posted by Sandra Muller on November 22, 2008 at 04:12 AM PST #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Java EE Consultants - JHeadstart, ADF, JSF

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today