Tuesday, May 14, 2013

(Automatic) Partial Page Rendering across Taskflows

One of the great features of ADF is the easy out-of-the-box support for Partial Page Rendering (PPR) using the partialTriggers, autoSubmit and partialSubmit properties. And when things get a bit more complex there is a very easy API to do it programmatically. But what if

  • you don’t know beforehand what attribute or column will change, or
  • you don’t want to replicate this “Model logic” in the View layer (e.g. because you want to adhere to the Model-View-Controller pattern), or
  • components are scattered across Taskflows (in which case you cannot use the declarative support and the programmatic approach is suddenly very hard)?

Fortunately, ADF has a solution: Automatic Partial Page Rendering (or Auto PPR). If you have used ADF with Business Components, you might have seen or used it already, but it is less obvious that you can also use it with ordinary Beans or Bean DataControls.

Using Business Components

With ADF Business Components this is almost too easy. The ADF BC DataControl automatically maintains per Attribute a list of View Components that use/display it. It also keeps track of changes in attributes. The only thing you need to do is tell it that it should use this information for Auto PPR, using one simple property: ChangeEventPolicy in the PageDef. You can set it on an attributeValues binding. In that case only changes from that particular attribute will be propagated automatically, for example:

<attributeValues IterBinding="EmployeesView1Iterator" id="FullName"
                 ChangeEventPolicy="ppr">
  <AttrNames>
    <Item Value="FullName"/>
  </AttrNames>
</attributeValues>

Or you can set it on the iterator binding, in which case it is enabled for all attributes of this iterator:

<iterator Binds="EmployeesView1" RangeSize="25"
          DataControl="AppModuleDataControl" id="EmployeesView1Iterator" 
          ChangeEventPolicy="ppr"/>

Using ordinary Beans or Bean DataControls

As explained above, ADF Business Components automatically maintains a list of consuming Components and keeps track of changes, but your Bean – being a Plain Old Java Object – obviously does not. Suppose we want to create the following “application”:

image

Whenever someone enters a first name or a last name, the full name updates. When the full name updates (whenever someone enters something in any of the name fields) the bottom label also updates to reflect the new full name. This would be very easy using partialTriggers if it weren’t for the fact that the name fields are in a separate Taskflow. This Taskflow is included in a region on the page that contains the “Enter a bio for <FullName>” label.

This example is obviously made up for this blogpost, but situations like this are quite common. It is not hard to imagine that you would want to reuse the logic to enter a person’s name, but also use the person’s full name in other parts of the application. You don’t know (in the context of the “Name details” Taskflow) all the components in other parts of the application that depend on the full name. In fact you don’t want to know, because that would rather defeat the purpose of the Taskflow (i.e. reuse and modularization). So the programmatic approach is also not an option.

The basis of the implementation is a DataControl based on the following bean (not yet including the Auto PPR logic):

public class EmployeeBean {
  private String firstName, lastName, fullName;
  public void setFirstName(String firstName) {
    this.firstName = firstName;
    refreshFullName();
  }
  public String getFirstName() {
    return firstName;
  }
  public void setLastName(String lastName) {
    this.lastName = lastName;
    refreshFullName();
  }
  public String getLastName() {
    return lastName;
  }
  public void setFullName(String fullName) {
    this.fullName = fullName;
  }
  public String getFullName() {
    return fullName;
  }
  private void refreshFullName() {
    String first = firstName == null ? "" : firstName;
    String fullName = lastName == null ? first : first + " " + lastName;
    setFullName(fullName);
  }
}

Now, we need to do two things:

  1. Whenever getFullName() gets called we want to remember the consumer (JSF Component) that requested it.
  2. Whenever setFullName(String) gets called we want to do a partial update of all the components that we remembered from step 1.

This sounds easy and it also turns out to be easy. We can reuse the part of the ADF framework that does this for ADF BC. Using a small utility class (AutoPPRSupport.java) I created for this occasion, the only thing we have to do is add a new member to our bean...

private final AutoPPRSupport autoPPRSupport = new AutoPPRSupport();

... and change the getFullName() en setFullName(String) as follows…

public void setFullName(String fullName) {
  this.fullName = fullName;
  autoPPRSupport.notifyConsumers("fullName", fullName);
}
public String getFullName() {
  autoPPRSupport.rememberConsumerForAttribute("fullName");
  return fullName;
}

... and that’s it! Now, whenever you use the fullName attribute in a JSF Component, it will automatically update when needed. Download the example.

In this particular case we don’t have any rows, so we don’t have to tell the framework which specific row was updated. I leave it as an exercise for the reader to work that one out (hint: using primary keys).

(This post also appeared on my employer's blog: http://technology.amis.nl/.)