Respond to end of download in ADF 11g – using poll, clientListener and fileDownloadActionListener

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.

Image

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.

It turns out that responding to the beginning of the download is easy: we can add an af:clientListener component inside the fileDownloadActionListener and have a JavaScript function invoked.

In this JavaScript function, using the ADF 11g Rich Faces Client Side APIs, we can access and manipulate the entire page. However, for security reasons, we can not through JavaScript disable a button. The function setDisabled(true) does not exist. The trick to still ‘suggest’ the disabling and enabling of buttons from JavaScript is to add two additional initially invisble buttons that have the status disabled. The download button has an invisible, disabled counter part and so does the cancel button. When the Download button is pressed, the clientListener fires and sets the visible property for both enabled buttons to false and sets visible to true for the two disabled buttons. At this point, the animated ‘I am in progress’ gif could be shown as well.

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
  • add a clientListener to the poll component that calls a JavaScript function processPollEvent
  • add a clientListener to the fileDownloadActionListener that calls a JavaScript function startDownload
  • add an af:resource component of type JavaScript and create a variables startTime and reportRunning. Also create the two JavaScript functions startDownload and processPollEvent
  • 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:

Image

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.

also note: JavaScript code is to be added later to this article

Resources

See http://saumoinak.blogspot.com/2011/04/file-download-in-adf.html for a straighforward example of using the fileDownloadActionListener.

3 Comments

  1. Gennadiy April 29, 2014
  2. Lucas Jellema December 11, 2011
  3. Amitava December 2, 2011