View Javadoc

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