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 }