View Javadoc

    1   /*
    2    * $Id: JasperReportsResult.java 508875 2007-02-18 06:02:23Z tschneider $
    3    *
    4    * Licensed to the Apache Software Foundation (ASF) under one
    5    * or more contributor license agreements.  See the NOTICE file
    6    * distributed with this work for additional information
    7    * regarding copyright ownership.  The ASF licenses this file
    8    * to you under the Apache License, Version 2.0 (the
    9    * "License"); you may not use this file except in compliance
    10   * with the License.  You may obtain a copy of the License at
    11   *
    12   *  http://www.apache.org/licenses/LICENSE-2.0
    13   *
    14   * Unless required by applicable law or agreed to in writing,
    15   * software distributed under the License is distributed on an
    16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    17   * KIND, either express or implied.  See the License for the
    18   * specific language governing permissions and limitations
    19   * under the License.
    20   */
    21  package org.apache.struts2.views.jasperreports;
    22
    23  import java.io.ByteArrayOutputStream;
    24  import java.io.File;
    25  import java.io.IOException;
    26  import java.util.HashMap;
    27  import java.util.Map;
    28
    29  import javax.servlet.ServletContext;
    30  import javax.servlet.ServletException;
    31  import javax.servlet.ServletOutputStream;
    32  import javax.servlet.http.HttpServletRequest;
    33  import javax.servlet.http.HttpServletResponse;
    34
    35  import net.sf.jasperreports.engine.JRException;
    36  import net.sf.jasperreports.engine.JRExporter;
    37  import net.sf.jasperreports.engine.JRExporterParameter;
    38  import net.sf.jasperreports.engine.JRParameter;
    39  import net.sf.jasperreports.engine.JasperExportManager;
    40  import net.sf.jasperreports.engine.JasperFillManager;
    41  import net.sf.jasperreports.engine.JasperPrint;
    42  import net.sf.jasperreports.engine.JasperReport;
    43  import net.sf.jasperreports.engine.export.JRCsvExporter;
    44  import net.sf.jasperreports.engine.export.JRCsvExporterParameter;
    45  import net.sf.jasperreports.engine.export.JRHtmlExporter;
    46  import net.sf.jasperreports.engine.export.JRHtmlExporterParameter;
    47  import net.sf.jasperreports.engine.export.JRRtfExporter;
    48  import net.sf.jasperreports.engine.export.JRXlsExporter;
    49  import net.sf.jasperreports.engine.export.JRXmlExporter;
    50  import net.sf.jasperreports.engine.util.JRLoader;
    51
    52  import org.apache.commons.logging.Log;
    53  import org.apache.commons.logging.LogFactory;
    54  import org.apache.struts2.ServletActionContext;
    55  import org.apache.struts2.dispatcher.StrutsResultSupport;
    56
    57  import com.opensymphony.xwork2.ActionInvocation;
    58  import com.opensymphony.xwork2.util.TextUtils;
    59  import com.opensymphony.xwork2.util.ValueStack;
    60
    61  /***
    62   * <!-- START SNIPPET: description -->
    63   *
    64   * Generates a JasperReports report using the specified format or PDF if no
    65   * format is specified.
    66   *
    67   * <!-- END SNIPPET: description -->
    68   * <p />
    69   * <b>This result type takes the following parameters:</b>
    70   *
    71   * <!-- START SNIPPET: params -->
    72   *
    73   * <ul>
    74   *
    75   * <li><b>location (default)</b> - the location where the compiled jasper report
    76   * definition is (foo.jasper), relative from current URL.</li>
    77   *
    78   * <li><b>dataSource (required)</b> - the EL expression used to retrieve the
    79   * datasource from the value stack (usually a List).</li>
    80   *
    81   * <li><b>parse</b> - true by default. If set to false, the location param will
    82   * not be parsed for EL expressions.</li>
    83   *
    84   * <li><b>format</b> - the format in which the report should be generated. Valid
    85   * values can be found in {@link JasperReportConstants}. If no format is
    86   * specified, PDF will be used.</li>
    87   *
    88   * <li><b>contentDisposition</b> - disposition (defaults to "inline", values are
    89   * typically <i>filename="document.pdf"</i>).</li>
    90   *
    91   * <li><b>documentName</b> - name of the document (will generate the http header
    92   * <code>Content-disposition = X; filename=X.[format]</code>).</li>
    93   *
    94   * <li><b>delimiter</b> - the delimiter used when generating CSV reports. By
    95   * default, the character used is ",".</li>
    96   *
    97   * <li><b>imageServletUrl</b> - name of the url that, when prefixed with the
    98   * context page, can return report images.</li>
    99   *
    100  * </ul>
    101  *
    102  * <p>
    103  * This result follows the same rules from {@link StrutsResultSupport}.
    104  * Specifically, all parameters will be parsed if the "parse" parameter is not
    105  * set to false.
    106  * </p>
    107  * <!-- END SNIPPET: params -->
    108  *
    109  * <b>Example:</b>
    110  *
    111  * <pre><!-- START SNIPPET: example1 -->
    112  * &lt;result name="success" type="jasper"&gt;
    113  *   &lt;param name="location"&gt;foo.jasper&lt;/param&gt;
    114  *   &lt;param name="dataSource"&gt;mySource&lt;/param&gt;
    115  *   &lt;param name="format"&gt;CSV&lt;/param&gt;
    116  * &lt;/result&gt;
    117  * <!-- END SNIPPET: example1 --></pre>
    118  * or for pdf
    119  * <pre><!-- START SNIPPET: example2 -->
    120  * &lt;result name="success" type="jasper"&gt;
    121  *   &lt;param name="location"&gt;foo.jasper&lt;/param&gt;
    122  *   &lt;param name="dataSource"&gt;mySource&lt;/param&gt;
    123  * &lt;/result&gt;
    124  * <!-- END SNIPPET: example2 --></pre>
    125  *
    126  */
    127 public class JasperReportsResult extends StrutsResultSupport implements JasperReportConstants {
    128
    129     private static final long serialVersionUID = -2523174799621182907L;
    130
    131
    132     private final static Log LOG = LogFactory.getLog(JasperReportsResult.class);
    133
    134
    135     protected String dataSource;
    136     protected String format;
    137     protected String documentName;
    138     protected String contentDisposition;
    139     protected String delimiter;
    140     protected String imageServletUrl = "/images/";
    141
    142     public JasperReportsResult() {
    143         super();
    144     }
    145
    146     public JasperReportsResult(String location) {
    147         super(location);
    148     }
    149
    150     public String getImageServletUrl() {
    151         return imageServletUrl;
    152     }
    153
    154     public JasperReportsResult setImageServletUrl(final String imageServletUrl) {
    155         this.imageServletUrl = imageServletUrl;
    156         return this;
    157     }
    158
    159     public JasperReportsResult setDataSource(String dataSource) {
    160         this.dataSource = dataSource;
    161         return this;
    162     }
    163
    164     public JasperReportsResult setFormat(String format) {
    165         this.format = format;
    166         return this;
    167     }
    168
    169     public JasperReportsResult setDocumentName(String documentName) {
    170         this.documentName = documentName;
    171         return this;
    172     }
    173
    174     public JasperReportsResult setContentDisposition(String contentDisposition) {
    175         this.contentDisposition = contentDisposition;
    176         return this;
    177     }
    178
    179     public JasperReportsResult setDelimiter(String delimiter) {
    180         this.delimiter = delimiter;
    181         return this;
    182     }
    183
    184     protected void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {
    185         if (this.format == null) {
    186             this.format = FORMAT_PDF;
    187         }
    188
    189         if (dataSource == null) {
    190             String message = "No dataSource specified...";
    191             LOG.error(message);
    192             throw new RuntimeException(message);
    193         }
    194
    195         if (LOG.isDebugEnabled()) {
    196             LOG.debug("Creating JasperReport for dataSource = " + dataSource + ", format = " + this.format);
    197         }
    198
    199         HttpServletRequest request = (HttpServletRequest) invocation.getInvocationContext().get(ServletActionContext.HTTP_REQUEST);
    200         HttpServletResponse response = (HttpServletResponse) invocation.getInvocationContext().get(ServletActionContext.HTTP_RESPONSE);
    201
    202         //construct the data source for the report
    203         ValueStack stack = invocation.getStack();
    204         ValueStackDataSource stackDataSource = new ValueStackDataSource(stack, dataSource);
    205
    206         format = conditionalParse(format, invocation);
    207         dataSource = conditionalParse(dataSource, invocation);
    208
    209         if (contentDisposition != null) {
    210             contentDisposition = conditionalParse(contentDisposition, invocation);
    211         }
    212
    213         if (documentName != null) {
    214             documentName = conditionalParse(documentName, invocation);
    215         }
    216
    217         // (Map) ActionContext.getContext().getSession().get("IMAGES_MAP");
    218         if (!TextUtils.stringSet(format)) {
    219             format = FORMAT_PDF;
    220         }
    221
    222         if (!"contype".equals(request.getHeader("User-Agent"))) {
    223             // Determine the directory that the report file is in and set the reportDirectory parameter
    224             // For WW 2.1.7:
    225             //  ServletContext servletContext = ((ServletConfig) invocation.getInvocationContext().get(ServletActionContext.SERVLET_CONFIG)).getServletContext();
    226             ServletContext servletContext = (ServletContext) invocation.getInvocationContext().get(ServletActionContext.SERVLET_CONTEXT);
    227             String systemId = servletContext.getRealPath(finalLocation);
    228             Map parameters = new ValueStackShadowMap(stack);
    229             File directory = new File(systemId.substring(0, systemId.lastIndexOf(File.separator)));
    230             parameters.put("reportDirectory", directory);
    231             parameters.put(JRParameter.REPORT_LOCALE, invocation.getInvocationContext().getLocale());
    232
    233             byte[] output;
    234             JasperPrint jasperPrint;
    235
    236             // Fill the report and produce a print object
    237             try {
    238                 JasperReport jasperReport = (JasperReport) JRLoader.loadObject(systemId);
    239
    240                 jasperPrint =
    241                         JasperFillManager.fillReport(jasperReport,
    242                                 parameters,
    243                                 stackDataSource);
    244             } catch (JRException e) {
    245                 LOG.error("Error building report for uri " + systemId, e);
    246                 throw new ServletException(e.getMessage(), e);
    247             }
    248
    249             // Export the print object to the desired output format
    250             try {
    251                 if (contentDisposition != null || documentName != null) {
    252                     final StringBuffer tmp = new StringBuffer();
    253                     tmp.append((contentDisposition == null) ? "inline" : contentDisposition);
    254
    255                     if (documentName != null) {
    256                         tmp.append("; filename=");
    257                         tmp.append(documentName);
    258                         tmp.append(".");
    259                         tmp.append(format.toLowerCase());
    260                     }
    261
    262                     response.setHeader("Content-disposition", tmp.toString());
    263                 }
    264
    265                 if (format.equals(FORMAT_PDF)) {
    266                     response.setContentType("application/pdf");
    267
    268                     // response.setHeader("Content-disposition", "inline; filename=report.pdf");
    269                     output = JasperExportManager.exportReportToPdf(jasperPrint);
    270                 } else {
    271                     JRExporter exporter;
    272
    273                     if (format.equals(FORMAT_CSV)) {
    274                         response.setContentType("text/plain");
    275                         exporter = new JRCsvExporter();
    276                     } else if (format.equals(FORMAT_HTML)) {
    277                         response.setContentType("text/html");
    278
    279                         // IMAGES_MAPS seems to be only supported as "backward compatible" from JasperReports 1.1.0
    280
    281                         Map imagesMap = new HashMap();
    282
    283                         request.getSession(true).setAttribute("IMAGES_MAP", imagesMap);
    284                         exporter = new JRHtmlExporter();
    285                         exporter.setParameter(JRHtmlExporterParameter.IMAGES_MAP, imagesMap);
    286                         exporter.setParameter(JRHtmlExporterParameter.IMAGES_URI, request.getContextPath() + imageServletUrl);
    287                         // Needed to support chart images:
    288                         exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
    289                         request.getSession().setAttribute("net.sf.jasperreports.j2ee.jasper_print", jasperPrint);
    290
    291                     } else if (format.equals(FORMAT_XLS)) {
    292                         response.setContentType("application/vnd.ms-excel");
    293                         exporter = new JRXlsExporter();
    294                     } else if (format.equals(FORMAT_XML)) {
    295                         response.setContentType("text/xml");
    296                         exporter = new JRXmlExporter();
    297                     } else if (format.equals(FORMAT_RTF)) {
    298                         response.setContentType("application/rtf");
    299                         exporter = new JRRtfExporter();
    300                     } else {
    301                         throw new ServletException("Unknown report format: " + format);
    302                     }
    303
    304                     output = exportReportToBytes(jasperPrint, exporter);
    305                 }
    306             } catch (JRException e) {
    307                 String message = "Error producing " + format + " report for uri " + systemId;
    308                 LOG.error(message, e);
    309                 throw new ServletException(e.getMessage(), e);
    310             }
    311
    312             response.setContentLength(output.length);
    313
    314             ServletOutputStream ouputStream;
    315
    316             try {
    317                 ouputStream = response.getOutputStream();
    318                 ouputStream.write(output);
    319                 ouputStream.flush();
    320                 ouputStream.close();
    321             } catch (IOException e) {
    322                 LOG.error("Error writing report output", e);
    323                 throw new ServletException(e.getMessage(), e);
    324             }
    325         } else {
    326             // Code to handle "contype" request from IE
    327             try {
    328                 ServletOutputStream outputStream;
    329                 response.setContentType("application/pdf");
    330                 response.setContentLength(0);
    331                 outputStream = response.getOutputStream();
    332                 outputStream.close();
    333             } catch (IOException e) {
    334                 LOG.error("Error writing report output", e);
    335                 throw new ServletException(e.getMessage(), e);
    336             }
    337         }
    338     }
    339
    340     /***
    341      * Run a Jasper report to CSV format and put the results in a byte array
    342      *
    343      * @param jasperPrint The Print object to render as CSV
    344      * @param exporter    The exporter to use to export the report
    345      * @return A CSV formatted report
    346      * @throws net.sf.jasperreports.engine.JRException
    347      *          If there is a problem running the report
    348      */
    349     private byte[] exportReportToBytes(JasperPrint jasperPrint, JRExporter exporter) throws JRException {
    350         byte[] output;
    351         ByteArrayOutputStream baos = new ByteArrayOutputStream();
    352
    353         exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
    354         exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, baos);
    355         if (delimiter != null) {
    356             exporter.setParameter(JRCsvExporterParameter.FIELD_DELIMITER, delimiter);
    357         }
    358
    359         exporter.exportReport();
    360
    361         output = baos.toByteArray();
    362
    363         return output;
    364     }
    365 }