As Published In
Oracle Magazine
May/June 2007

DEVELOPER: Frameworks


Beyond Declarative Validation

By Steve Muench Oracle Employee ACE

Implement more-complex business logic using Java.

When building enterprise applications, you have to think about more than just the design of your user interfaces. A lot of your development effort must be spent on efficiently implementing business logic. When you use Oracle Application Development Framework (Oracle ADF) Business Components, it is best practice to encapsulate as much of this logic as possible into the entity objects that constitute your domain business object layer. If you do so, your application will validate and process your important business data in a consistent way, regardless of how it is accessed.

The declarative nature of Oracle ADF makes it possible to implement a lot of basic functionality without writing code. However, the most interesting business logic you write will often involve more-complex routines that require some amount of Java code. In this column, I take a look at the basics of writing entity object business logic in Java.

Programmatic Business Logic Basics

In past columns, I've discussed the built-in declarative validation features of Oracle ADF entity objects. To complement those features, you can also use method validators to implement encapsulated business logic within your entity objects using custom Java code. Method validators are validation rules that trigger a corresponding method in your entity object's Java class to enforce a business constraint. You can create both attribute-level and entity-level method validators. Attribute-level method validators trigger when a specific attribute's value changes, whereas entity-level validators trigger when the entire entity row requires validation. Each entity object tracks whether its data is valid and automatically triggers validation rules at the appropriate time to ensure that it saves only valid data. When an application retrieves an existing entity row from the database, it assumes that the data is valid. However, when it creates a new entity row or modifies a persistent attribute of an existing entity row, it invalidates the entity to ensure the enforcement of your validation rules.

Your validation rules can get complex if you have a hierarchy of entity objects. To make your work easier, Oracle ADF invalidates a parent entity whenever composed child entity rows are created, modified, or deleted. This feature makes it easy for you to use a parent entity to enforce validation rules related to any or all of its child rows. For example, a purchase order entity object might enforce a rule saying that the sum of its line items cannot exceed a certain amount. Whenever a line item is added, modified, or removed, this validation rule on the parent purchase order will trigger to ensure that the data is consistent with business policies.

Entity objects also expose many programming events to which you can attach additional business logic. For example, you can write custom code that assigns default values for attributes and have this code execute whenever an entity object is created or saved. I show simple examples of some of these techniques in the sections that follow.

Creating Method Validators

You can add any number of attribute-level and entity-level method validators to your entity objects, as long as they each trigger a distinct method in your code. The name of every validation method must begin with the word validate . Aside from that rule, however, you can name your methods in any way you want.

To create an attribute-level method validator, start by opening the Entity Object Editor in Oracle JDeveloper. If your entity object does not yet have a custom Java class, first visit the Java panel and click the check box to generate a Java file for the Entity Object Class . Click Apply to generate the *.java file. Next, visit the Validation panel and select the attribute you want to validate. Click New to add a validation rule. In the Add Validation Rule dialog box, which appears, select the Method Validator type from the Rule list (as shown in Figure 1).

 

figure 1
Figure 1: Adding an attribute-level method validator


Next, specify a method to apply as your validator rule. You have two choices: 

  • If you already have a method in your entity object's custom Java class with the appropriate signature, it will appear in Select a Method to Apply as a Rule . You can select this method after unchecking Create and Select Method .

  • If you leave Create and Select Method checked, you can enter any method name in the Method Name field (but make sure it begins with the word validate ).

Finally, input the error message text for the default locale the end user will see if this validation rule fails. Oracle JDeveloper will add that message to your entity object's message bundle class.

When you add a new method validator, Oracle JDeveloper updates the entity object's XML component definition to reflect the new validation rule. If you created a new method, that method will also be added to the entity object's custom Java class.

Listing 1 illustrates a simple attribute-level validation rule that ensures that the AssignedDate of a service request is a date in the current month. As the code shows, the attribute-level method validator accepts a candidate attribute value as an argument to the method. Note that the return value of the compareTo() method equals zero (0) if the two dates are equal, negative one (-1) if the first date is less than the second, or positive one (1) if the first date is greater than the second. The validation method in Listing 1 returns true if the data is valid (meaning that the service date is in the current month) and false if not. If the code returns false, the application will throw an exception with the error message text you supplied when you defined the validation rule.

Code Listing 1: Simple attribute-level method validator 

// In ServiceRequestImpl.java in SRDemo Sample
public boolean validateAssignedDate(Date  data) {
  if (data != null && data.compareTo(getFirstDayOfCurrentMonth()) <= 0) {
    return false;
  }
  return true;
}

