2

I have SSJS in a button that opens a pdf file, writes to some fields on it (Acroform) and then downloads the file to the user. All works great (using pdfbox) but I wanted to be a good programmer and if the original pdf file was not available then cancel the operation. Otherwise, the user still gets prompted to open the file but Adobe Reader reports the file is corrupted (obviously it will be). I do my pdf operations in a Java class that I call and pass in the outputStream of the response object. Below is my SSJS. If I test the ret value from newVal.outputPdf and put all the other code in the if statement then my XPage is just blank. I assume because the response and outputStream was already opened?

Howard

importPackage(com.tlcc);
var newVal = new PdfBoxTest();
importPackage(java.net);
importPackage(java.lang);
var con = facesContext.getExternalContext();
var response:com.ibm.xsp.webapp.XspHttpServletResponse = con.getResponse();

try {           
var writer:javax.servlet.ServletOutputStream = response.getOutputStream();


//get the stream
var ret = newVal.outputPdf(writer, "http://localhost/pdfexportcc.nsf/certificate.pdf");

// setting response headers for browser
print("Good output");
response.setContentType("application/pdf");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", -1);
response.setHeader( "Content-Disposition", "attachment; filename=\"mypdf.pdf\"" );
writer.flush();
writer.close();
print("in close");
facesContext.responseComplete();
} catch (e) {
    var errorMessage = "An error has occured: " + e.toString();
    _dump(errorMessage); 
    writer.close();
    response.sendError(500, errorMessage);
}

Tried again with all the work being done in Java. I called this method from a button. Works fine with a valid url but when the url is bad it throws an error. Exception Can't get a Writer while an OutputStream is already in use.

public boolean outputAllInJavaPdf() {
    try {
        FacesContext context = FacesContext.getCurrentInstance();

        XspHttpServletResponse response = (XspHttpServletResponse) context.getExternalContext().getResponse();

        ServletOutputStream writer = response.getOutputStream();
        InputStream docUrl = new URL("http://localhost/pdfexportcc.nsf/certifxxicate.pdf").openStream();
        pdfDoc = PDDocument.load(docUrl);

        System.out.println("Number of pages is " + pdfDoc.getNumberOfPages());
        setField("Student", "James Namce");
        setField("CourseName", "XPages Development 2 for Notes and Domino 9");
        setField("Instructor", "John Smith");
        System.out.println("After set field");
        pdfDoc.save(writer);
        pdfDoc.close();

        response.setContentType("application/pdf");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", -1);
        response.setHeader("Content-Disposition", "attachment; filename=\"mypdf.pdf\"");
        writer.flush();
        writer.close();

        context.responseComplete();
        return true;

    } catch (Exception e) {
        System.out.println(e.getMessage());
        e.printStackTrace();
        return false;
    }
}
Howard
  • 1,503
  • 7
  • 16

2 Answers2

1

All depends on what you tell the browser.

You use content type of PDF file. Browser opens (downloads) PDF file. Anything you put inside, for example error page, is treated as content of PDF file. So in case PDF generation fails, do not set that content type and redirect browser to error page, or back to original XPage with explanation.

public boolean outputAllInJavaPdf() {
    try {
        FacesContext context = FacesContext.getCurrentInstance();

        XspHttpServletResponse response = (XspHttpServletResponse) context.getExternalContext().getResponse();

        ServletOutputStream writer = response.getOutputStream();
        boolean servePdf = true;
        try {
            InputStream docUrl = new URL("http://localhost/pdfexportcc.nsf/certifxxicate.pdf").openStream();
            pdfDoc = PDDocument.load(docUrl);
            // do something to validate PDF
        } catch (Exception e) {
            //no PDF
            servePdf = false;
        }

        if (servePdf) {
            System.out.println("Number of pages is " + pdfDoc.getNumberOfPages());
            setField("Student", "James Namce");
            setField("CourseName", "XPages Development 2 for Notes and Domino 9");
            setField("Instructor", "John Smith");
            System.out.println("After set field");
            pdfDoc.save(writer);
            pdfDoc.close();

            response.setContentType("application/pdf");
            response.setHeader("Cache-Control", "no-cache");
            response.setDateHeader("Expires", -1);
            response.setHeader("Content-Disposition", "attachment; filename=\"mypdf.pdf\"");

        } else {
            // take care of no PDF response - redirect?
        }
            writer.flush();
            writer.close();
            context.responseComplete();
            return true;
    } catch (Exception e) {
        System.out.println(e.getMessage());
        e.printStackTrace();
        return false;
    }
}
Frantisek Kossuth
  • 3,524
  • 2
  • 23
  • 42
  • Not quite sure how to arrange the code to do this. If opening the existing pdf fails it drops into the catch block. The control returns back to the button that called the Java code and returns false. – Howard Apr 03 '15 at 13:27
  • That revised code works bettter, but, I am still stumped by how to reload the original XPage (or even better, have no effect on the original XPage). Now I just get a blank XPage. In my SSJS on the button if the returned value from the Java call is false I did a context.reloadPage() but it still just shows a blank web page. thanks for your help... – Howard Apr 03 '15 at 20:32
  • Tried again with a context.redirectToPage("myPage.xsp")... like var ret = new newVal.outputAllInJavaPdf(); if (ret == false){ context.redirectToPage("testPDF.xsp"); That worked. Seems like a brute force way to handle an exception in the Java class? Is this the best way? thanks, Howard } – Howard Apr 03 '15 at 20:36
  • Also it seems like when I call the Java method to generate the pdf (and it finds the url and opens it as a pdf) it takes about thirty seconds before I can again click on the button in the XPage. I put in a print statement on the button and that does not print to the log, so, I assume the issue is not in the Java code but the XPage itself is waiting for something? – Howard Apr 03 '15 at 21:00
  • Howard, you can remove the submit delay by using XSP.allowSubmit(). See my answer here: http://stackoverflow.com/a/21931903/785061 – Per Henrik Lausten Apr 06 '15 at 08:25
  • Thanks Per, that should do it. That question also had an idea that I will try where the button calls an XAgent. I might experiement with that. nd thanKs Frantisek! – Howard Apr 06 '15 at 13:18
0

It is probably trying to render your Xpage, but you want to control the response yourself. To prevent rendering of the Xpage and take control of the writer, add rendered="false"to your Xpage.

Ferry Kranenburg
  • 2,625
  • 1
  • 17
  • 23
  • I do want the XPage. I want to have button(s) on the page to create the pdf and download/open it. But the XPage should remain open. – Howard Apr 03 '15 at 13:26