JSF 2.0 Convert XHTML Page to PDF using Flying Saucer: java.lang.IllegalStateException

11,069

You need to instruct JSF that you've already taken the response handling in your hands and that JSF should not handle the default navigation when the action method is finished. Add this to the end of the action method:

facesContext.responseComplete();

Update as per the comments, you're accessing ExternalContext as an instance variable which suggests that you assigned it and the FacesContext as class variable, either static or as property of a session scoped bean. This is definitely a bad idea. You should get hand of them inside the local method by FacesContext#getCurrentInstance() and never assign them as class variable. They are namely bound to a specific request thread which do not exist in next request anymore.

Share:
11,069
reen
Author by

reen

Updated on June 14, 2022

Comments

  • reen
    reen almost 2 years

    I am trying to convert and export a JSF Page to PDF. I tried it the following way:

    Bean:

    public void createPDF() {
        try {
            ITextRenderer renderer = new ITextRenderer();
            renderer.setDocument(new URL(url).toString());
            renderer.layout();
            HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
            response.reset();
            response.setContentType("application/pdf");
            response.setHeader("Content-Disposition", "inline; filename=\"" +PDF_FILE_NAME+ "\"");
            OutputStream browserStream = response.getOutputStream();
            renderer.createPDF(browserStream);
        } catch (Exception ex) {
            Logger.getLogger(PdfBean.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    

    Page with the Create PDF Button /home.xhtml:

    <ui:define name="content">
        <center>
            <h:form id="pdfgen">
                <h:panelGrid columns="2">
                    <h:outputText value="Enter Name:"/>
                    <h:inputText value="#{pdfBean.name}"/>
                </h:panelGrid>
                <h:commandButton value="Create PDF" action="#{pdfBean.createPDF()}"/>
            </h:form>
        </center>
    </ui:define>
    

    The Page which I want to convert:

    <ui:define name="content">
        <center>
            <h:outputText value="Hello #{pdfBean.name}"/>
        </center>
    </ui:define>
    

    When I try that I get a PDF only once, then never again. I got following Facelet Exception:

    SEVERE: Error Rendering View[/home.xhtml]
    java.lang.IllegalStateException: PWC3991: getOutputStream() has already been called for this response
    ...
    WARNING: StandardWrapperValve[Faces Servlet]: PWC1406: Servlet.service() for servlet Faces Servlet threw exception
    java.lang.IllegalStateException: PWC3991: getOutputStream() has already been called for this response
    

    What am I doing wrong?

    Updated Bean: see BalusC's answer:

    public void createPDF() {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        ExternalContext externalContext = facesContext.getExternalContext();
        String servername = externalContext.getRequestServerName();
        String port = String.valueOf(externalContext.getRequestServerPort());
        String appname = externalContext.getRequestContextPath();
        String protocol = externalContext.getRequestScheme();
        this.url = protocol + "://" + servername + ":" + port + appname + PDF_PAGE;
        try {
            ITextRenderer renderer = new ITextRenderer();
            renderer.setDocument(new URL(url).toString());
            renderer.layout();
            HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
            response.reset();
            response.setContentType("application/pdf");
            response.setHeader("Content-Disposition", "inline; filename=\"" + PDF_FILE_NAME + "\"");
            OutputStream browserStream = response.getOutputStream();
            renderer.createPDF(browserStream);
    
        } catch (Exception ex) {
            Logger.getLogger(PdfBean.class.getName()).log(Level.SEVERE, null, ex);
        }
        facesContext.responseComplete();
    }
    
  • reen
    reen about 13 years
    I am still getting Illegal State errors at line (PdfBean.java:58). This is exactly the line where I put facesContext.responseComplete(); Maybe my approach is wrong?
  • BalusC
    BalusC about 13 years
    If it throws IllegalStateException then it means that there's no FacesContext anymore at that point. I however don't see any possible cause for that. Do you have more code in the method than you're showing as far in your question?
  • reen
    reen about 13 years
    I updated the Bean as proposed, no exceptions anymore. Thanks a lot! But in the /home.xhtml page I have also a name Inputfield, which should appear in the pdf. Unfortunately it does not. If I access the xhtml page, then it is there. Any Ideas?
  • BalusC
    BalusC about 13 years
    This is a different problem. Press Ask Question :) Hint: jsessionid.
  • reen
    reen about 13 years
    OK, found the solution, thanks to the hint :-) I needed to append the sessionid. Again, thanks a lot BalusC