diff --git a/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/export/JasperReport7CsvExporterProvider.java b/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/export/JasperReport7CsvExporterProvider.java index a11b51d005..582d32443f 100644 --- a/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/export/JasperReport7CsvExporterProvider.java +++ b/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/export/JasperReport7CsvExporterProvider.java @@ -82,7 +82,8 @@ public JRCsvExporter createExporter(ActionInvocation invocation, JasperPrint jas SimpleExporterInput input = new SimpleExporterInput(jasperPrint); exporter.setExporterInput(input); - try (OutputStream responseStream = response.getOutputStream()) { + try { + OutputStream responseStream = response.getOutputStream(); WriterExporterOutput exporterOutput = new SimpleWriterExporterOutput(responseStream); exporter.setExporterOutput(exporterOutput); } catch (IOException e) { diff --git a/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/export/JasperReport7HtmlExporterProvider.java b/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/export/JasperReport7HtmlExporterProvider.java index 995b6d9461..bc4236d375 100644 --- a/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/export/JasperReport7HtmlExporterProvider.java +++ b/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/export/JasperReport7HtmlExporterProvider.java @@ -70,7 +70,8 @@ public HtmlExporter createExporter(ActionInvocation invocation, JasperPrint jasp SimpleExporterInput input = new SimpleExporterInput(jasperPrint); exporter.setExporterInput(input); - try (OutputStream responseStream = response.getOutputStream()) { + try { + OutputStream responseStream = response.getOutputStream(); SimpleHtmlExporterOutput exporterOutput = new SimpleHtmlExporterOutput(responseStream); HtmlResourceHandler imageHandler = new WebHtmlResourceHandler(request.getContextPath() + imageServletUrl + "%s"); exporterOutput.setImageHandler(imageHandler); diff --git a/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/export/JasperReport7PdfExporterProvider.java b/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/export/JasperReport7PdfExporterProvider.java index 24af92ca28..24ec670b51 100644 --- a/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/export/JasperReport7PdfExporterProvider.java +++ b/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/export/JasperReport7PdfExporterProvider.java @@ -48,7 +48,8 @@ public JRPdfExporter createExporter(ActionInvocation invocation, JasperPrint jas SimpleExporterInput input = new SimpleExporterInput(jasperPrint); exporter.setExporterInput(input); - try (OutputStream responseStream = response.getOutputStream()) { + try { + OutputStream responseStream = response.getOutputStream(); OutputStreamExporterOutput exporterOutput = new SimpleOutputStreamExporterOutput(responseStream); exporter.setExporterOutput(exporterOutput); } catch (IOException e) { diff --git a/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/export/JasperReport7RtfExporterProvider.java b/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/export/JasperReport7RtfExporterProvider.java index 91dc002d70..160bcfc44f 100644 --- a/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/export/JasperReport7RtfExporterProvider.java +++ b/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/export/JasperReport7RtfExporterProvider.java @@ -48,7 +48,8 @@ public JRRtfExporter createExporter(ActionInvocation invocation, JasperPrint jas SimpleExporterInput input = new SimpleExporterInput(jasperPrint); exporter.setExporterInput(input); - try (OutputStream responseStream = response.getOutputStream()) { + try { + OutputStream responseStream = response.getOutputStream(); WriterExporterOutput exporterOutput = new SimpleWriterExporterOutput(responseStream); exporter.setExporterOutput(exporterOutput); } catch (IOException e) { diff --git a/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/export/JasperReport7XlsxExporterProvider.java b/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/export/JasperReport7XlsxExporterProvider.java index cf1317f6e4..49c5a51fea 100644 --- a/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/export/JasperReport7XlsxExporterProvider.java +++ b/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/export/JasperReport7XlsxExporterProvider.java @@ -48,7 +48,8 @@ public JRXlsxExporter createExporter(ActionInvocation invocation, JasperPrint ja SimpleExporterInput input = new SimpleExporterInput(jasperPrint); exporter.setExporterInput(input); - try (OutputStream responseStream = response.getOutputStream()) { + try { + OutputStream responseStream = response.getOutputStream(); OutputStreamExporterOutput exporterOutput = new SimpleOutputStreamExporterOutput(responseStream); exporter.setExporterOutput(exporterOutput); } catch (IOException e) { diff --git a/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/export/JasperReport7XmlExporterProvider.java b/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/export/JasperReport7XmlExporterProvider.java index be34c8ebc6..b6b9ee1522 100644 --- a/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/export/JasperReport7XmlExporterProvider.java +++ b/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/export/JasperReport7XmlExporterProvider.java @@ -48,7 +48,8 @@ public JRXmlExporter createExporter(ActionInvocation invocation, JasperPrint jas SimpleExporterInput input = new SimpleExporterInput(jasperPrint); exporter.setExporterInput(input); - try (OutputStream responseOutput = response.getOutputStream()) { + try { + OutputStream responseOutput = response.getOutputStream(); XmlExporterOutput exporterOutput = new SimpleXmlExporterOutput(responseOutput); exporter.setExporterOutput(exporterOutput); } catch (IOException e) { diff --git a/plugins/jasperreports7/src/test/java/org/apache/struts2/views/jasperreports7/JasperReport7ResultTest.java b/plugins/jasperreports7/src/test/java/org/apache/struts2/views/jasperreports7/JasperReport7ResultTest.java index d7c3aadc3e..630ada4f8b 100644 --- a/plugins/jasperreports7/src/test/java/org/apache/struts2/views/jasperreports7/JasperReport7ResultTest.java +++ b/plugins/jasperreports7/src/test/java/org/apache/struts2/views/jasperreports7/JasperReport7ResultTest.java @@ -19,6 +19,10 @@ package org.apache.struts2.views.jasperreports7; import jakarta.servlet.ServletException; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.WriteListener; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponseWrapper; import net.sf.jasperreports.engine.JasperCompileManager; import org.apache.struts2.ActionContext; import org.apache.struts2.ActionInvocation; @@ -29,6 +33,7 @@ import org.apache.struts2.util.ValueStack; import java.io.ByteArrayInputStream; +import java.io.IOException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.sql.Connection; @@ -303,6 +308,26 @@ public void testFormatLookupIsCaseInsensitive() throws Exception { assertThat(response.getContentAsString()).contains("Qux Report"); } + public void testExportWritesNothingAfterClosingTheResponseStream() throws Exception { + for (String format : List.of(JasperReport7Constants.FORMAT_PDF, JasperReport7Constants.FORMAT_CSV, + JasperReport7Constants.FORMAT_HTML, JasperReport7Constants.FORMAT_RTF, + JasperReport7Constants.FORMAT_XML, JasperReport7Constants.FORMAT_XLSX)) { + // given + response.setCommitted(false); + response.reset(); + ActionContext.getContext().withServletResponse(new StrictCloseResponse(response)); + result.setDataSource("{#{'firstName':'ignore', 'lastName':'ignore'}}"); + result.setReportParameters("#{'title':'Qux'}"); + result.setFormat(format); + + // when + result.execute(this.invocation); + + // then + assertThat(response.getContentAsByteArray()).as(format).isNotEmpty(); + } + } + public void testExportToRtf() throws Exception { // given result.setDataSource("{#{'firstName':'ignore', 'lastName':'ignore'}}"); @@ -386,6 +411,53 @@ private void compileAndUseReport(String jrxml) throws Exception { result.setLocation(resource + ".jasper"); } + /** + * Behaves like a servlet container: once the output stream is closed, further writes are lost. + */ + private static class StrictCloseResponse extends HttpServletResponseWrapper { + + private ServletOutputStream stream; + + StrictCloseResponse(HttpServletResponse response) { + super(response); + } + + @Override + public ServletOutputStream getOutputStream() throws IOException { + if (stream == null) { + ServletOutputStream delegate = super.getOutputStream(); + stream = new ServletOutputStream() { + private boolean closed; + + @Override + public void write(int b) throws IOException { + if (closed) { + throw new IOException("Stream closed"); + } + delegate.write(b); + } + + @Override + public void close() throws IOException { + closed = true; + delegate.close(); + } + + @Override + public boolean isReady() { + return delegate.isReady(); + } + + @Override + public void setWriteListener(WriteListener writeListener) { + delegate.setWriteListener(writeListener); + } + }; + } + return stream; + } + } + private static final List> JR_MAP_ARRAY_DATA_SOURCE = Stream.>of( new HashMap<>() {{ put("firstName", "Foo");