If your business rule needs to reference the value of multiple attributes, you should create an entity-level method validator instead. The process of creating an entity-level method validator is similar to that of creating an attribute-level validator. The only difference is that when you visit the Validation panel of the Entity Object Editor, you select the root node, which represents the entity object itself instead of a particular attribute in the tree. Listing 2 illustrates a simple entity-level validation rule that ensures that the AssignedDate of a service request comes after the RequestDate. Note that this method signature accepts no arguments and references the values of any attributes it needs by calling the respective attribute getter methods.

Code Listing 2: Simple entity-level method validator 

public boolean validateAssignedDateAfterRequestDate() {
  Date assignedDate = getAssignedDate();
  Date requestDate  = getRequestDate();
  if (assignedDate != null && assignedDate.compareTo(requestDate) < 0) {
    return false;
  }
  return true;
}

Assigning Default Values

When declarative static default values don't meet your needs, you can programmatically assign default values in your entity object. You can execute this logic at either create time or save time, depending on your requirements.

The create() method provides an entity object event you can handle to initialize default values the first time an entity row is created. Listing 3 shows how you can override the create() method, in this example taken from the ServiceHistory entity object in the ADFBC version of the Oracle sample application called SRDemo. (The SRDemo sample is a moderately complex data entry application that showcases the many features of Oracle ADF. It is available from the Oracle JDeveloper update center on OTN and is explained in Oracle ADF Developer's Guide for Forms/4GL Developers .) This create() method calls the attribute setter methods to populate the SvhType, CreatedBy, and LineNo attributes in a new service history entity row.

Code Listing 3: Programmatically defaulting attribute values for new rows 

// In ServiceHistoryImpl.java in SRDemo sample
protected void create(AttributeList nameValuePair) {
  super.create(nameValuePair);
  setSvhType(getDefaultNoteType());
  setCreatedBy(getCurrentUserId());
  setLineNo(new Number(getServiceRequest().getMaxHistoryLineNumber()+1));
}

To assign programmatic defaults for entity object attribute values before a row is saved, override the prepareForDML() method instead of the create() method. In the prepareForDML() method, you can call the appropriate attribute setter methods to populate your desired attribute values. If you want to perform the attribute assignment only during INSERT, UPDATE, or DELETE operations, you can compare the value of the operation parameter passed to this method with the integer constants DML_INSERT, DML_UPDATE, and DML_DELETE, respectively.

Next Steps



READ more about Oracle JDeveloper 10g and Oracle ADF
oracle.com/technetwork/products/jdev
oracle.com/technetwork/products/adf/learnadf.html

 DOWNLOAD Oracle JDeveloper 10g

Listing 4 shows the overridden prepareForDML() method used by the ServiceHistory entity object in the SRDemo sample. This example automatically changes the status of a service request whenever a user creates a service history note, subject to a set of rules. If a new history note is added by a customer, this code changes the status of a pending or closed service request to "open." If a technician adds a new history note and the service request status is "open," the code will change the status to "pending."

Code Listing 4: Assigning derived values before saving, using prepareForDML 

// In ServiceHistoryImpl.java
protected void prepareForDML(int operation, TransactionEvent e) {
  super.prepareForDML(operation, e);
  // If we are inserting a new service history entry... 
  if (operation == DML_INSERT) {
    ServiceRequestImpl serviceReq = getServiceRequest();
    String historyType = getSvhType();
    // If request is pending or closed and customer adds note, status => Open
    if ((serviceReq.isPending() || serviceReq.isClosed())
        && CUSTOMER_TYPE.equals(historyType)) {
        serviceReq.setOpen();
    }
    // If request is open & technician adds a nonhidden note, status => Pending
    if (serviceReq.isOpen() && TECHNICIAN_TYPE.equals(historyType)) {
        serviceReq.setPending();
    }
  }
}


Conclusion

In this column, I've only touched on the basics of how to write business logic in entity objects. You can find more information on this topic by visiting the Oracle ADF Learning Center on OTN. Furthermore, chapter 9 of Oracle ADF Developer's Guide for Forms/4GL Developers ("Implementing Programmatic Business Rules in Entity Objects") provides more-detailed information about implementing programmatic business logic, and section 26.9 ("Implementing Custom Validation Rules") explains the details of implementing a custom validation rule. Happy validating!

 


Steve Muench has worked for Oracle since 1990. He is a consulting product manager for Oracle JDeveloper and an Oracle ACE. Muench coauthored Oracle ADF Developer's Guide for Forms/4GL Developers (Oracle, 2006), and he shares tips and tricks on OTN and in his Dive into BC4J and ADF blog.


Send us your comments