WW-5455 Defines a new plugin to support Jasper Reports 7 (#1124)

* WW-5455 Defines a new plugin to support Jasper Reports 7

* WW-5455 Allows action to modify result behaviour

* WW-5455 Defines exporter as an extension point

* WW-5455 Extracts logic to create connection or dataSource

* WW-5455 Uses defaultDelimiter as more meaningful name

* WW-5455 Fixes parent version after re-basing

* WW-5455 Updates pom to match the latest SNAPSHOT version

* WW-5455 Cleans up code

* WW-5455 Adds the new plugin to BOM

* WW-5455 Fixes broken test
This commit is contained in:
Lukasz Lenart
2025-03-06 06:50:38 +01:00
committed by GitHub
parent 9861b834ab
commit 1c3219e7f9
24 changed files with 2063 additions and 0 deletions
+5
View File
@@ -172,6 +172,11 @@
<artifactId>struts2-jasperreports-plugin</artifactId>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-jasperreports7-plugin</artifactId>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-jfreechart-plugin</artifactId>
+5
View File
@@ -86,6 +86,11 @@
<artifactId>struts2-jasperreports-plugin</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-jasperreports7-plugin</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-javatemplates-plugin</artifactId>
+6
View File
@@ -0,0 +1,6 @@
# Jasper Reports plugin
This plugin allows to use Jasper reports as a one of the result types.
You will find more details in [documentation](https://struts.apache.org/plugins/jasperreports/).
## Installation
Just drop this plugin JAR into `WEB-INF/lib` folder or add it as a Maven dependency.
+83
View File
@@ -0,0 +1,83 @@
<?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.
*/
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>7.0.4-SNAPSHOT</version>
</parent>
<artifactId>struts2-jasperreports7-plugin</artifactId>
<packaging>jar</packaging>
<name>Struts 2 Jasper Reports 7 Plugin [EXPERIMENTAL]</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jasperreports7.version>7.0.1</jasperreports7.version>
</properties>
<dependencies>
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>${jasperreports7.version}</version>
<exclusions>
<!-- not necessary to compile and it force dependency convergence issues -->
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports-pdf</artifactId>
<version>${jasperreports7.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-junit-plugin</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,65 @@
/*
* 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.
*/
package org.apache.struts2.views.jasperreports7;
import net.sf.jasperreports.engine.JasperReport;
import org.apache.struts2.ActionInvocation;
import org.apache.struts2.StrutsException;
import java.util.Locale;
public interface JasperReport7Aware {
/**
* Used to perform an action before report is going to be generated
*
* @param invocation current {@link ActionInvocation}
*/
default void beforeReportGeneration(ActionInvocation invocation) throws StrutsException {
}
/**
* Used to perform an action before report is going to be generated
*
* @param invocation current {@link ActionInvocation}
*/
default void afterReportGeneration(ActionInvocation invocation, JasperReport jasperReport) throws StrutsException {
}
/**
* Allows to specify action specific CSV delimiter, if returns null,
* default one specified by {@link JasperReport7Constants#STRUTS_JASPER_REPORT_CSV_DELIMITER} will be used
*
* @return delimiter or null
*/
default String getCsvDelimiter(ActionInvocation invocation) {
return null;
}
/**
* Allows to specify different local than used by the framework or an action
*
* @param invocation current {@link ActionInvocation}
* @return locale or null
*/
default Locale getReportLocale(ActionInvocation invocation) {
return invocation.getInvocationContext().getLocale();
}
}
@@ -0,0 +1,63 @@
/*
* 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.
*/
package org.apache.struts2.views.jasperreports7;
public interface JasperReport7Constants {
/**
* PDF format constant
*/
String FORMAT_PDF = "pdf";
/**
* XML format constant
*/
String FORMAT_XML = "xml";
/**
* HTML format constant
*/
String FORMAT_HTML = "html";
/**
* XLSX format constant
*/
String FORMAT_XLSX = "xlsx";
/**
* CSV format constant
*/
String FORMAT_CSV = "csv";
/**
* RTF format constant
*/
String FORMAT_RTF = "rtf";
/**
* Allows to define a custom default delimiter when exporting report into CSV file
*/
String STRUTS_JASPER_REPORT_CSV_DELIMITER = "struts.jasperReport7.csv.defaultDelimiter";
/**
* Allows to define a custom url to image servlet used when exporting report into HTML
*/
String STRUTS_JASPER_REPORT_HTML_IMAGE_SERVLET_URL = "struts.jasperReport7.html.imageServletUrl";
}
@@ -0,0 +1,384 @@
/*
* 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.
*/
package org.apache.struts2.views.jasperreports7;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletResponse;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRParameter;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.util.JRLoader;
import net.sf.jasperreports.export.Exporter;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.ActionInvocation;
import org.apache.struts2.StrutsException;
import org.apache.struts2.inject.Inject;
import org.apache.struts2.result.StrutsResultSupport;
import org.apache.struts2.security.NotExcludedAcceptedPatternsChecker;
import org.apache.struts2.util.ValueStack;
import org.apache.struts2.views.jasperreports7.export.JasperReport7ExporterProvider;
import java.io.File;
import java.sql.Connection;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
/**
* <!-- START SNIPPET: description -->
* <p>
* Generates a JasperReports report using the specified format or PDF if no
* format is specified.
* </p>
* <!-- END SNIPPET: description -->
* <p>
* <b>This result type takes the following parameters:</b>
* </p>
* <!-- START SNIPPET: params -->
*
* <ul>
*
* <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>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
* values can be found in {@link JasperReport7Constants}. If no format is
* specified, PDF will be used.</li>
* <li><b>contentDisposition</b> - disposition (defaults to "inline", values are
* typically <i>filename="document.pdf"</i>).</li>
* <li><b>documentName</b> - name of the document (will generate the http header
* <code>Content-disposition = X; filename=X.[format]</code>).</li>
* <li>
* <b>reportParameters</b> - an expression used to retrieve a map of
* report parameters from the value stack. The parameters may be accessed
* in the report via the usual JR mechanism and might include data not
* part of the dataSource, such as the user name of the report creator, etc.
* </li>
* <li>
* <b>connection</b> - a JDBC Connection which can be passed to the
* report instead of dataSource
* </li>
* <li><b>wrapField</b> - defines if fields should warp with ValueStackDataSource
* see <a href="https://issues.apache.org/jira/browse/WW-3698">WW-3698</a> for more details
* </li>
* </ul>
* <p>
* This result follows the same rules from {@link StrutsResultSupport}.
* Specifically, all parameters will be parsed if the "parse" parameter
* is not set to false.
* </p>
* <!-- END SNIPPET: params -->
* <p><b>Example:</b></p>
* <pre>
* <!-- START SNIPPET: example1 -->
* &lt;result name="success" type="jasperReport7"&gt;
* &lt;param name="location"&gt;foo.jasper&lt;/param&gt;
* &lt;param name="dataSource"&gt;mySource&lt;/param&gt;
* &lt;param name="format"&gt;CSV&lt;/param&gt;
* &lt;/result&gt;
* <!-- END SNIPPET: example1 -->
* </pre>
* <p>
* or for pdf
*
* <pre>
* <!-- START SNIPPET: example2 -->
* &lt;result name="success" type="jasperReport7"&gt;
* &lt;param name="location"&gt;foo.jasper&lt;/param&gt;
* &lt;param name="dataSource"&gt;mySource&lt;/param&gt;
* &lt;/result&gt;
* <!-- END SNIPPET: example2 -->
* </pre>
*/
public class JasperReport7Result extends StrutsResultSupport implements JasperReport7Constants {
private static final Logger LOG = LogManager.getLogger(JasperReport7Result.class);
private String parsedDataSource;
protected String dataSource;
protected String format;
protected String documentName;
protected String contentDisposition;
protected String timeZone;
protected boolean wrapField = true;
/**
* Connection can be passed to the report instead of dataSource.
*/
protected String connection;
/**
* Names a report parameters map stack value, allowing additional report parameters from the action.
*/
protected String reportParameters;
private String parsedReportParameters;
/**
* Parameters validator, excludes not accepted params
*/
private NotExcludedAcceptedPatternsChecker notExcludedAcceptedPatterns;
public JasperReport7Result() {
super();
}
@Inject
public void setNotExcludedAcceptedPatterns(NotExcludedAcceptedPatternsChecker notExcludedAcceptedPatterns) {
this.notExcludedAcceptedPatterns = notExcludedAcceptedPatterns;
}
protected void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {
initializeProperties(invocation);
LOG.debug("Creating JasperReport for dataSource: {} and format: {}", dataSource, format);
// Construct the data source for the report.
ValueStack stack = invocation.getStack();
Connection reportConnection = (Connection) stack.findValue(connection);
ValueStackDataSource reportDataSource = null;
if (reportConnection == null) {
reportDataSource = prepareDataSource(stack);
}
if (invocation.getAction() instanceof JasperReport7Aware action) {
LOG.debug("Passing control to action: {} before generating report", invocation.getInvocationContext().getActionName());
action.beforeReportGeneration(invocation);
}
ServletContext servletContext = invocation.getInvocationContext().getServletContext();
String systemId = servletContext.getRealPath(finalLocation);
Map<String, Object> parameters = new ValueStackShadowMap(stack);
File directory = new File(systemId.substring(0, systemId.lastIndexOf(File.separator)));
parameters.put("reportDirectory", directory);
applyLocale(invocation, parameters);
applyTimeZone(invocation, parameters);
applyCustomParameters(stack, parameters);
JasperPrint jasperPrint;
// Fill the report and produce a print object
try {
JasperReport jasperReport = (JasperReport) JRLoader.loadObject(new File(systemId));
if (reportConnection == null) {
jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, reportDataSource);
} else {
jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, reportConnection);
}
if (invocation.getAction() instanceof JasperReport7Aware action) {
LOG.debug("Passing control to action: {} after generating report: {}",
invocation.getInvocationContext().getActionName(), jasperReport.getName());
action.afterReportGeneration(invocation, jasperReport);
}
} catch (JRException e) {
LOG.error("Error building report for uri: {}", systemId, e);
throw new ServletException(e.getMessage(), e);
}
try {
LOG.debug("Export the print object to the desired output format: {}", format);
JasperReport7ExporterProvider<?> exporterProvider = invocation.getInvocationContext().getContainer().getInstance(JasperReport7ExporterProvider.class, format);
if (exporterProvider == null) {
throw new StrutsException("No exporter found for format: " + format);
}
exportReport(invocation, jasperPrint, exporterProvider);
} catch (StrutsException e) {
LOG.error("Error producing: {} report for uri: {}", format, systemId, e);
throw new ServletException(e.getMessage(), e);
} finally {
try {
if (reportConnection != null) {
reportConnection.close();
}
} catch (Exception e) {
LOG.warn("Could not close db connection properly", e);
}
}
}
protected ValueStackDataSource prepareDataSource(ValueStack stack) throws ServletException {
boolean evaluated = parsedDataSource != null && !parsedDataSource.equals(dataSource);
boolean reevaluate = !evaluated || isAcceptableExpression(parsedDataSource);
if (reevaluate) {
return new ValueStackDataSource(stack, parsedDataSource, wrapField);
} else {
throw new ServletException(String.format("Unaccepted dataSource expression [%s]", parsedDataSource));
}
}
protected void applyLocale(ActionInvocation invocation, Map<String, Object> parameters) {
Locale locale = null;
if (invocation.getAction() instanceof JasperReport7Aware action) {
locale = action.getReportLocale(invocation);
}
if (locale == null) {
locale = invocation.getInvocationContext().getLocale();
}
LOG.debug("Using locale: {} to generate report", locale);
parameters.put(JRParameter.REPORT_LOCALE, locale);
}
protected void applyTimeZone(ActionInvocation invocation, Map<String, Object> parameters) {
if (timeZone != null) {
timeZone = conditionalParse(timeZone, invocation);
LOG.debug("Puts timezone in jasper report parameter: {}", timeZone);
final TimeZone tz = TimeZone.getTimeZone(timeZone);
if (tz != null) {
parameters.put(JRParameter.REPORT_TIME_ZONE, tz);
}
}
}
@SuppressWarnings("unchecked")
protected void applyCustomParameters(ValueStack stack, Map<String, Object> parameters) {
boolean evaluated = parsedReportParameters != null && !parsedReportParameters.equals(reportParameters);
boolean reevaluate = !evaluated || isAcceptableExpression(parsedReportParameters);
Map<String, Object> reportParams = reevaluate ? (Map<String, Object>) stack.findValue(parsedReportParameters) : null;
if (reportParams != null) {
LOG.debug("Found report parameters: {}", reportParams);
parameters.putAll(reportParams);
}
}
protected void exportReport(ActionInvocation invocation, JasperPrint jasperPrint, JasperReport7ExporterProvider<?> exporterProvider) throws StrutsException {
HttpServletResponse response = prepapreHttpServletResponse(invocation);
try {
Exporter<?, ?, ?, ?> exporter = exporterProvider.createExporter(invocation, jasperPrint);
LOG.debug("Exporting report: {} as: {} and flushing response stream", jasperPrint.getName(), format);
exporter.exportReport();
response.getOutputStream().flush();
} catch (Exception e) {
throw new StrutsException(e);
}
}
private HttpServletResponse prepapreHttpServletResponse(ActionInvocation invocation) {
HttpServletResponse response = invocation.getInvocationContext().getServletResponse();
if (contentDisposition != null || documentName != null) {
final StringBuilder tmp = new StringBuilder();
tmp.append((contentDisposition == null) ? "inline" : contentDisposition);
if (documentName != null) {
tmp.append("; filename=");
tmp.append(documentName);
tmp.append(".");
tmp.append(format);
}
response.setHeader("Content-disposition", tmp.toString());
}
return response;
}
/**
* Sets up result properties, parsing etc.
*
* @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);
}
format = conditionalParse(format, invocation);
if (StringUtils.isEmpty(format)) {
format = FORMAT_PDF;
}
if (contentDisposition != null) {
contentDisposition = conditionalParse(contentDisposition, invocation);
}
if (documentName != null) {
documentName = conditionalParse(documentName, invocation);
}
parsedReportParameters = conditionalParse(reportParameters, invocation);
}
/**
* Checks if expression doesn't contain vulnerable code
*
* @param expression of result
* @return true|false
* @since 6.0.0
*/
protected boolean isAcceptableExpression(String expression) {
NotExcludedAcceptedPatternsChecker.IsAllowed isAllowed = notExcludedAcceptedPatterns.isAllowed(expression);
if (isAllowed.isAllowed()) {
return true;
}
LOG.warn("Expression [{}] isn't allowed by pattern [{}]! See Accepted / Excluded patterns at\n" +
"https://struts.apache.org/security/", expression, isAllowed.getAllowedPattern());
return false;
}
public void setDataSource(String dataSource) {
this.dataSource = dataSource;
}
public void setFormat(String format) {
this.format = format;
}
public void setDocumentName(String documentName) {
this.documentName = documentName;
}
public void setContentDisposition(String contentDisposition) {
this.contentDisposition = contentDisposition;
}
public void setTimeZone(final String timeZone) {
this.timeZone = timeZone;
}
public void setWrapField(boolean wrapField) {
this.wrapField = wrapField;
}
public void setReportParameters(String reportParameters) {
this.reportParameters = reportParameters;
}
public void setConnection(String connection) {
this.connection = connection;
}
}
@@ -0,0 +1,145 @@
/*
* 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.
*/
package org.apache.struts2.views.jasperreports7;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRField;
import net.sf.jasperreports.engine.JRRewindableDataSource;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.util.MakeIterator;
import org.apache.struts2.util.ValueStack;
import java.util.Iterator;
/**
* Ported to Struts.
*/
public class ValueStackDataSource implements JRRewindableDataSource {
private static final Logger LOG = LogManager.getLogger(ValueStackDataSource.class);
private final ValueStack valueStack;
private final String dataSource;
private final boolean wrapField;
private Iterator<?> iterator;
private boolean firstTimeThrough = true;
/**
* Create a value stack data source on the given iterable property
*
* @param valueStack The value stack to base the data source on
* @param dataSourceParam The property to iterate over for the report
*/
public ValueStackDataSource(ValueStack valueStack, String dataSourceParam, boolean wrapField) {
this.valueStack = valueStack;
this.dataSource = dataSourceParam;
this.wrapField = wrapField;
Object dataSourceValue = valueStack.findValue(dataSource);
if (dataSourceValue != null) {
if (MakeIterator.isIterable(dataSourceValue)) {
iterator = MakeIterator.convert(dataSourceValue);
} else {
Object[] array = new Object[1];
array[0] = dataSourceValue;
iterator = MakeIterator.convert(array);
}
} else {
LOG.warn("Data source value for data source: {} was null", dataSource);
}
}
/**
* Get the value of a given field
*
* @param field The field to get the value for. The expression language to get the value
* of the field is either taken from the description property or from the name of the field
* if the description is <code>null</code>.
* @return an <code>Object</code> containing the field value or a new
* <code>ValueStackDataSource</code> object if the field value evaluates to
* an object that can be iterated over.
*/
public Object getFieldValue(JRField field) {
String expression = field.getName();
Object value = valueStack.findValue(expression);
LOG.debug("Field [{}] = [{}]", field.getName(), value);
if (!wrapField && MakeIterator.isIterable(value) && field.getValueClass().isInstance(value)) {
return value;
} else if (MakeIterator.isIterable(value)) {
// wrap value with ValueStackDataSource if not already wrapped
return new ValueStackDataSource(this.valueStack, expression, wrapField);
} else {
return value;
}
}
/**
* Move to the first item.
*/
public void moveFirst() {
Object dataSourceValue = valueStack.findValue(dataSource);
if (dataSourceValue != null) {
if (MakeIterator.isIterable(dataSourceValue)) {
iterator = MakeIterator.convert(dataSourceValue);
} else {
Object[] array = new Object[1];
array[0] = dataSourceValue;
iterator = MakeIterator.convert(array);
}
} else {
LOG.warn("Data source value for data source [{}] was null", dataSource);
}
}
/**
* Is there any more data
*
* @return <code>true</code> if there are more elements to iterate over and
* <code>false</code> otherwise
* @throws JRException if there is a problem determining whether there
* is more data
*/
public boolean next() throws JRException {
if (firstTimeThrough) {
firstTimeThrough = false;
} else {
valueStack.pop();
}
if ((iterator != null) && (iterator.hasNext())) {
valueStack.push(iterator.next());
if (LOG.isDebugEnabled()) {
LOG.debug("Pushed next value: {}", valueStack.findValue("."));
}
return true;
} else {
LOG.debug("No more values");
return false;
}
}
}
@@ -0,0 +1,82 @@
/*
* 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.
*/
package org.apache.struts2.views.jasperreports7;
import org.apache.struts2.util.ValueStack;
import java.util.HashMap;
import java.util.Objects;
/**
* Ported to Struts:
*/
public class ValueStackShadowMap extends HashMap<String, Object> {
/**
* valueStack reference
*/
transient ValueStack valueStack;
/**
* Constructs an instance of ValueStackShadowMap.
*
* @param valueStack - the underlying valuestack
*/
public ValueStackShadowMap(ValueStack valueStack) {
this.valueStack = valueStack;
}
/**
* Implementation of containsKey(), overriding HashMap implementation.
*
* @param key - The key to check in HashMap and if not found to check on valueStack.
* @return <tt>true</tt>, if contains key, <tt>false</tt> otherwise.
* @see java.util.HashMap#containsKey
*/
@Override
public boolean containsKey(Object key) {
boolean hasKey = super.containsKey(key);
if (!hasKey && key != null && valueStack.findValue(key.toString()) != null) {
hasKey = true;
}
return hasKey;
}
/**
* Implementation of get(), overriding HashMap implementation.
*
* @param key - The key to get in HashMap and if not found there from the valueStack.
* @return value - The object from HashMap or if null, from the valueStack.
* @see java.util.HashMap#get
*/
@Override
public Object get(Object key) {
Object value = super.get(key);
if (key != null && value == null) {
value = valueStack.findValue(key.toString());
}
return value;
}
}
@@ -0,0 +1,96 @@
/*
* 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.
*/
package org.apache.struts2.views.jasperreports7.export;
import jakarta.servlet.http.HttpServletResponse;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.export.JRCsvExporter;
import net.sf.jasperreports.export.SimpleCsvExporterConfiguration;
import net.sf.jasperreports.export.SimpleExporterInput;
import net.sf.jasperreports.export.SimpleWriterExporterOutput;
import net.sf.jasperreports.export.WriterExporterOutput;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.ActionInvocation;
import org.apache.struts2.StrutsException;
import org.apache.struts2.inject.Inject;
import org.apache.struts2.views.jasperreports7.JasperReport7Aware;
import org.apache.struts2.views.jasperreports7.JasperReport7Constants;
import java.io.IOException;
import java.io.OutputStream;
public class JasperReport7CsvExporterProvider implements JasperReport7ExporterProvider<JRCsvExporter> {
private static final Logger LOG = LogManager.getLogger(JasperReport7CsvExporterProvider.class);
/**
* A delimiter used when generating CSV report. By default, "," is used.
*/
private String defaultDelimiter = ",";
@Inject
public JasperReport7CsvExporterProvider(
@Inject(value = JasperReport7Constants.STRUTS_JASPER_REPORT_CSV_DELIMITER, required = false)
String defaultDelimiter
) {
if (StringUtils.isNoneEmpty(defaultDelimiter)) {
LOG.debug("Using custom default delimiter [{}]", defaultDelimiter);
this.defaultDelimiter = defaultDelimiter;
}
}
@Override
public JRCsvExporter createExporter(ActionInvocation invocation, JasperPrint jasperPrint) throws StrutsException {
LOG.debug("Creating: {} exporter", this.getClass().getSimpleName());
HttpServletResponse response = invocation.getInvocationContext().getServletResponse();
response.setContentType("text/csv");
JRCsvExporter exporter = new JRCsvExporter();
String reportDelimiter = null;
if (invocation.getAction() instanceof JasperReport7Aware action) {
reportDelimiter = action.getCsvDelimiter(invocation);
}
if (StringUtils.isEmpty(reportDelimiter)) {
reportDelimiter = defaultDelimiter;
}
LOG.debug("Using delimiter: [{}]", reportDelimiter);
SimpleCsvExporterConfiguration config = new SimpleCsvExporterConfiguration();
config.setFieldDelimiter(reportDelimiter);
config.setRecordDelimiter(reportDelimiter);
exporter.setConfiguration(config);
SimpleExporterInput input = new SimpleExporterInput(jasperPrint);
exporter.setExporterInput(input);
try (OutputStream responseStream = response.getOutputStream()) {
WriterExporterOutput exporterOutput = new SimpleWriterExporterOutput(responseStream);
exporter.setExporterOutput(exporterOutput);
} catch (IOException e) {
LOG.error("Error writing CSV report output using: {}", JasperReport7CsvExporterProvider.class.getName(), e);
throw new StrutsException(e.getMessage(), e);
}
return exporter;
}
}
@@ -0,0 +1,34 @@
/*
* 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.
*/
package org.apache.struts2.views.jasperreports7.export;
import net.sf.jasperreports.engine.JRAbstractExporter;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.export.JRExporterContext;
import net.sf.jasperreports.export.ExporterConfiguration;
import net.sf.jasperreports.export.ExporterOutput;
import net.sf.jasperreports.export.ReportExportConfiguration;
import org.apache.struts2.ActionInvocation;
import org.apache.struts2.StrutsException;
public interface JasperReport7ExporterProvider<T extends JRAbstractExporter<? extends ReportExportConfiguration, ? extends ExporterConfiguration, ? extends ExporterOutput, ? extends JRExporterContext>> {
T createExporter(ActionInvocation invocation, JasperPrint jasperPrint) throws StrutsException;
}
@@ -0,0 +1,85 @@
/*
* 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.
*/
package org.apache.struts2.views.jasperreports7.export;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.export.HtmlExporter;
import net.sf.jasperreports.engine.export.HtmlResourceHandler;
import net.sf.jasperreports.export.SimpleExporterInput;
import net.sf.jasperreports.export.SimpleHtmlExporterOutput;
import net.sf.jasperreports.web.util.WebHtmlResourceHandler;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.ActionInvocation;
import org.apache.struts2.StrutsException;
import org.apache.struts2.inject.Inject;
import org.apache.struts2.views.jasperreports7.JasperReport7Constants;
import java.io.IOException;
import java.io.OutputStream;
public class JasperReport7HtmlExporterProvider implements JasperReport7ExporterProvider<HtmlExporter> {
private static final Logger LOG = LogManager.getLogger(JasperReport7HtmlExporterProvider.class);
/**
* Name of the url that, when prefixed with the context page, can return report images
*/
private String imageServletUrl = "/images/";
@Inject
public JasperReport7HtmlExporterProvider(
@Inject(value = JasperReport7Constants.STRUTS_JASPER_REPORT_HTML_IMAGE_SERVLET_URL, required = false)
String imageServletUrl
) {
if (StringUtils.isNoneEmpty(imageServletUrl)) {
LOG.debug("Using custom image servlet url: {}", imageServletUrl);
this.imageServletUrl = imageServletUrl;
}
}
@Override
public HtmlExporter createExporter(ActionInvocation invocation, JasperPrint jasperPrint) throws StrutsException {
LOG.debug("Creating: {} exporter with image servlet url: {}", this.getClass().getSimpleName(), imageServletUrl);
HttpServletRequest request = invocation.getInvocationContext().getServletRequest();
HttpServletResponse response = invocation.getInvocationContext().getServletResponse();
response.setContentType("text/html");
HtmlExporter exporter = new HtmlExporter();
SimpleExporterInput input = new SimpleExporterInput(jasperPrint);
exporter.setExporterInput(input);
try (OutputStream responseStream = response.getOutputStream()) {
SimpleHtmlExporterOutput exporterOutput = new SimpleHtmlExporterOutput(responseStream);
HtmlResourceHandler imageHandler = new WebHtmlResourceHandler(request.getContextPath() + imageServletUrl + "%s");
exporterOutput.setImageHandler(imageHandler);
exporter.setExporterOutput(exporterOutput);
} catch (IOException e) {
LOG.error("Error writing HTML report output using: {}", JasperReport7HtmlExporterProvider.class.getName(), e);
throw new StrutsException(e.getMessage(), e);
}
return exporter;
}
}
@@ -0,0 +1,61 @@
/*
* 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.
*/
package org.apache.struts2.views.jasperreports7.export;
import jakarta.servlet.http.HttpServletResponse;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.export.OutputStreamExporterOutput;
import net.sf.jasperreports.export.SimpleExporterInput;
import net.sf.jasperreports.export.SimpleOutputStreamExporterOutput;
import net.sf.jasperreports.pdf.JRPdfExporter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.ActionInvocation;
import org.apache.struts2.StrutsException;
import java.io.IOException;
import java.io.OutputStream;
public class JasperReport7PdfExporterProvider implements JasperReport7ExporterProvider<JRPdfExporter> {
private static final Logger LOG = LogManager.getLogger(JasperReport7PdfExporterProvider.class);
@Override
public JRPdfExporter createExporter(ActionInvocation invocation, JasperPrint jasperPrint) throws StrutsException {
LOG.debug("Creating: {} exporter", this.getClass().getSimpleName());
HttpServletResponse response = invocation.getInvocationContext().getServletResponse();
response.setContentType("application/pdf");
JRPdfExporter exporter = new JRPdfExporter();
SimpleExporterInput input = new SimpleExporterInput(jasperPrint);
exporter.setExporterInput(input);
try (OutputStream responseStream = response.getOutputStream()) {
OutputStreamExporterOutput exporterOutput = new SimpleOutputStreamExporterOutput(responseStream);
exporter.setExporterOutput(exporterOutput);
} catch (IOException e) {
LOG.error("Error writing PDF report output using: {}", JasperReport7PdfExporterProvider.class.getName(), e);
throw new StrutsException(e.getMessage(), e);
}
return exporter;
}
}
@@ -0,0 +1,61 @@
/*
* 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.
*/
package org.apache.struts2.views.jasperreports7.export;
import jakarta.servlet.http.HttpServletResponse;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.export.JRRtfExporter;
import net.sf.jasperreports.export.SimpleExporterInput;
import net.sf.jasperreports.export.SimpleWriterExporterOutput;
import net.sf.jasperreports.export.WriterExporterOutput;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.ActionInvocation;
import org.apache.struts2.StrutsException;
import java.io.IOException;
import java.io.OutputStream;
public class JasperReport7RtfExporterProvider implements JasperReport7ExporterProvider<JRRtfExporter> {
private static final Logger LOG = LogManager.getLogger(JasperReport7RtfExporterProvider.class);
@Override
public JRRtfExporter createExporter(ActionInvocation invocation, JasperPrint jasperPrint) throws StrutsException {
LOG.debug("Creating: {} exporter", this.getClass().getSimpleName());
HttpServletResponse response = invocation.getInvocationContext().getServletResponse();
response.setContentType("application/rtf");
JRRtfExporter exporter = new JRRtfExporter();
SimpleExporterInput input = new SimpleExporterInput(jasperPrint);
exporter.setExporterInput(input);
try (OutputStream responseStream = response.getOutputStream()) {
WriterExporterOutput exporterOutput = new SimpleWriterExporterOutput(responseStream);
exporter.setExporterOutput(exporterOutput);
} catch (IOException e) {
LOG.error("Error writing RTF report output using: {}", JasperReport7RtfExporterProvider.class.getName(), e);
throw new StrutsException(e.getMessage(), e);
}
return exporter;
}
}
@@ -0,0 +1,61 @@
/*
* 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.
*/
package org.apache.struts2.views.jasperreports7.export;
import jakarta.servlet.http.HttpServletResponse;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.export.ooxml.JRXlsxExporter;
import net.sf.jasperreports.export.OutputStreamExporterOutput;
import net.sf.jasperreports.export.SimpleExporterInput;
import net.sf.jasperreports.export.SimpleOutputStreamExporterOutput;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.ActionInvocation;
import org.apache.struts2.StrutsException;
import java.io.IOException;
import java.io.OutputStream;
public class JasperReport7XlsxExporterProvider implements JasperReport7ExporterProvider<JRXlsxExporter> {
private static final Logger LOG = LogManager.getLogger(JasperReport7XlsxExporterProvider.class);
@Override
public JRXlsxExporter createExporter(ActionInvocation invocation, JasperPrint jasperPrint) throws StrutsException {
LOG.debug("Creating: {} exporter", this.getClass().getSimpleName());
HttpServletResponse response = invocation.getInvocationContext().getServletResponse();
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
JRXlsxExporter exporter = new JRXlsxExporter();
SimpleExporterInput input = new SimpleExporterInput(jasperPrint);
exporter.setExporterInput(input);
try (OutputStream responseStream = response.getOutputStream()) {
OutputStreamExporterOutput exporterOutput = new SimpleOutputStreamExporterOutput(responseStream);
exporter.setExporterOutput(exporterOutput);
} catch (IOException e) {
LOG.error("Error writing XLSX report output using: {}", JasperReport7XlsxExporterProvider.class.getName(), e);
throw new StrutsException(e.getMessage(), e);
}
return exporter;
}
}
@@ -0,0 +1,61 @@
/*
* 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.
*/
package org.apache.struts2.views.jasperreports7.export;
import jakarta.servlet.http.HttpServletResponse;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.export.JRXmlExporter;
import net.sf.jasperreports.export.SimpleExporterInput;
import net.sf.jasperreports.export.SimpleXmlExporterOutput;
import net.sf.jasperreports.export.XmlExporterOutput;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.ActionInvocation;
import org.apache.struts2.StrutsException;
import java.io.IOException;
import java.io.OutputStream;
public class JasperReport7XmlExporterProvider implements JasperReport7ExporterProvider<JRXmlExporter> {
private static final Logger LOG = LogManager.getLogger(JasperReport7XmlExporterProvider.class);
@Override
public JRXmlExporter createExporter(ActionInvocation invocation, JasperPrint jasperPrint) throws StrutsException {
LOG.debug("Creating: {} exporter", this.getClass().getSimpleName());
HttpServletResponse response = invocation.getInvocationContext().getServletResponse();
response.setContentType("text/xml");
JRXmlExporter exporter = new JRXmlExporter();
SimpleExporterInput input = new SimpleExporterInput(jasperPrint);
exporter.setExporterInput(input);
try (OutputStream responseOutput = response.getOutputStream()) {
XmlExporterOutput exporterOutput = new SimpleXmlExporterOutput(responseOutput);
exporter.setExporterOutput(exporterOutput);
} catch (IOException e) {
LOG.error("Error writing report XML output using: {}", JRXmlExporter.class.getName(), e);
throw new StrutsException(e.getMessage(), e);
}
return exporter;
}
}
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed 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.
@@ -0,0 +1,5 @@
Apache Struts
Copyright 2000-2024 The Apache Software Foundation
This product includes software developed by
The Apache Software Foundation (http://www.apache.org/).
@@ -0,0 +1,59 @@
<?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.
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
<struts>
<constant name="struts.jasperReport7.csv.defaultDelimiter" value=","/>
<bean name="pdf"
class="org.apache.struts2.views.jasperreports7.export.JasperReport7PdfExporterProvider"
type="org.apache.struts2.views.jasperreports7.export.JasperReport7ExporterProvider"/>
<bean name="csv"
class="org.apache.struts2.views.jasperreports7.export.JasperReport7CsvExporterProvider"
type="org.apache.struts2.views.jasperreports7.export.JasperReport7ExporterProvider"/>
<bean name="html"
class="org.apache.struts2.views.jasperreports7.export.JasperReport7HtmlExporterProvider"
type="org.apache.struts2.views.jasperreports7.export.JasperReport7ExporterProvider"/>
<bean name="xlsx"
class="org.apache.struts2.views.jasperreports7.export.JasperReport7XlsxExporterProvider"
type="org.apache.struts2.views.jasperreports7.export.JasperReport7ExporterProvider"/>
<bean name="xml"
class="org.apache.struts2.views.jasperreports7.export.JasperReport7XmlExporterProvider"
type="org.apache.struts2.views.jasperreports7.export.JasperReport7ExporterProvider"/>
<bean name="rtf"
class="org.apache.struts2.views.jasperreports7.export.JasperReport7RtfExporterProvider"
type="org.apache.struts2.views.jasperreports7.export.JasperReport7ExporterProvider"/>
<package name="jasperreports7-default" extends="struts-default">
<result-types>
<result-type name="jasperReport7" class="org.apache.struts2.views.jasperreports7.JasperReport7Result"/>
</result-types>
</package>
</struts>
+56
View File
@@ -0,0 +1,56 @@
<?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.
*/
-->
<project name="Apache Struts">
<skin>
<groupId>org.apache.maven.skins</groupId>
<artifactId>maven-fluido-skin</artifactId>
<version>${fluido-skin.version}</version>
</skin>
<bannerLeft>
<name>Apache Software Foundation</name>
<src>http://www.apache.org/images/asf-logo.gif</src>
<href>http://www.apache.org/</href>
</bannerLeft>
<bannerRight>
<name>Apache Struts</name>
<src>http://struts.apache.org/img/struts-logo.svg</src>
<href>http://struts.apache.org/</href>
</bannerRight>
<publishDate position="left"/>
<version position="right"/>
<body>
<links>
<item name="Apache" href="http://www.apache.org/"/>
<item name="Struts" href="http://struts.apache.org/"/>
</links>
<menu ref="parent"/>
<menu ref="reports"/>
<footer>
<![CDATA[<div class="row span12">
Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts project
logos are trademarks of The Apache Software Foundation.
</div>]]>
</footer>
</body>
</project>
@@ -0,0 +1,368 @@
/*
* 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.
*/
package org.apache.struts2.views.jasperreports7;
import jakarta.servlet.ServletException;
import net.sf.jasperreports.engine.JasperCompileManager;
import org.apache.struts2.ActionContext;
import org.apache.struts2.junit.StrutsTestCase;
import org.apache.struts2.mock.MockActionInvocation;
import org.apache.struts2.security.NotExcludedAcceptedPatternsChecker;
import org.apache.struts2.util.ClassLoaderUtil;
import org.apache.struts2.util.ValueStack;
import java.net.URL;
import java.sql.Connection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
public class JasperReport7ResultTest extends StrutsTestCase {
private MockActionInvocation invocation;
private ValueStack stack;
private JasperReport7Result result;
public void testConnClose() throws Exception {
// given
Connection connection = createMock(Connection.class);
final Boolean[] closed = {Boolean.FALSE};
connection.close();
expectLastCall().andAnswer(() -> {
closed[0] = true;
return null;
});
replay(connection);
stack.push(connection);
result.setConnection("top");
assertFalse(closed[0]);
// when
result.execute(this.invocation);
// then
verify(connection);
assertTrue(closed[0]);
}
public void testDataSourceNotAccepted() throws Exception {
// given
stack.push(new Object() {
public String getDatasourceName() {
return "getDatasource()";
}
public List<Map<String, String>> getDatasource() {
return JR_MAP_ARRAY_DATA_SOURCE;
}
});
result.setDataSource("${datasourceName}");
try {
result.execute(this.invocation);
} catch (ServletException e) {
assertEquals("Unaccepted dataSource expression [getDatasource()]", e.getMessage());
}
// verify that above test has really effect
result.setNotExcludedAcceptedPatterns(NO_EXCLUSION_ACCEPT_ALL_PATTERNS_CHECKER);
// when
result.execute(this.invocation);
// then
assertThat(response.getContentType()).isEqualTo("text/xml");
assertThat(response.getContentAsString()).contains("Hello Foo Bar!");
}
public void testDataSourceAccepted() throws Exception {
// given
stack.push(new Object() {
public String getDatasourceName() {
return "datasource";
}
public List<Map<String, String>> getDatasource() {
return JR_MAP_ARRAY_DATA_SOURCE;
}
});
result.setDataSource("${datasourceName}");
// when
result.execute(this.invocation);
// then
assertThat(response.getContentType()).isEqualTo("text/xml");
assertTrue(response.getContentAsString().contains("Hello Foo Bar!"));
}
public void testDataSourceExpressionAccepted() throws Exception {
// given
result.setDataSource("{#{'firstName':'Qux', 'lastName':'Quux'}}");
// when
result.execute(this.invocation);
// then
assertThat(response.getContentType()).isEqualTo("text/xml");
assertThat(response.getContentAsString()).contains("Hello Qux Quux!");
}
public void testReportParametersNotAccepted() throws Exception {
// given
result.setDataSource("{#{'firstName':'ignore', 'lastName':'ignore'}}");
stack.push(new Object() {
public String getReportParametersName() {
return "getReportParameters()";
}
public Map<String, String> getReportParameters() {
return new HashMap<>() {{
put("title", "Baz");
}};
}
});
result.setReportParameters("${reportParametersName}");
// when
result.execute(this.invocation);
assertTrue(response.getContentAsString().contains("null Report"));
// verify that above test has really effect
response.setCommitted(false);
response.reset();
result.setNotExcludedAcceptedPatterns(NO_EXCLUSION_ACCEPT_ALL_PATTERNS_CHECKER);
// when
result.execute(this.invocation);
// then
assertThat(response.getContentType()).isEqualTo("text/xml");
assertTrue(response.getContentAsString().contains("Baz Report"));
}
public void testReportParametersAccepted() throws Exception {
// given
result.setDataSource("{#{'firstName':'ignore', 'lastName':'ignore'}}");
stack.push(new Object() {
public String getReportParametersName() {
return "reportParameters";
}
public Map<String, String> getReportParameters() {
return new HashMap<>() {{
put("title", "Baz");
}};
}
});
result.setReportParameters("${reportParametersName}");
// when
result.execute(this.invocation);
// then
assertThat(response.getContentType()).isEqualTo("text/xml");
assertThat(response.getContentAsString()).contains("Baz Report");
}
public void testExportToXml() throws Exception {
// given
result.setDataSource("{#{'firstName':'ignore', 'lastName':'ignore'}}");
result.setReportParameters("#{'title':'Qux'}");
result.setFormat(JasperReport7Constants.FORMAT_XML);
// when
result.execute(this.invocation);
// then
assertThat(response.getContentType()).isEqualTo("text/xml");
assertThat(response.getContentAsString()).contains("Qux Report");
}
public void testExportToCsv() throws Exception {
// given
result.setDataSource("{#{'firstName':'ignore', 'lastName':'ignore'}}");
result.setReportParameters("#{'title':'Qux'}");
result.setFormat(JasperReport7Constants.FORMAT_CSV);
// when
result.execute(this.invocation);
// then
assertThat(response.getContentType()).isEqualTo("text/csv");
assertThat(response.getContentAsString()).contains("Qux Report");
}
public void testExportToRtf() throws Exception {
// given
result.setDataSource("{#{'firstName':'ignore', 'lastName':'ignore'}}");
result.setReportParameters("#{'title':'Qux'}");
result.setFormat(JasperReport7Constants.FORMAT_RTF);
// when
result.execute(this.invocation);
// then
assertThat(response.getContentType()).isEqualTo("application/rtf");
assertThat(response.getContentAsString()).contains("Qux Report");
}
public void testExportToPdf() throws Exception {
// given
result.setDataSource("{#{'firstName':'ignore', 'lastName':'ignore'}}");
result.setReportParameters("#{'title':'Qux'}");
result.setFormat(JasperReport7Constants.FORMAT_PDF);
// when
result.execute(this.invocation);
// then
assertThat(response.getContentType()).isEqualTo("application/pdf");
assertThat(response.getContentAsByteArray()).hasSizeGreaterThan(0);
}
public void testExportToHtml() throws Exception {
// given
result.setDataSource("{#{'firstName':'ignore', 'lastName':'ignore'}}");
result.setReportParameters("#{'title':'Qux'}");
result.setFormat(JasperReport7Constants.FORMAT_HTML);
// when
result.execute(this.invocation);
// then
assertThat(response.getContentType()).isEqualTo("text/html");
assertThat(response.getContentAsString()).contains("Qux Report");
}
public void testExportToXlsx() throws Exception {
// given
result.setDataSource("{#{'firstName':'ignore', 'lastName':'ignore'}}");
result.setReportParameters("#{'title':'Qux'}");
result.setFormat(JasperReport7Constants.FORMAT_XLSX);
// when
result.execute(this.invocation);
// then
assertThat(response.getContentType()).isEqualTo("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
assertThat(response.getContentAsByteArray()).hasSizeGreaterThan(0);
}
@Override
protected void setUp() throws Exception {
super.setUp();
this.request.setRequestURI("http://someuri");
ActionContext context = ActionContext.getContext()
.withServletResponse(this.response)
.withServletRequest(this.request)
.withServletContext(this.servletContext);
this.stack = context.getValueStack();
this.invocation = new MockActionInvocation();
this.invocation.setInvocationContext(context);
this.invocation.setStack(this.stack);
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");
result.setFormat(JasperReport7Constants.FORMAT_XML);
}
private static final List<Map<String, String>> JR_MAP_ARRAY_DATA_SOURCE = Stream.<Map<String, String>>of(
new HashMap<>() {{
put("firstName", "Foo");
put("lastName", "Bar");
}}
).toList();
private static final NotExcludedAcceptedPatternsChecker NO_EXCLUSION_ACCEPT_ALL_PATTERNS_CHECKER
= new NotExcludedAcceptedPatternsChecker() {
@Override
public IsAllowed isAllowed(String value) {
return IsAllowed.yes("*");
}
@Override
public IsAccepted isAccepted(String value) {
return null;
}
@Override
public void setAcceptedPatterns(String commaDelimitedPatterns) {
}
@Override
public void setAcceptedPatterns(String[] patterns) {
}
@Override
public void setAcceptedPatterns(Set<String> patterns) {
}
@Override
public Set<Pattern> getAcceptedPatterns() {
return null;
}
@Override
public IsExcluded isExcluded(String value) {
return null;
}
@Override
public void setExcludedPatterns(String commaDelimitedPatterns) {
}
@Override
public void setExcludedPatterns(String[] patterns) {
}
@Override
public void setExcludedPatterns(Set<String> patterns) {
}
@Override
public Set<Pattern> getExcludedPatterns() {
return null;
}
};
}
@@ -0,0 +1,34 @@
<?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.
*/
-->
<Configuration>
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT">
<PatternLayout pattern="[%-5p] %C{2} (%F:%L) - %m%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="STDOUT"/>
</Root>
<Logger name="org.apache.struts2.views.jasperreports7" level="info"/>
</Loggers>
</Configuration>
@@ -0,0 +1,42 @@
<?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="SimpleReport" language="java" pageWidth="842" pageHeight="595" orientation="Landscape" columnWidth="802"
uuid="3de5a1ab-15fd-4dec-bc88-dc18453bf715">
<parameter name="title"/>
<field name="firstName"/>
<field name="lastName"/>
<title height="40">
<element kind="textField" x="0" y="10" width="515" height="30" fontSize="22.0" hTextAlign="Center"
uuid="26fc2f4f-de0f-411a-b386-e67caf96f441">
<expression><![CDATA[$P{title}]]> + " Report"</expression>
</element>
</title>
<detail>
<band height="16">
<element kind="textField" x="0" y="0" width="100" height="16"
uuid="ef0654b5-f925-42ed-a611-bc5b3db61c43">
<expression>"Hello " + <![CDATA[$F{firstName}]]> + " " + <![CDATA[$F{lastName}]]> + "!"</expression>
</element>
</band>
</detail>
</jasperReport>
+1
View File
@@ -39,6 +39,7 @@
<module>config-browser</module>
<module>convention</module>
<module>jasperreports</module>
<module>jasperreports7</module>
<module>javatemplates</module>
<module>jfreechart</module>
<module>json</module>