Yesterday Christian asked a very interesting question: He had observed that the browser UI is blocked when clicking a button which generates a PDF on the server and sends the result. For about 30 seconds no button is working, no events are fired. My first thought was that this behaviour is caused by the submit locking during partial refreshs, and after testing a XSP.allowSubmit() in the debug console I could prove myself that I was right.
It looked first like an easy solution, but after thinking a little bit about it, I had no idea how to execute this little piece of CSJS code. Let’s have a look at the PDF generation:
public void exportPdfOutputStream(TemplateData pdfData, String templateFile, String filename) {
ExternalContext con = facescontext.getExternalContext();
XspHttpServletResponse response = (XspHttpServletResponse) con.getResponse();
try {
ServletOutputStream writer = response.getOutputStream();
// setting response headers for browser
response.setContentType(“application/pdf”);
response.setHeader(“Cache-Control”, “no-cache”);
response.setDateHeader(“Expires”, -1);
response.setHeader(“Content-Disposition”, “attachment; filename=\”" + filename + “\”");
PdfReader pdfTemplate = getPdfReader(templateFile);
PdfStamper stamper = new PdfStamper(pdfTemplate, writer);
stamper.setFormFlattening(true);
setDataToPdfDocument(pdfData, stamper);
stamper.close();
pdfTemplate.close();
writer.flush();
writer.close();
facescontext.responseComplete();
} catch (Exception e) {
String errorMessage = “PDF Exporter: An error occureed: ” + e.toString();
try {
response.sendError(500, errorMessage);
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
A full refresh is submitted to the server, the code generates a PDF, adds the result to the output stream as attachment, and then closes the response. The browser receives a file to download and that’s it. Works fine. But how to execute some CSJS code? You cannot use view.postScript nor you cannot send an additional HTML element (a JS script block) and there is no onComplete event.
While I was musing I had the idea to use a repeating interval and check the server response in Javascript. But how can you access this information? When performing a XHR Call (a partial refresh) you can access the HTTP header of the response, but this is not possible when performing a full refresh.
I googled for an answer an found this article about a solution to block the UI: http://geekswithblogs.net/GruffCode/archive/2010/10/28/detecting-the-file-download-dialog-in-the-browser.aspx
It uses a cookie to send the information back to the browser, which is a really amazing idea. Here is my implementation for XPages and Dojo 1.8:
The button to download the PDF first executes a CSJS script which fills in the field fileDownloadToken with the current timestamp. This timestamp is sent to the server and then added to the cookie in the response.
![](data:image/png;base64,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)
The client checks every 500ms if the cookie exists and if the value equals the original token sent to the server. If the cookie is set correctly, the finishDownload() method is executed which calls XSP.allowSubmit() and removes the cookie.
This is the code of the XPage:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core">
<xp:button
id="buttonDownload"
value="Download">
<xp:eventHandler
event="onclick"
submit="true">
<xp:this.action>
<![CDATA[#{javascript:
importPackage( ch.hasselba.tools );
new ch.hasselba.tools.PDFUtil().exportPdfOutputStream("output.pdf");}]]>
</xp:this.action>
<xp:this.script><![CDATA[
require(["dojo/cookie"], function(cookie){
var fileDownloadCheckTimer;
var tokenName = 'fileDownloadToken';
function finishDownload() {
window.clearInterval(fileDownloadCheckTimer);
cookie('fileDownloadToken', null, {expire: -1});
XSP.allowSubmit();
}
function startDownload(){
setToken();
fileDownloadCheckTimer = window.setInterval(function () {
var cookieValue = cookie( tokenName );
if ( cookieValue == getToken() )
finishDownload();
}, 500);
}
function getToken(){
return XSP.getElementById( tokenField ).value;
}
function setToken(){
XSP.getElementById( tokenField ).value = Date.now();
}
startDownload();
})
]]></xp:this.script>
</xp:eventHandler>
</xp:button>
<input type="hidden" id="fileDownloadToken" name="fileDownloadToken" />
</xp:view>
And this is the modified Java code:
package ch.hasselba.tools;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import com.ibm.xsp.webapp.XspHttpServletResponse;
public class PDFUtil {
public void exportPdfOutputStream(String filename) {
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ex = fc.getExternalContext();
XspHttpServletResponse response = (XspHttpServletResponse) ex
.getResponse();
// get the token from the request
String token = (String) ex.getRequestParameterMap().get(
"fileDownloadToken");
try {
ServletOutputStream writer = response.getOutputStream();
// setting response headers for browser
response.setContentType("application/pdf");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", -1);
response.setHeader("Content-Disposition", "attachment; filename=\""
+ filename + "\"");
// set the cookie to the response
response.addCookie(new Cookie("fileDownloadToken", token));
//
// generate the output
//
// close the writer and mark response as completed
writer.flush();
writer.close();
fc.responseComplete();
} catch (Exception e) {
e.printStackTrace();
}
}
}