Generate a report in various formats (e.g., PDF, delimited, HTML) using an ADF Task Flow.
HTTP headers are being sent twice: once by the framework and once by a bean.
The source code includes:
The button action:
<af:commandButton text="Report" id="submitReport" action="Execute" />
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());
}
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.
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.
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.
How can you set the HTTP headers in ADF while using a Task Flow to generate a PDF by calling a managed bean?
Some additional ideas:
ADFPhaseListener
+ PageLifecycle
)Thank you!
The problem was an incorrect implementation of RFC 2183:
response.setHeader( "Content-Disposition", "attachment; filename="
+ getFilename() );
The ;
cannot be a ,
.