Techniques to Invoke Multiple Actions While Handling a DataAction Event

Send me a mail
 Dive into Oracle ADF   Click to see the XML version of this web page.   (Updated: 2/3/2008; 9:25:04 PM.)
Tips and tricks from Steve Muench on Oracle ADF Framework and JDeveloper IDE

Techniques to Invoke Multiple Actions While Handling a DataAction Event

Over in this OTN JDeveloper Forum thread, a user says:

 I want to have a link next to each row that will delete that row. In order to do that I need to:

1) Set the current row
2) Delete the row
3) Commit

I was able to do this declarative without having to create any extra data actions by passing three event params on a link such as the following

<a href="engineManagement.do?event=selectRow&rowKeyStr=<c:out value='${Row.rowKeyStr}' />&event=Delete&event=Commit" >Delete</a>

(selectRow is bound to the setCurrentRowWithKey method)

Should I be using the event system in this way?

This seems to work very well, but i'm concerned about the ordering of the events, if they get executed in the wrong order, the wrong row will be deleted or the transaction won't get committed. Will the ADF controller always execute events in the order they appear on the query string?

The question about firing multiple declarative events is a smart one. We will process them in the order that they are given to us by the J2EE web container in the String[] of parameter values for the event parameter.However, I don't believe there is any iron-clad guarantee from the servlet spec that the order of these parameters will necessary exactly mimic their sequential order in your hyperlink's URL. It's probably better assume that they will appear in an essentially random order.

This means that you should use a single event, and then handle it by causing three action bindings to be invoked in the order you wish. I've written up a little WaysToDelete.zip example workspace that shows four different approaches. The ViewController project contains a single data page "/DeleteEmployees" that shows a table of all rows in the Employees iterator, with four different "Delete" links in each row:

  • Delete - href="DeleteEmployees.do?event=Delete&id=<c:out value='${Row.Empno}' />"
  • Delete2- href="DeleteEmployees.do?event=Delete2&id=<c:out value='${Row.Empno}' />"
  • Delete3 - href="DeleteEmployees.do?event=Delete3&id=<c:out value='${Row.Empno}' />"
  • Delete4 - href="DeleteEmployees.do?event=removeEmployee&id=<c:out value='${Row.Empno}' />"

In the DeleteEmployeesAction.java class, I illustrate how to handle the Delete, Delete2, and Delete3 events programmatically in a corresponding onEventName() handling method... 

The event handler for the Delete event, uses some helper methods to invoke an action binding before and after the one whose name matches the event:

public void onDelete(DataActionContext ctx) {
invokeAction(ctx,"setCurrentRowWithKeyValue");
  invokeEventAction(ctx);
  invokeAction(ctx,"Commit");
}

The event handler for the Delete2 event uses another helper method invoke multiple action bindings in the order that their names appear in the String[] passed in:

 public void onDelete2(DataActionContext ctx) {
   invokeActions(ctx, new String[]{
  "setCurrentRowWithKeyValue",
     "Delete",
     "Commit"
   });   
 }

The event handler for the Delete3 event illustrates the code-only approach, without invoking action bindings:

public void onDelete3(DataActionContext ctx) {
  String idToDelete = ctx.getHttpServletRequest().getParameter("id");
  if (idToDelete != null && !idToDelete.equals("")) {
    DCIteratorBinding iter = ctx.getBindingContainer()
.findIteratorBinding("EmployeesIterator");
    iter.setCurrentRowWithKeyValue(idToDelete);
    Row curRow = iter.getCurrentRow();
    if (curRow != null) {
     curRow.remove();
    }
   iter.getDataControl().commitTransaction();
}
}

The "Delete4" link in the page uses an event name that corresponding the the removeEmployee action binding in the binding container. I created this action binding in the UI Model structure window tab using the right-mouse menu to "Create Binding > Actions > Action" and clicked on the removeEmployee() method after selecting the TestModuleDataControl. I expanded the removeEmployee node in the UI Model tab, to see the param child node. In the Property Inspector, I changed the default name Arg0 of this first (and only, in this case!) parameter to be named id instead to match the name of the HTTP parameter my link will be passing in. [NOTE: I did a similar parameter renaming for the setCurrentRowWithKeyValue action as well]

This last approach is a declarative way of wiring the handling of the removeEmployee event to the invocation of the TestModuleDataControl method of the same name: removeEmployee(). Over inside the TestModuleImpl.java file, you can see the service-side implementation of the removeEmployee() method:

  public void removeEmployee(Number id) {
    if (id != null) {
      EmpViewImpl employees = getEmployees();
      Key k = new Key(new Object[]{id});
      Row[] found = employees.findByKey(k,1);
      if (found.length > 0) {
        found[0].remove();
      }
      getDBTransaction().commit();
    }
  }

Since it lives inside an ADF Application Module's *Impl.java file, it can freely work with any other *Impl objects and use any of ADF Business Components API's to get the job done.

Hope the example gets some ideas going for how you will prefer to handle similar situations in your own ADF-based applications.

IMPORTANT NOTE: Keep in mind that here we are committing the delete row immediately because that's what this customer said they wanted to do in their application. It's absolutely up to the developer to decide whether or not to commit the change immediately, or to let the ADF state management mechanism track that pending change in your current middle-tier unit of work until you decide later -- perhaps after many other creations, modifications, and deletes -- to finally commit or rollback the whole set of changes in one go.



© Copyright 2008 Steve Muench. Click here to send an email to the editor of this weblog.
Last update: 2/3/2008; 9:25:04 PM.