diff --git a/plugins/jasperreports/src/main/java/org/apache/struts2/views/jasperreports/JasperReportsResult.java b/plugins/jasperreports/src/main/java/org/apache/struts2/views/jasperreports/JasperReportsResult.java index f24ddd1b83..b014c9ecc5 100644 --- a/plugins/jasperreports/src/main/java/org/apache/struts2/views/jasperreports/JasperReportsResult.java +++ b/plugins/jasperreports/src/main/java/org/apache/struts2/views/jasperreports/JasperReportsResult.java @@ -72,8 +72,13 @@ * *
  • location (default) - the location where the compiled jasper report * definition is (foo.jasper), relative from current URL.
  • - *
  • dataSource (required) - the EL expression used to retrieve the - * datasource from the value stack (usually a List).
  • + *
  • dataSource - the EL expression used to retrieve the + * datasource from the value stack (usually a List). When neither dataSource + * nor connection is set the report is filled from its parameters alone, + * so a data supplier expected by the report's query executer (e.g. + * HIBERNATE_SESSION, CSV_INPUT_STREAM, + * JSON_INPUT_STREAM) or a ready REPORT_DATA_SOURCE / + * REPORT_CONNECTION can be handed over via reportParameters.
  • *
  • parse - true by default. If set to false, the location param will * not be parsed for EL expressions.
  • *
  • format - the format in which the report should be generated. Valid @@ -258,7 +263,6 @@ public void setConnection(String connection) { } protected void doExecute(String finalLocation, ActionInvocation invocation) throws Exception { - // Will throw a runtime exception if no "datasource" property. TODO Best place for that is...? initializeProperties(invocation); LOG.debug("Creating JasperReport for dataSource = {}, format = {}", dataSource, format); @@ -284,7 +288,7 @@ protected void doExecute(String finalLocation, ActionInvocation invocation) thro ValueStackDataSource stackDataSource = null; Connection conn = (Connection) stack.findValue(connection); - if (conn == null) { + if (conn == null && dataSource != null) { boolean evaluated = parsedDataSource != null && !parsedDataSource.equals(dataSource); boolean reevaluate = !evaluated || isAcceptableExpression(parsedDataSource); if (reevaluate) { @@ -338,10 +342,13 @@ protected void doExecute(String finalLocation, ActionInvocation invocation) thro // Fill the report and produce a print object try { JasperReport jasperReport = (JasperReport) JRLoader.loadObject(new File(systemId)); - if (conn == null) { + if (conn != null) { + jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, conn); + } else if (stackDataSource != null) { jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, stackDataSource); } else { - jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, conn); + LOG.debug("No dataSource or connection set, filling {} from report parameters only", systemId); + jasperPrint = JasperFillManager.fillReport(jasperReport, parameters); } } catch (JRException e) { LOG.error("Error building report for uri {}", systemId, e); @@ -460,11 +467,6 @@ private void writeReport(HttpServletResponse response, ByteArrayOutputStream out * @throws Exception on initialization error. */ private void initializeProperties(ActionInvocation invocation) { - if (dataSource == null && connection == null) { - String message = "No dataSource specified..."; - LOG.error(message); - throw new RuntimeException(message); - } if (dataSource != null) { parsedDataSource = conditionalParse(dataSource, invocation); } diff --git a/plugins/jasperreports/src/test/java/org/apache/struts2/views/jasperreports/JasperReportsResultTest.java b/plugins/jasperreports/src/test/java/org/apache/struts2/views/jasperreports/JasperReportsResultTest.java index 1fb31763ed..beb563c122 100644 --- a/plugins/jasperreports/src/test/java/org/apache/struts2/views/jasperreports/JasperReportsResultTest.java +++ b/plugins/jasperreports/src/test/java/org/apache/struts2/views/jasperreports/JasperReportsResultTest.java @@ -28,7 +28,9 @@ import org.apache.struts2.junit.StrutsTestCase; import jakarta.servlet.ServletException; +import java.io.ByteArrayInputStream; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.sql.Connection; import java.util.HashMap; import java.util.Map; @@ -169,6 +171,21 @@ public void testReportParametersExpressionAccepted() throws Exception { assertTrue(response.getContentAsString().contains("Qux Report")); } + public void testFillFromReportParametersWithoutDataSourceOrConnection() throws Exception { + stack.push(new Object() { + public Map getReportParameters() { + return Map.of("CSV_INPUT_STREAM", + new ByteArrayInputStream("Foo,Bar\n".getBytes(StandardCharsets.UTF_8))); + } + }); + result.setReportParameters("reportParameters"); + compileAndUseReport("csv.jrxml"); + + result.execute(this.invocation); + + assertTrue(response.getContentAsString().contains("Hello Foo Bar!")); + } + public void testExportParametersNotAccepted() throws Exception { result.setDataSource("{#{'firstName':'ignore', 'lastName':'ignore'}}"); @@ -234,12 +251,17 @@ protected void setUp() throws Exception { result = new JasperReportsResult(); container.inject(result); - URL url = ClassLoaderUtil.getResource("org/apache/struts2/views/jasperreports/simple.jrxml", this.getClass()); - JasperCompileManager.compileReportToFile(url.getFile(), url.getFile() + ".jasper"); - result.setLocation("org/apache/struts2/views/jasperreports/simple.jrxml.jasper"); + compileAndUseReport("simple.jrxml"); result.setFormat(JasperReportConstants.FORMAT_XML); } + private void compileAndUseReport(String jrxml) throws Exception { + String resource = "org/apache/struts2/views/jasperreports/" + jrxml; + URL url = ClassLoaderUtil.getResource(resource, this.getClass()); + JasperCompileManager.compileReportToFile(url.getFile(), url.getFile() + ".jasper"); + result.setLocation(resource + ".jasper"); + } + private static final Map[] JR_MAP_ARRAY_DATA_SOURCE = new Map[]{ new HashMap() {{ diff --git a/plugins/jasperreports/src/test/resources/org/apache/struts2/views/jasperreports/csv.jrxml b/plugins/jasperreports/src/test/resources/org/apache/struts2/views/jasperreports/csv.jrxml new file mode 100644 index 0000000000..f235420b9f --- /dev/null +++ b/plugins/jasperreports/src/test/resources/org/apache/struts2/views/jasperreports/csv.jrxml @@ -0,0 +1,38 @@ + + + + + + + + + + + + "Hello " + + " " + + "!" + + + + diff --git a/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/JasperReport7Result.java b/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/JasperReport7Result.java index e8d26bb82b..fde8590bcd 100644 --- a/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/JasperReport7Result.java +++ b/plugins/jasperreports7/src/main/java/org/apache/struts2/views/jasperreports7/JasperReport7Result.java @@ -61,8 +61,13 @@ * *
  • location (default) - the location where the compiled jasper report * definition is (foo.jasper), relative from current URL.
  • - *
  • dataSource (required) - the EL expression used to retrieve the - * datasource from the value stack (usually a List).
  • + *
  • dataSource - the EL expression used to retrieve the + * datasource from the value stack (usually a List). When neither dataSource + * nor connection is set the report is filled from its parameters alone, + * so a data supplier expected by the report's query executer (e.g. + * HIBERNATE_SESSION, CSV_INPUT_STREAM, + * JSON_INPUT_STREAM) or a ready REPORT_DATA_SOURCE / + * REPORT_CONNECTION can be handed over via reportParameters.
  • *
  • parse - true by default. If set to false, all the parameters will * not be parsed for EL expressions.
  • *
  • format - the format in which the report should be generated. Valid @@ -161,7 +166,7 @@ protected void doExecute(String finalLocation, ActionInvocation invocation) thro ValueStack stack = invocation.getStack(); Connection reportConnection = (Connection) stack.findValue(connection); ValueStackDataSource reportDataSource = null; - if (reportConnection == null) { + if (reportConnection == null && dataSource != null) { reportDataSource = prepareDataSource(stack); } @@ -185,10 +190,13 @@ protected void doExecute(String finalLocation, ActionInvocation invocation) thro // Fill the report and produce a print object try { JasperReport jasperReport = (JasperReport) JRLoader.loadObject(new File(systemId)); - if (reportConnection == null) { + if (reportConnection != null) { + jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, reportConnection); + } else if (reportDataSource != null) { jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, reportDataSource); } else { - jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, reportConnection); + LOG.debug("No dataSource or connection set, filling {} from report parameters only", systemId); + jasperPrint = JasperFillManager.fillReport(jasperReport, parameters); } if (invocation.getAction() instanceof JasperReport7Aware action) { @@ -305,11 +313,6 @@ private HttpServletResponse prepapreHttpServletResponse(ActionInvocation invocat * @param invocation Current invocation. */ private void initializeProperties(ActionInvocation invocation) { - if (dataSource == null && connection == null) { - String message = "No dataSource specified..."; - LOG.error(message); - throw new RuntimeException(message); - } if (dataSource != null) { parsedDataSource = conditionalParse(dataSource, invocation); } 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 38d3fe27e7..d1f82d5dc0 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 @@ -27,7 +27,9 @@ import org.apache.struts2.util.ClassLoaderUtil; import org.apache.struts2.util.ValueStack; +import java.io.ByteArrayInputStream; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.sql.Connection; import java.util.HashMap; import java.util.List; @@ -195,6 +197,25 @@ public Map getReportParameters() { assertThat(response.getContentAsString()).contains("Baz Report"); } + public void testFillFromReportParametersWithoutDataSourceOrConnection() throws Exception { + // given + stack.push(new Object() { + public Map getReportParameters() { + return Map.of("CSV_INPUT_STREAM", + new ByteArrayInputStream("Foo,Bar\n".getBytes(StandardCharsets.UTF_8))); + } + }); + result.setReportParameters("reportParameters"); + compileAndUseReport("csv.jrxml"); + + // when + result.execute(this.invocation); + + // then + assertThat(response.getContentType()).isEqualTo("text/xml"); + assertThat(response.getContentAsString()).contains("Hello Foo Bar!"); + } + public void testExportToXml() throws Exception { // given result.setDataSource("{#{'firstName':'ignore', 'lastName':'ignore'}}"); @@ -295,12 +316,17 @@ protected void setUp() throws Exception { result = new JasperReport7Result(); container.inject(result); - URL url = ClassLoaderUtil.getResource("org/apache/struts2/views/jasperreports7/simple.jrxml", this.getClass()); - JasperCompileManager.compileReportToFile(url.getFile(), url.getFile() + ".jasper"); - result.setLocation("org/apache/struts2/views/jasperreports7/simple.jrxml.jasper"); + compileAndUseReport("simple.jrxml"); result.setFormat(JasperReport7Constants.FORMAT_XML); } + private void compileAndUseReport(String jrxml) throws Exception { + String resource = "org/apache/struts2/views/jasperreports7/" + jrxml; + URL url = ClassLoaderUtil.getResource(resource, this.getClass()); + JasperCompileManager.compileReportToFile(url.getFile(), url.getFile() + ".jasper"); + result.setLocation(resource + ".jasper"); + } + private static final List> JR_MAP_ARRAY_DATA_SOURCE = Stream.>of( new HashMap<>() {{ put("firstName", "Foo"); diff --git a/plugins/jasperreports7/src/test/resources/org/apache/struts2/views/jasperreports7/csv.jrxml b/plugins/jasperreports7/src/test/resources/org/apache/struts2/views/jasperreports7/csv.jrxml new file mode 100644 index 0000000000..b8e966286c --- /dev/null +++ b/plugins/jasperreports7/src/test/resources/org/apache/struts2/views/jasperreports7/csv.jrxml @@ -0,0 +1,37 @@ + + + + + + + + + + + + "Hello " + + " " + + "!" + + + +