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)

Reply via email to