Search code examples
jsf-2http-headersreportingjdeveloperoracle-adf

Generate PDF using Managed Bean with custom HTTP headers


Background

Generate a report in various formats (e.g., PDF, delimited, HTML) using an ADF Task Flow.

Problem

HTTP headers are being sent twice: once by the framework and once by a bean.

Source Code

The source code includes:

  • Button Action
  • Managed Bean
  • Task Flow

Button Action

The button action:

<af:commandButton text="Report" id="submitReport" action="Execute" />

Managed Bean

The Managed Bean is fairly complex. The code to responseComplete is getting called, however it does not seem to be called sufficiently early to prevent the application framework from writing the HTTP headers.

HTTP Response Header Override

/**
 * Sets the HTTP headers required to indicate to the browser that the
 * report is to be downloaded (rather than displayed in the current
 * window).
 */
protected void setDownloadHeaders() {
  HttpServletResponse response = getServletResponse();
  response.setHeader( "Content-Description", getContentDescription() );
  response.setHeader( "Content-Disposition", "attachment, filename="
    + getFilename() );
  response.setHeader( "Content-Type", getContentType() );
  response.setHeader( "Content-Transfer-Encoding",
    getContentTransferEncoding() );
}

Issue Response Complete

getFacesContext().responseComplete();

Bean Run and Configure

public void run() {
  try {
    Report report = getReport();
    configure(report.getParameters());
    report.run();
  } catch (Exception e) {
    e.printStackTrace();
  }
}

private void configure(Parameters p) {
  p.put(ReportImpl.SYSTEM_REPORT_PROTOCOL, "http");
  p.put(ReportImpl.SYSTEM_REPORT_HOST, "localhost");
  p.put(ReportImpl.SYSTEM_REPORT_PORT, "7002");
  p.put(ReportImpl.SYSTEM_REPORT_PATH, "/reports/rwservlet");
  p.put(Parameters.PARAM_REPORT_FORMAT, "pdf");

  p.put("report_cmdkey", getReportName());
  p.put("report_ORACLE_1", getReportDestinationType());
  p.put("report_ORACLE_2", getReportDestinationFormat());
}

Task Flow

The Task Flow calls Execute, which refers to the bean's run() method:

entry -> main -> Execute -> ReportBeanRun

Where:

  <method-call id="ReportBeanRun">
    <description>Executes a report</description>
    <display-name>Execute Report</display-name>
    <method>#{reportBean.run}</method>
    <outcome>
      <fixed-outcome>success</fixed-outcome>
    </outcome>
  </method-call>

The bean is assigned to the request scope, with a few managed properties:

  <control-flow-rule id="__3">
    <from-activity-id>main</from-activity-id>
    <control-flow-case id="ExecuteReport">
      <from-outcome>Execute</from-outcome>
      <to-activity-id>ReportBeanRun</to-activity-id>
    </control-flow-case>
  </control-flow-rule>

  <managed-bean id="ReportBean">
    <description>Executes a report</description>
    <display-name>ReportBean</display-name>
    <managed-bean-scope>request</managed-bean-scope>
    ...
  </managed-bean>

The <fixed-outcome>success</fixed-outcome> strikes me as incorrect -- I don't want the method call to return to another task.

Restrictions

The report server receives requests from the web server exclusively. The report server URL cannot be used by browsers to download directly, for security reasons.

Error Messages

The error message that is generated:

Duplicate headers received from server

Error 349 (net::ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION): Multiple distinct Content-Disposition headers received. This is disallowed to protect against HTTP response splitting attacks.

Nevertheless, the report is being generated. Preventing the framework from writing the HTTP headers would resolve this issue.

Question

How can you set the HTTP headers in ADF while using a Task Flow to generate a PDF by calling a managed bean?

Ideas

Some additional ideas:

  • Override the Page Lifecycle Phase Listener (ADFPhaseListener + PageLifecycle)
  • Develop a custom Servlet on the web server

Related Links

Thank you!


Solution

  • The problem was an incorrect implementation of RFC 2183:

    response.setHeader( "Content-Disposition", "attachment; filename="
      + getFilename() );
    

    The ; cannot be a ,.