View Javadoc

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