Lukasz Lenart created WW-5733:
---------------------------------
Summary: JasperReports 7 exporter providers close the response
stream before the report is written — empty response on Tomcat
Key: WW-5733
URL: https://issues.apache.org/jira/browse/WW-5733
Project: Struts 2
Issue Type: Bug
Components: Plugin - JasperReports
Reporter: Lukasz Lenart
Fix For: 7.4.0
Every {{JasperReport7*ExporterProvider.createExporter}} in
{{struts2-jasperreports7-plugin}} (CSV, HTML, PDF, RTF, XML, XLSX) wires the
exporter output like this:
{code:java}try (OutputStream responseStream = response.getOutputStream()) {
exporter.setExporterOutput(new SimpleWriterExporterOutput(responseStream));
}
{code}
The try-with-resources closes the servlet output stream when {{createExporter}}
returns. Only afterwards does {{JasperReport7Result.exportReport}} call
{{exporter.exportReport()}} and {{response.getOutputStream().flush()}}, so the
whole report is written to an already closed stream.
On Tomcat, {{ServletOutputStream.close()}} commits the response with the bytes
written so far, which is none, and discards every later write. Reproduced with
an embedded Tomcat 10.1.34 and a servlet performing the same sequence (close
inside try-with-resources, then write, then flush):
{code}/good -> status=200 content-length=(none) body=28 bytes
/bad -> status=200 content-length=0 body=0 bytes
{code}
So on Tomcat the plugin returns an empty {{200}} with {{Content-Length: 0}} for
every format. {{JasperReport7ResultTest}} does not catch it because Spring's
{{MockHttpServletResponse}} keeps accepting writes after {{close()}}.
The 6.x {{struts2-jasperreports-plugin}} is unaffected: {{JasperReportsResult}}
exports into a {{ByteArrayOutputStream}}, sets {{Content-Length}}, then writes
and closes.
Fix: obtain {{response.getOutputStream()}} without try-with-resources in all
six providers (the {{IOException}} is still wrapped into {{StrutsException}}),
keep streaming directly to the response, and leave flushing to
{{JasperReport7Result}} and closing to the container. Guard it with a test that
runs the result against an {{HttpServletResponseWrapper}} whose
{{ServletOutputStream}} rejects writes after {{close()}}, which is the
semantics the mock lacks.
Affects every release since 7.1.0.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)