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