You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@struts.apache.org by "Lukasz Lenart (Jira)" <ji...@apache.org> on 2026/09/12 10:01:00 UTC
[jira] [Created] (WW-5733) JasperReports 7 exporter providers close the response stream before the report is written — empty response on Tomcat
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)