Hi guys, if you use jasper report in a web app with Struts2 and you deploy it in Tomcat with unpacked option set to true, it doesn't work because it's impossible to find the ation's relative path; I've modify the class org.apache.struts2.views.jasperreports.JasperReportsResult so you ca use absolute path in the result's location tag. If you set tag locationAbsolute to true (default false), jasper use the path of location tag without add at the beginning the action's absolute path:

<result name="pdf" type="jasper">
<param name="locationAbsolute">true</param>
<param name="location">/home/nicosia/report/fileJasper.jasper</param>
......
</result>

Here there is the new version of the source class:

/*

  • $Id: JasperReportsResult.java,v 1.1 2012/05/10 16:06:15 34200007 Exp $
    *
  • 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.jasperreports;

import com.opensymphony.xwork2.ActionInvocation;

import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExporter;
import net.sf.jasperreports.engine.JRExporterParameter;
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.export.JRCsvExporter;
import net.sf.jasperreports.engine.export.JRCsvExporterParameter;
import net.sf.jasperreports.engine.export.JRHtmlExporter;
import net.sf.jasperreports.engine.export.JRHtmlExporterParameter;
import net.sf.jasperreports.engine.export.JRPdfExporter;
import net.sf.jasperreports.engine.export.JRRtfExporter;
import net.sf.jasperreports.engine.export.JRXlsExporter;
import net.sf.jasperreports.engine.export.JRXmlExporter;
import net.sf.jasperreports.engine.util.JRLoader;
import org.apache.commons.lang.StringUtils;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.dispatcher.StrutsResultSupport;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.util.HashMap;
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 -->
  • <p/>
  • <ul>
  • <p/>
  • <li><b>locationAbsolute (optional, boolean, default false)</b> - specify if location path is
  • absolute or relative from current URL.</li>
  • <li><b>location (default)</b> - the location where the compiled jasper report
  • definition is (foo.jasper), relative from current URL if locationAbsolute is not true.</li>
  • <p/>
  • <li><b>dataSource (required)</b> - the EL expression used to retrieve the
  • datasource from the value stack (usually a List).</li>
  • <p/>
  • <li><b>parse</b> - true by default. If set to false, the location param will
  • not be parsed for EL expressions.</li>
  • <p/>
  • <li><b>format</b> - the format in which the report should be generated. Valid
  • values can be found in
    Unknown macro: {@link JasperReportConstants}
    . If no format is
  • specified, PDF will be used.</li>
  • <p/>
  • <li><b>contentDisposition</b> - disposition (defaults to "inline", values are
  • typically <i>filename="document.pdf"</i>).</li>
  • <p/>
  • <li><b>documentName</b> - name of the document (will generate the http header
  • <code>Content-disposition = X; filename=X.[format]</code>).</li>
  • <p/>
  • <li><b>delimiter</b> - the delimiter used when generating CSV reports. By
  • default, the character used is ",".</li>
  • <p/>
  • <li><b>imageServletUrl</b> - name of the url that, when prefixed with the
  • context page, can return report images.</li>
  • <p/>
  • <li>
  • <b>reportParameters</b> - (2.1.2+) OGNL 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>
  • <p/>
  • <li>
  • <b>exportParameters</b> - (2.1.2+) OGNL expression used to retrieve a map of
  • JR exporter parameters from the value stack. The export parameters are
  • used to customize the JR export. For example, a PDF export might enable
  • encryption and set the user password to a string known to the report creator.
  • </li>
  • <p/>
  • <li>
  • <b>connection</b> - (2.1.7+) JDBC Connection which can be passed to the
  • report instead of dataSource
  • </li>
  • <p/>
  • </ul>
  • <p/>
  • <p>
  • This result follows the same rules from
    Unknown macro: {@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 -->
  • <result name="success" type="jasper">
  • <param name="location">foo.jasper</param>
  • <param name="dataSource">mySource</param>
  • <param name="format">CSV</param>
  • </result>
  • <!-- END SNIPPET: example1 --></pre>
  • or for pdf
  • <pre><!-- START SNIPPET: example2 -->
  • <result name="success" type="jasper">
  • <param name="location">foo.jasper</param>
  • <param name="dataSource">mySource</param>
  • </result>
  • <!-- END SNIPPET: example2 --></pre>
    */
    public class JasperReportsResult extends StrutsResultSupport implements JasperReportConstants {

private static final long serialVersionUID = -2523174799621182907L;

private final static Logger LOG = LoggerFactory.getLogger(JasperReportsResult.class);

protected String dataSource;
protected String format;
protected String documentName;
protected String contentDisposition;
protected String delimiter;
protected String imageServletUrl = "/images/";
protected String timeZone;
protected boolean locationAbsolute = false;

public void setLocationAbsolute(boolean locationAbsolute)

Unknown macro: { this.locationAbsolute = locationAbsolute; }

/**

  • Connection which can be passed to the report
  • instead od dataSource.
    */
    protected String connection;

/**

  • Names a report parameters map stack value, allowing
  • additional report parameters from the action.
    */
    protected String reportParameters;

/**

  • Names an exporter parameters map stack value,
  • allowing the use of custom export parameters.
    */
    protected String exportParameters;

/**

  • Default ctor.
    */
    public JasperReportsResult()
    Unknown macro: { super(); }

/**

  • Default ctor with location.
    *
  • @param location Result location.
    */
    public JasperReportsResult(String location)
    Unknown macro: { super(location); }

public String getImageServletUrl()

Unknown macro: { return imageServletUrl; }

public void setImageServletUrl(final String imageServletUrl)

Unknown macro: { this.imageServletUrl = imageServletUrl; }

public void setDataSource(String dataSource)

Unknown macro: { this.dataSource = dataSource; }

public void setFormat(String format)

Unknown macro: { this.format = format; }

public void setDocumentName(String documentName)

Unknown macro: { this.documentName = documentName; }

public void setContentDisposition(String contentDisposition)

Unknown macro: { this.contentDisposition = contentDisposition; }

public void setDelimiter(String delimiter)

Unknown macro: { this.delimiter = delimiter; }

/**

  • set time zone id
    *
  • @param timeZone
    */
    public void setTimeZone(final String timeZone)
    Unknown macro: { this.timeZone = timeZone; }

public String getReportParameters()

Unknown macro: { return reportParameters; }

public void setReportParameters(String reportParameters)

Unknown macro: { this.reportParameters = reportParameters; }

public String getExportParameters()

Unknown macro: { return exportParameters; }

public void setExportParameters(String exportParameters)

Unknown macro: { this.exportParameters = exportParameters; }

public String getConnection()

Unknown macro: { return connection; }

public void setConnection(String connection)

Unknown macro: { this.connection = 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);

if (LOG.isDebugEnabled())

Unknown macro: { LOG.debug("Creating JasperReport for dataSource = " + dataSource + ", format = " + format); }

HttpServletRequest request = (HttpServletRequest) invocation.getInvocationContext().get(ServletActionContext.HTTP_REQUEST);
HttpServletResponse response = (HttpServletResponse) invocation.getInvocationContext().get(ServletActionContext.HTTP_RESPONSE);

// Handle IE special case: it sends a "contype" request first.
// TODO Set content type to config settings?
if ("contype".equals(request.getHeader("User-Agent"))) {
try

Unknown macro: { response.setContentType("application/pdf"); response.setContentLength(0); ServletOutputStream outputStream = response.getOutputStream(); outputStream.close(); }

catch (IOException e)

Unknown macro: { LOG.error("Error writing report output", e); throw new ServletException(e.getMessage(), e); }

return;
}

// Construct the data source for the report.
ValueStack stack = invocation.getStack();
ValueStackDataSource stackDataSource = null;

Connection conn = (Connection) stack.findValue(connection);
if (conn == null)
stackDataSource = new ValueStackDataSource(stack, dataSource);

// Determine the directory that the report file is in and set the reportDirectory parameter
// For WW 2.1.7:
// ServletContext servletContext = ((ServletConfig) invocation.getInvocationContext().get(ServletActionContext.SERVLET_CONFIG)).getServletContext();
ServletContext servletContext = (ServletContext) invocation.getInvocationContext().get(ServletActionContext.SERVLET_CONTEXT);
String systemId = null;

if(locationAbsolute == true)

Unknown macro: { systemId = finalLocation; }

else

Unknown macro: { systemId = servletContext.getRealPath(finalLocation); }

Map parameters = new ValueStackShadowMap(stack);
File directory = new File(systemId.substring(0, systemId.lastIndexOf(File.separator)));
parameters.put("reportDirectory", directory);
parameters.put(JRParameter.REPORT_LOCALE, invocation.getInvocationContext().getLocale());

// put timezone in jasper report parameter
if (timeZone != null) {
timeZone = conditionalParse(timeZone, invocation);
final TimeZone tz = TimeZone.getTimeZone(timeZone);
if (tz != null)

Unknown macro: { // put the report time zone parameters.put(JRParameter.REPORT_TIME_ZONE, tz); }

}

// Add any report parameters from action to param map.
Map reportParams = (Map) stack.findValue(reportParameters);
if (reportParams != null) {
if (LOG.isDebugEnabled())

Unknown macro: { LOG.debug("Found report parameters; adding to parameters..."); }

parameters.putAll(reportParams);
}

byte[] output;
JasperPrint jasperPrint;

// Fill the report and produce a print object
try

Unknown macro: { JasperReport jasperReport = (JasperReport) JRLoader.loadObject(systemId); if (conn == null) jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, stackDataSource); else jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, conn); }

