Wednesday, January 20, 2010

Customizing the execution of <af:fileDownloadActionListener> to validate user input

Let me explain an interesting use case on 'file downloading' and a possible solution at this time. Requirement is to accept user input and validate the same, before initiating the file download. If the validation fails, then file download should get suspended.

ADF Faces recommends to use <af:fileDownloadActionListener> for downloading file. This provides a declarative solution for the problem, in most of the cases. As you can see from tag documentation, <af:fileDownloadActionListener> is designed in a very generic way. Currently we don’t have any direct mechanism to intercept the invocation of fileDownloadActionListener, apparently solution for the above mentioned use case turns out to be bit tricky. (Thanks to Jeanne Waldman for sharing this idea )

Interesting part of the solutions is queuing the ActionEvent for a ‘hidden’ action component which wraps fileDownloadActionListener. The java script method to queue ActionEvent is invoked from second button’s (this appears as download button for the end user) actionListener method, conditionally (if all business criteria’s are met). Let me detail the steps below.

Solution: To keep the logic simple, let us define 2 buttons, one hidden which has the fileDownloadActionListener tag inside. Second button (Download button) actually perform custom business process (like validating user input) before download starts. Please remember that, our objective is to validate the user input first and then download the file. If validation fails, suspend file download. So Download button performs required business checks (validates user inputs) and if all goes well, calls client side java script method to generate ‘AdfActionEvent‘ for the hidden button that in turn triggers file download.
<af:commandButton text="HiddenBtn" id="HiddenBtn"
   clientComponent="true" visible="false"
   partialSubmit="false">
   <af:fileDownloadActionListener filename="#{TestBean.fileName}"
           method="#{TestBean.downloadMe}"
           contentType="text/plain; charset=utf-8"/>
</af:commandButton>
<af:commandButton text="Download" id="dwnloadBtn"
      actionListener="#{TestBean.prepareForDownloadAction}"
      clientComponent="true"
      partialSubmit="true"/>

java script method to queue AdfActionEvent for the hidden button
function customHandler(event) {
    hidePopup(event);
    var exportCmd = AdfPage.PAGE.findComponentByAbsoluteId("HiddenBtn");
    var actionEvent = new AdfActionEvent(exportCmd);
    actionEvent.forceFullSubmit();
    actionEvent.noResponseExpected();
    actionEvent.queue();
}

The actionListener method for the Download button:
public void prepareForDownloadAction(ActionEvent act) {
/**
 * You can add your business logic here to process the
 * user input, and based on the outcome you can decide
 * whether to continue the processing or not.
 */
FacesContext context = FacesContext.getCurrentInstance();
ExtendedRenderKitService erks =
    Service.getService(context.getRenderKit(),
               ExtendedRenderKitService.class);

erks.addScript(context, "customHandler();");
}
You can download the sample workspace from here.
Unzip the project and run the test.jspx. Click on Download button, key in the file name and press OK. You can observe that, file download starts only after validating the input. If validation fails, file download is getting suspended.
[Runs with Oracle JDeveloper 11g R1 PS1]


Learn More ...

There are a lot more points like this. If  you are curious to learn the internals of the ADF Business Components and ADF Binding Layer,  the following book is for you - Oracle ADF Real World Developer’s Guide.
More details about this book can be found in this post- http://jobinesh.blogspot.in/2012/07/pre-order-your-copy-of-oracle-adf-real.html

Friday, January 15, 2010

Search by child attributes on a tree table

This example illustrates search functionality on a tree table (based  on both master and child record attributes). Steve discusses similar topic in one of his blog post http://blogs.oracle.com/smuenchadf/examples/ Example #150. This post is also based on the same idea (slightly differ, from implementation perspective).

Let me detail the use case with classic Department-Employee example. Your tree table may looks like as shown below. Search mechanism should enable user to search based on attributes both from parent and child records, i.e. Department and Employee entities.

-DeptID , DeptName
|
|__EmpId1, Firstname1, Email1
|__EmpId2, Firstname2, Email2

Obviously implementation part involves two ViewObjects, one for Department and the other for Employee. Next step is to define a ViewCriteriathat act as model for the query. Below image shows a ViewCriteria defined on DeprtmentViewObject.




Define another ViewCriteria on EmployeeViewOject, to filter out the 'Employee' child records programmatically. Please note that, this is used from overridden DepartmentViewObjectImpl::createViewLinkAccessorRS() method as detailed below.



To enable search across child rows(Employee records), I overrode the ViewObjectImpl::createViewLinkAccessorRS(---) as given below(in the DeprtmentViewObjectImpl). This method contains the filtering logic for child rows(Employees).

 @Override
 protected ViewRowSetImpl createViewLinkAccessorRS(AssociationDefImpl associationDefImpl,
                                                     ViewObjectImpl viewObjectImpl,
                                                      Row row,
                                                      Object[] object) {
   ViewRowSetImpl viewRowSetImpl =
       super.createViewLinkAccessorRS(associationDefImpl,
       viewObjectImpl,row, object);
     
   String firstName = getbndVarFirstName();
   if (firstName != null && firstName.trim().length() > 0) {
      ViewCriteriaManager vcm = viewObjectImpl.getViewCriteriaManager();
      ViewCriteria vc = vcm.getViewCriteria("EmployeesViewCriteria");
      VariableValueManager vvm = vc.ensureVariableManager();
      vvm.setVariableValue("bndVarFirstName", getbndVarFirstName());
      viewObjectImpl.applyViewCriteria(vc);
      } else {

         viewObjectImpl.removeApplyViewCriteriaName("EmployeesViewCriteria");
      }

      return viewRowSetImpl;
 }


Rest of the steps remains same as for a normal tree table creation.

You can download the sample workspace from here. This example illustrates the above said use case -Search by child attributes on a tree table.
Run the test.jspx from the attached project. Search panel displays two fields, DepartmentName and FirstName. DepartmentName is used to search the parent record of the tree table, and FirstName filters out the child records.
[Runs with Oracle JDeveloper 11g R1 PS1 + HR Schema]