This is an automated email from the ASF dual-hosted git repository.
lukaszlenart pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/struts.git
The following commit(s) were added to refs/heads/main by this push:
new 8f8be8f4b WW-3245 feat(jasperreports): fill from report parameters
when no dataSource or connection is set (#1919)
8f8be8f4b is described below
commit 8f8be8f4b9346dd69f50bf5169a05941dea39c08
Author: Lukasz Lenart <[email protected]>
AuthorDate: Sat Sep 12 10:57:34 2026 +0200
WW-3245 feat(jasperreports): fill from report parameters when no dataSource
or connection is set (#1919)
Both result types insisted on either dataSource or connection and only ever
called the three-argument JasperFillManager.fillReport overloads, so a
report whose data comes from a JasperReports query executer (Hibernate,
CSV, JSON, EJBQL, ...) could not be filled without first materialising a
List in the action.
When neither is configured the result now calls fillReport(report, params)
and lets JasperReports resolve the data from the parameter map exactly as
its standalone API does: the executer's supplier object (HIBERNATE_SESSION,
CSV_INPUT_STREAM, ...) or a ready REPORT_DATA_SOURCE / REPORT_CONNECTION is
handed over via reportParameters. No parameter-key inspection and no new
dependency, so every present and future executer works the same way.
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
---
.../views/jasperreports/JasperReportsResult.java | 24 +++++++-------
.../jasperreports/JasperReportsResultTest.java | 28 ++++++++++++++--
.../apache/struts2/views/jasperreports/csv.jrxml | 38 ++++++++++++++++++++++
.../views/jasperreports7/JasperReport7Result.java | 23 +++++++------
.../jasperreports7/JasperReport7ResultTest.java | 32 ++++++++++++++++--
.../apache/struts2/views/jasperreports7/csv.jrxml | 37 +++++++++++++++++++++
6 files changed, 155 insertions(+), 27 deletions(-)
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 f24ddd1b8..b014c9ecc 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 @@ import java.util.TimeZone;
*
* <li><b>location (default)</b> - the location where the compiled jasper
report
* definition is (foo.jasper), relative from current URL.</li>
- * <li><b>dataSource (required)</b> - the EL expression used to retrieve the
- * datasource from the value stack (usually a List).</li>
+ * <li><b>dataSource</b> - 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.
+ * <code>HIBERNATE_SESSION</code>, <code>CSV_INPUT_STREAM</code>,
+ * <code>JSON_INPUT_STREAM</code>) or a ready <code>REPORT_DATA_SOURCE</code> /
+ * <code>REPORT_CONNECTION</code> can be handed over via reportParameters.</li>
* <li><b>parse</b> - true by default. If set to false, the location param will
* not be parsed for EL expressions.</li>
* <li><b>format</b> - the format in which the report should be generated.
Valid
@@ -258,7 +263,6 @@ public class JasperReportsResult extends
StrutsResultSupport implements JasperRe
}
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 @@ public class JasperReportsResult extends
StrutsResultSupport implements JasperRe
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 @@ public class JasperReportsResult extends
StrutsResultSupport implements JasperRe
// 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 @@ public class JasperReportsResult extends
StrutsResultSupport implements JasperRe
* @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 1fb31763e..beb563c12 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.StrutsStatics;
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 class JasperReportsResultTest extends
StrutsTestCase {
assertTrue(response.getContentAsString().contains("Qux Report"));
}
+ public void testFillFromReportParametersWithoutDataSourceOrConnection()
throws Exception {
+ stack.push(new Object() {
+ public Map<String, Object> 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 @@ public class JasperReportsResultTest extends
StrutsTestCase {
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<String, String>[] JR_MAP_ARRAY_DATA_SOURCE = new
Map[]{
new HashMap<String, String>() {{
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 000000000..f235420b9
--- /dev/null
+++
b/plugins/jasperreports/src/test/resources/org/apache/struts2/views/jasperreports/csv.jrxml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+-->
+<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+
xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports
http://jasperreports.sourceforge.net/xsd/jasperreport.xsd"
+ name="csv" pageWidth="842" pageHeight="595"
orientation="Landscape" columnWidth="802">
+ <property name="net.sf.jasperreports.csv.column.names"
value="firstName,lastName"/>
+ <queryString language="csv"><![CDATA[]]></queryString>
+ <field name="firstName" />
+ <field name="lastName" />
+ <detail>
+ <band height="16">
+ <textField>
+ <reportElement x="0" y="0" width="100" height="16" />
+ <textFieldExpression>"Hello " + <![CDATA[$F{firstName}]]> + "
" + <![CDATA[$F{lastName}]]> + "!"</textFieldExpression>
+ </textField>
+ </band>
+ </detail>
+</jasperReport>
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 e8d26bb82..fde8590bc 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 @@ import java.util.TimeZone;
*
* <li><b>location (default)</b> - the location where the compiled jasper
report
* definition is (foo.jasper), relative from current URL.</li>
- * <li><b>dataSource (required)</b> - the EL expression used to retrieve the
- * datasource from the value stack (usually a List).</li>
+ * <li><b>dataSource</b> - 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.
+ * <code>HIBERNATE_SESSION</code>, <code>CSV_INPUT_STREAM</code>,
+ * <code>JSON_INPUT_STREAM</code>) or a ready <code>REPORT_DATA_SOURCE</code> /
+ * <code>REPORT_CONNECTION</code> can be handed over via reportParameters.</li>
* <li><b>parse</b> - true by default. If set to false, all the parameters will
* not be parsed for EL expressions.</li>
* <li><b>format</b> - the format in which the report should be generated.
Valid
@@ -161,7 +166,7 @@ public class JasperReport7Result extends
StrutsResultSupport implements JasperRe
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 @@ public class JasperReport7Result extends
StrutsResultSupport implements JasperRe
// 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 @@ public class JasperReport7Result extends
StrutsResultSupport implements JasperRe
* @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 38d3fe27e..d1f82d5dc 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.security.NotExcludedAcceptedPatternsChecker;
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 class JasperReport7ResultTest extends
StrutsTestCase {
assertThat(response.getContentAsString()).contains("Baz Report");
}
+ public void testFillFromReportParametersWithoutDataSourceOrConnection()
throws Exception {
+ // given
+ stack.push(new Object() {
+ public Map<String, Object> 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 @@ public class JasperReport7ResultTest extends
StrutsTestCase {
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<Map<String, String>> JR_MAP_ARRAY_DATA_SOURCE =
Stream.<Map<String, String>>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 000000000..b8e966286
--- /dev/null
+++
b/plugins/jasperreports7/src/test/resources/org/apache/struts2/views/jasperreports7/csv.jrxml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+-->
+<jasperReport name="CsvReport" language="java" pageWidth="842"
pageHeight="595" orientation="Landscape" columnWidth="802"
+ uuid="5a2a5a9e-4c9c-4f3e-9c1c-6a1a7b1f2d30">
+ <property name="net.sf.jasperreports.csv.column.names"
value="firstName,lastName"/>
+ <query language="csv"><![CDATA[]]></query>
+ <field name="firstName"/>
+ <field name="lastName"/>
+
+ <detail>
+ <band height="16">
+ <element kind="textField" x="0" y="0" width="100" height="16"
+ uuid="9a7c6d2e-2b3f-4d5e-8f60-1b2c3d4e5f60">
+ <expression>"Hello " + <![CDATA[$F{firstName}]]> + " " +
<![CDATA[$F{lastName}]]> + "!"</expression>
+ </element>
+ </band>
+ </detail>
+</jasperReport>