JSF 2.0 Преобразование страницы XHTML в PDF с помощью Flying Saucer: java.lang.IllegalStateException - PullRequest
7 голосов
/ 14 апреля 2011

Я пытаюсь конвертировать и экспортировать страницу JSF в PDF. Я попробовал это следующим образом:

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);
    }
}

Страница с кнопкой «Создать PDF» /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>

Страница, которую я хочу конвертировать:

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

Когда я пытаюсь получить PDF только один раз, то больше никогда. Я получил следующее исключение Facelet:

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

Что я делаю не так?

Обновленный Бин: см. Ответ BalusC:

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();
}

1 Ответ

8 голосов
/ 14 апреля 2011

Вы должны указать JSF, что вы уже взяли обработку ответов в свои руки и что JSF не должен обрабатывать навигацию по умолчанию, когда метод действия завершен. Добавьте это в конец метода действия:

facesContext.responseComplete();

Обновление в соответствии с комментариями, вы получаете доступ к ExternalContext в качестве переменной экземпляра, которая предполагает, что вы присвоили ее и FacesContext как переменную класса, либо static, либо как свойство объекта. боб сессионной области. Это определенно плохая идея . Вы должны получить их в локальном методе с помощью FacesContext#getCurrentInstance(), а никогда не назначать их как переменную класса. Именно они связаны с конкретным потоком запроса, который больше не существует в следующем запросе.

...