1
votes

JSF/Seam. I have a page which accepts some parameters supplied by the user via a form, and then (when the user clicks a button on the page) the server generates a file and sends it in the response such that the user is prompted with a save-as dialog.

Here is the scenario that I'm having trouble with:

  1. If the user initially enters invalid input and then clicks the button, Seam processes the request, but stops at the Process Validations phase. My page then displays the validation error message.
  2. Next, if the user then enters the correct input and clicks the button, Seam calls my action handler, the file is generated and sent to the user in the response - but the validation error message is still displayed!

Initially, I tried some hacks to force the rerendering of the <h:messages/> tag, but nothing was satisfactory. I now suspect the root cause is because Seam does not enter the Render Response phase when I place a file in the response.

Here's my button:

<h:commandButton value="#{messages.Reports_RunReportPDF}"
                 action="#{bean.generateReportPdf}"/>

And here's my action handler:

public String generateReportPdf() throws IOException {
  FacesContext facesContext = FacesContext.getCurrentInstance();
  HttpServletResponse response = (HttpServletResponse)facesContext.getExternalContext().getResponse();

  ServletOutputStream servletOutputStream = response.getOutputStream();

  // add this header to make browser prompt user with a save-as dialog
  response.addHeader("Content-Disposition",
                     "attachment;filename=" + reportName + ".pdf");
  response.setContentType(exportType.contentType());

  try {
    HashMap<String, Object> parameters = getReportParameters();
    ReportContent content = createReport(parameters);

    servletOutputStream.write(content.getContents());
    servletOutputStream.flush();
    servletOutputStream.close();
  } catch (ReportingException e) {
    e.printStackTrace();
    return "fail";
  }

  return "success";
}

If I comment-out the code which adds the file (and just return "success", the page follows the navigation rules I have set up correctly. But with the file, the page stays exactly the same as it was before the button was pushed.

So, how can I both return a file in the response, and cause the <h:messages/> tag to be rerendered?

2

2 Answers

0
votes

So, how can I both return a file in the response, and cause the tag to be rerendered?

That's not possible with a single HTTP request. You can return only one response per request. This is not a JSF limitation, this is a HTTP limitation.

You can use JavaScript to fire two HTTP requests by a single click, but in your particular case this wouldn't work nicely since the JSF request for the message depends on the result of the request for the PDF download. I don't see other ways than letting the servlet set a property of a session scoped managed which represents the status of the PDF export and introduce an ajax poll which requests this property at intervals and stops when it's not null anymore.

0
votes

Add this to your method after your write

//Skip the rest of JSF phases
FacesContext.getCurrentInstance().responseComplete();

You might also want to add the close in a finally block so that you ensure that the stream is properly closed if exception occurs. Or add the @Cleanup annotation from Lombok, and it will do it for you automatically

Update

If you are inside a long running conversation, you can end the conversation before the redirect, thus the validation message will be removed.

@End(beforeRedirect=true)