The situation: in an ADF Faces 11g page, we have a popup with two buttons: one to start a download and one to cancel the popup. When the users presses the download button, a fileDownloadActionListener is activated, the corresponding server side method is invoked to start producing the content to be downloaded and eventually the browser will prompt the user to open or save (or cancel in the case of IE) the download.
The challenge: when the download commences, the download and cancel buttons should be disabled and perhaps an animated gif should be shown to suggest progress (we want to at least prevent the user from clicking the download button or getting frustrated in other ways while she is waiting for the report to be produced on the server side). When the download is complete – that means: when through the browser interaction the user has saved, opened or canceled the actual content download to the browser – the buttons should be enabled again and the animated gif can be removed.
In short: we want to be able to react – both to the beginning of the download as well as to the completion.
Unfortunately, it is not so easy to detect the end of the download. There is no event to capture, neither client side, nor server side.
However, I found out that poll operations during the download are not able to reach the server. So even though the client side timer continues to fire, the server request cycle that could be triggered by a poll event whenever a component is associated with the poll (has the poll as its partial trigger) has an attribute dynamicaly associated through an EL Expression with a server side bean, does not take place. As soon as the download is complete, the next poll event will start reaching the server again.
Here is our clue for a solution: if we can find out that a poll leads to a server based update in the page, that means that the download must be complete because during the download this is not possible.
The approach can now be as follows:
- create a poll component in the page, set its interval to -1 (which means it does not fire at all)
- create a simple managed bean in the ADF Faces application with a method Date getLatestValue that returns new java.util.Date().
- add a component to the page that has an attribute configured with an EL expression that references the bean and its latestValue property
- add the poll to the partialTriggers attribute of this component
- in function startDownload, set reportRunning to true and set the variable startTime to the current value of the attribute based on the ‘timebean’ – the latestValue property. Get hold of the poll component and set its interval to 1000 (== 1 second)
- in function processPollEvent: test whether reportRunning is true. If that is so, then retrieve the value of the attribute based on the ‘timebean’. If that value is not the same as the value stored in startTime – this means that the poll has succeeded in updating the component which means that the download is completed and the channel open again. At this point set reportRunning to false, set the poller interval back to -1 and show the two enabled buttons while hiding the two disabled buttons
Visually outlined, it looks like this:
Note the two parallel event cycles. The white markers show the ‘initiate and execute download’ flow and the yellow markers show the poll-and-refresh-input-component sequence.
See http://saumoinak.blogspot.com/2011/04/file-download-in-adf.html for a straighforward example of using the fileDownloadActionListener.