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

5 comments:

Anonymous said...

Hello, I read your post, and I wanted to ask what I can do to cancel
the execution of the method that generates the file. Because if I put another button
cancel this does not run until the end of the method that generates
file.
We must do something with ajax or something.

I appreciate your response

Anonymous said...

Hi Jobinesh,

I have a case where I execute something in the VO when the user hits OK on a dialog and after that I start downloading. But because after hitting ok I do another hidden submit, there is a delay felt before something starts happening on the page.
A usecase to consider.

Thanks,
Karim

Anne Raj said...

coHI Jobinesh,
I read your pot and wanted to ask ehat i can do for refreshing the page post the download ?

Gues said...

Hi Jobinesh

Can't we just do the below instead of using javascript. I have tried the below it is giving some weird errors and not able to understand why... Is there any reason why you choose java script instead of below code and is anything wrong with this

public void prepareForDownloadAction(ActionEvent act) {

RichCommandButton button = (RichCommandButton) ADFUtil.findComponentNear(act.getComponent(), "HiddenBtn");
ActionEvent actionEvent = new ActionEvent(button);
actionEvent.queue();
}

arun prabhu said...

Hi Jobinesh,
what is the difference between using the below code and java script code...