catch (JRException e)

Unknown macro: { LOG.error("Error building report for uri " + systemId, e); throw new ServletException(e.getMessage(), e); }

// Export the print object to the desired output format
try {
if (contentDisposition != null || documentName != null) {
final StringBuffer tmp = new StringBuffer();
tmp.append((contentDisposition == null) ? "inline" : contentDisposition);

if (documentName != null)

Unknown macro: { tmp.append("; filename="); tmp.append(documentName); tmp.append("."); tmp.append(format.toLowerCase()); }

response.setHeader("Content-disposition", tmp.toString());
}

JRExporter exporter;

if (format.equals(FORMAT_PDF))

Unknown macro: { response.setContentType("application/pdf"); exporter = new JRPdfExporter(); }

else if (format.equals(FORMAT_CSV))

Unknown macro: { response.setContentType("text/csv"); exporter = new JRCsvExporter(); }

else if (format.equals(FORMAT_HTML))

Unknown macro: { response.setContentType("text/html"); // IMAGES_MAPS seems to be only supported as "backward compatible" from JasperReports 1.1.0 Map imagesMap = new HashMap(); request.getSession(true).setAttribute("IMAGES_MAP", imagesMap); exporter = new JRHtmlExporter(); exporter.setParameter(JRHtmlExporterParameter.IMAGES_MAP, imagesMap); exporter.setParameter(JRHtmlExporterParameter.IMAGES_URI, request.getContextPath() + imageServletUrl); // Needed to support chart images}

else if (format.equals(FORMAT_XLS))

Unknown macro: { response.setContentType("application/vnd.ms-excel"); exporter = new JRXlsExporter(); }

else if (format.equals(FORMAT_XML))

Unknown macro: { response.setContentType("text/xml"); exporter = new JRXmlExporter(); }

else if (format.equals(FORMAT_RTF))

Unknown macro: { response.setContentType("application/rtf"); exporter = new JRRtfExporter(); }

else

Unknown macro: { throw new ServletException("Unknown report format}

Map exportParams = (Map) stack.findValue(exportParameters);
if (exportParams != null) {
if (LOG.isDebugEnabled())

Unknown macro: { LOG.debug("Found export parameters; adding to exporter parameters..."); }

exporter.getParameters().putAll(exportParams);
}

output = exportReportToBytes(jasperPrint, exporter);
} catch (JRException e)

Unknown macro: { String message = "Error producing " + format + " report for uri " + systemId; LOG.error(message, e); throw new ServletException(e.getMessage(), e); }

response.setContentLength(output.length);

// Will throw ServletException on IOException.
writeReport(response, output);
}

/**

  • Writes report bytes to response output stream.
    *
  • @param response Current response.
  • @param output Report bytes to write.
  • @throws ServletException on stream IOException.
    */
    private void writeReport(HttpServletResponse response, byte[] output) throws ServletException {
    ServletOutputStream outputStream = null;
    try
    Unknown macro: { outputStream = response.getOutputStream(); outputStream.write(output); outputStream.flush(); }
    catch (IOException e)
    Unknown macro: { LOG.error("Error writing report output", e); throw new ServletException(e.getMessage(), e); }
    finally {
    try
    Unknown macro: { if (outputStream != null)
    Unknown macro: { outputStream.close(); }
    }
    catch (IOException e)
    Unknown macro: { LOG.error("Error closing report output stream", e); throw new ServletException(e.getMessage(), e); }
    }
    }

/**

  • Sets up result properties, parsing etc.
    *
  • @param invocation Current invocation.
  • @throws Exception on initialization error.
    */
    private void initializeProperties(ActionInvocation invocation) throws Exception {
    if (dataSource == null && connection == null)
    Unknown macro: { String message = "No dataSource specified..."; LOG.error(message); throw new RuntimeException(message); }
    if (dataSource != null)
    dataSource = conditionalParse(dataSource, invocation);

format = conditionalParse(format, invocation);
if (StringUtils.isEmpty(format))

Unknown macro: { format = FORMAT_PDF; }

if (contentDisposition != null)

Unknown macro: { contentDisposition = conditionalParse(contentDisposition, invocation); }

if (documentName != null)

Unknown macro: { documentName = conditionalParse(documentName, invocation); }

reportParameters = conditionalParse(reportParameters, invocation);
exportParameters = conditionalParse(exportParameters, invocation);
}

/**

  • Run a Jasper report to CSV format and put the results in a byte array
    *
  • @param jasperPrint The Print object to render as CSV
  • @param exporter The exporter to use to export the report
  • @return A CSV formatted report
  • @throws net.sf.jasperreports.engine.JRException
  • If there is a problem running the report
    */
    private byte[] exportReportToBytes(JasperPrint jasperPrint, JRExporter exporter) throws JRException {
    byte[] output;
    ByteArrayOutputStream baos = new ByteArrayOutputStream();

exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, baos);
if (delimiter != null)

Unknown macro: { exporter.setParameter(JRCsvExporterParameter.FIELD_DELIMITER, delimiter); }

exporter.exportReport();

output = baos.toByteArray();

return output;
}

}

  • No labels

1 Comment

  1. Could you register an issue and attach a patch instead posting the code here ? It's very hard to catch it up :/