1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.apache.struts2.views.jasperreports;
23
24 import java.io.ByteArrayOutputStream;
25 import java.io.File;
26 import java.io.IOException;
27 import java.util.HashMap;
28 import java.util.Map;
29 import java.util.TimeZone;
30 import java.sql.Connection;
31
32 import javax.servlet.ServletContext;
33 import javax.servlet.ServletException;
34 import javax.servlet.ServletOutputStream;
35 import javax.servlet.http.HttpServletRequest;
36 import javax.servlet.http.HttpServletResponse;
37
38 import net.sf.jasperreports.engine.JRException;
39 import net.sf.jasperreports.engine.JRExporter;
40 import net.sf.jasperreports.engine.JRExporterParameter;
41 import net.sf.jasperreports.engine.JRParameter;
42 import net.sf.jasperreports.engine.JasperFillManager;
43 import net.sf.jasperreports.engine.JasperPrint;
44 import net.sf.jasperreports.engine.JasperReport;
45 import net.sf.jasperreports.engine.export.JRCsvExporter;
46 import net.sf.jasperreports.engine.export.JRCsvExporterParameter;
47 import net.sf.jasperreports.engine.export.JRHtmlExporter;
48 import net.sf.jasperreports.engine.export.JRHtmlExporterParameter;
49 import net.sf.jasperreports.engine.export.JRPdfExporter;
50 import net.sf.jasperreports.engine.export.JRRtfExporter;
51 import net.sf.jasperreports.engine.export.JRXlsExporter;
52 import net.sf.jasperreports.engine.export.JRXmlExporter;
53 import net.sf.jasperreports.engine.util.JRLoader;
54
55 import org.apache.struts2.ServletActionContext;
56 import org.apache.struts2.dispatcher.StrutsResultSupport;
57 import org.apache.commons.lang.xwork.StringUtils;
58
59 import com.opensymphony.xwork2.ActionInvocation;
60 import com.opensymphony.xwork2.util.ValueStack;
61 import com.opensymphony.xwork2.util.logging.Logger;
62 import com.opensymphony.xwork2.util.logging.LoggerFactory;
63
64 /***
65 * <!-- START SNIPPET: description -->
66 * <p/>
67 * Generates a JasperReports report using the specified format or PDF if no
68 * format is specified.
69 * <p/>
70 * <!-- END SNIPPET: description -->
71 * <p />
72 * <b>This result type takes the following parameters:</b>
73 * <p/>
74 * <!-- START SNIPPET: params -->
75 * <p/>
76 * <ul>
77 * <p/>
78 * <li><b>location (default)</b> - the location where the compiled jasper report
79 * definition is (foo.jasper), relative from current URL.</li>
80 * <p/>
81 * <li><b>dataSource (required)</b> - the EL expression used to retrieve the
82 * datasource from the value stack (usually a List).</li>
83 * <p/>
84 * <li><b>parse</b> - true by default. If set to false, the location param will
85 * not be parsed for EL expressions.</li>
86 * <p/>
87 * <li><b>format</b> - the format in which the report should be generated. Valid
88 * values can be found in {@link JasperReportConstants}. If no format is
89 * specified, PDF will be used.</li>
90 * <p/>
91 * <li><b>contentDisposition</b> - disposition (defaults to "inline", values are
92 * typically <i>filename="document.pdf"</i>).</li>
93 * <p/>
94 * <li><b>documentName</b> - name of the document (will generate the http header
95 * <code>Content-disposition = X; filename=X.[format]</code>).</li>
96 * <p/>
97 * <li><b>delimiter</b> - the delimiter used when generating CSV reports. By
98 * default, the character used is ",".</li>
99 * <p/>
100 * <li><b>imageServletUrl</b> - name of the url that, when prefixed with the
101 * context page, can return report images.</li>
102 * <p/>
103 * <li>
104 * <b>reportParameters</b> - (2.1.2+) OGNL expression used to retrieve a map of
105 * report parameters from the value stack. The parameters may be accessed
106 * in the report via the usual JR mechanism and might include data not
107 * part of the dataSource, such as the user name of the report creator, etc.
108 * </li>
109 * <p/>
110 * <li>
111 * <b>exportParameters</b> - (2.1.2+) OGNL expression used to retrieve a map of
112 * JR exporter parameters from the value stack. The export parameters are
113 * used to customize the JR export. For example, a PDF export might enable
114 * encryption and set the user password to a string known to the report creator.
115 * </li>
116 * <p/>
117 * <li>
118 * <b>connection</b> - (2.1.7+) JDBC Connection which can be passed to the
119 * report instead of dataSource
120 * </li>
121 * <p/>
122 * </ul>
123 * <p/>
124 * <p>
125 * This result follows the same rules from {@link StrutsResultSupport}.
126 * Specifically, all parameters will be parsed if the "parse" parameter
127 * is not set to false.
128 * </p>
129 * <!-- END SNIPPET: params -->
130 * <p/>
131 * <b>Example:</b>
132 * <p/>
133 * <pre><!-- START SNIPPET: example1 -->
134 * <result name="success" type="jasper">
135 * <param name="location">foo.jasper</param>
136 * <param name="dataSource">mySource</param>
137 * <param name="format">CSV</param>
138 * </result>
139 * <!-- END SNIPPET: example1 --></pre>
140 * or for pdf
141 * <pre><!-- START SNIPPET: example2 -->
142 * <result name="success" type="jasper">
143 * <param name="location">foo.jasper</param>
144 * <param name="dataSource">mySource</param>
145 * </result>
146 * <!-- END SNIPPET: example2 --></pre>
147 */
148 public class JasperReportsResult extends StrutsResultSupport implements JasperReportConstants {
149
150 private static final long serialVersionUID = -2523174799621182907L;
151
152 private final static Logger LOG = LoggerFactory.getLogger(JasperReportsResult.class);
153
154 protected String dataSource;
155 protected String format;
156 protected String documentName;
157 protected String contentDisposition;
158 protected String delimiter;
159 protected String imageServletUrl = "/images/";
160 protected String timeZone;
161
162 /***
163 * Connection which can be passed to the report
164 * instead od dataSource.
165 */
166 protected String connection;
167
168 /***
169 * Names a report parameters map stack value, allowing
170 * additional report parameters from the action.
171 */
172 protected String reportParameters;
173
174 /***
175 * Names an exporter parameters map stack value,
176 * allowing the use of custom export parameters.
177 */
178 protected String exportParameters;
179
180 /***
181 * Default ctor.
182 */
183 public JasperReportsResult() {
184 super();
185 }
186
187 /***
188 * Default ctor with location.
189 *
190 * @param location Result location.
191 */
192 public JasperReportsResult(String location) {
193 super(location);
194 }
195
196 public String getImageServletUrl() {
197 return imageServletUrl;
198 }
199
200 public void setImageServletUrl(final String imageServletUrl) {
201 this.imageServletUrl = imageServletUrl;
202 }
203
204 public void setDataSource(String dataSource) {
205 this.dataSource = dataSource;
206 }
207
208 public void setFormat(String format) {
209 this.format = format;
210 }
211
212 public void setDocumentName(String documentName) {
213 this.documentName = documentName;
214 }
215
216 public void setContentDisposition(String contentDisposition) {
217 this.contentDisposition = contentDisposition;
218 }
219
220 public void setDelimiter(String delimiter) {
221 this.delimiter = delimiter;
222 }
223
224 /***
225 * set time zone id
226 *
227 * @param timeZone
228 */
229 public void setTimeZone(final String timeZone) {
230 this.timeZone = timeZone;
231 }
232
233 public String getReportParameters() {
234 return reportParameters;
235 }
236
237 public void setReportParameters(String reportParameters) {
238 this.reportParameters = reportParameters;
239 }
240
241 public String getExportParameters() {
242 return exportParameters;
243 }
244
245 public void setExportParameters(String exportParameters) {
246 this.exportParameters = exportParameters;
247 }
248
249 public String getConnection() {
250 return connection;
251 }
252
253 public void setConnection(String connection) {
254 this.connection = connection;
255 }
256
257 protected void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {
258
259 initializeProperties(invocation);
260
261 if (LOG.isDebugEnabled()) {
262 LOG.debug("Creating JasperReport for dataSource = " + dataSource + ", format = " + format);
263 }
264
265 HttpServletRequest request = (HttpServletRequest) invocation.getInvocationContext().get(ServletActionContext.HTTP_REQUEST);
266 HttpServletResponse response = (HttpServletResponse) invocation.getInvocationContext().get(ServletActionContext.HTTP_RESPONSE);
267
268
269
270 if ("contype".equals(request.getHeader("User-Agent"))) {
271 try {
272 response.setContentType("application/pdf");
273 response.setContentLength(0);
274
275 ServletOutputStream outputStream = response.getOutputStream();
276 outputStream.close();
277 } catch (IOException e) {
278 LOG.error("Error writing report output", e);
279 throw new ServletException(e.getMessage(), e);
280 }
281 return;
282 }
283
284
285 ValueStack stack = invocation.getStack();
286 ValueStackDataSource stackDataSource = null;
287
288 Connection conn = (Connection) stack.findValue(connection);
289 if (conn == null)
290 stackDataSource = new ValueStackDataSource(stack, dataSource);
291
292
293
294
295 ServletContext servletContext = (ServletContext) invocation.getInvocationContext().get(ServletActionContext.SERVLET_CONTEXT);
296 String systemId = servletContext.getRealPath(finalLocation);
297 Map parameters = new ValueStackShadowMap(stack);
298 File directory = new File(systemId.substring(0, systemId.lastIndexOf(File.separator)));
299 parameters.put("reportDirectory", directory);
300 parameters.put(JRParameter.REPORT_LOCALE, invocation.getInvocationContext().getLocale());
301
302
303 if (timeZone != null) {
304 timeZone = conditionalParse(timeZone, invocation);
305 final TimeZone tz = TimeZone.getTimeZone(timeZone);
306 if (tz != null) {
307
308 parameters.put(JRParameter.REPORT_TIME_ZONE, tz);
309 }
310 }
311
312
313 Map reportParams = (Map) stack.findValue(reportParameters);
314 if (reportParams != null) {
315 LOG.debug("Found report parameters; adding to parameters...");
316 parameters.putAll(reportParams);
317 }
318
319 byte[] output;
320 JasperPrint jasperPrint;
321
322
323 try {
324 JasperReport jasperReport = (JasperReport) JRLoader.loadObject(systemId);
325 if (conn == null)
326 jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, stackDataSource);
327 else
328 jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, conn);
329 } catch (JRException e) {
330 LOG.error("Error building report for uri " + systemId, e);
331 throw new ServletException(e.getMessage(), e);
332 }
333
334
335 try {
336 if (contentDisposition != null || documentName != null) {
337 final StringBuffer tmp = new StringBuffer();
338 tmp.append((contentDisposition == null) ? "inline" : contentDisposition);
339
340 if (documentName != null) {
341 tmp.append("; filename=");
342 tmp.append(documentName);
343 tmp.append(".");
344 tmp.append(format.toLowerCase());
345 }
346
347 response.setHeader("Content-disposition", tmp.toString());
348 }
349
350 JRExporter exporter;
351
352 if (format.equals(FORMAT_PDF)) {
353 response.setContentType("application/pdf");
354 exporter = new JRPdfExporter();
355 } else if (format.equals(FORMAT_CSV)) {
356 response.setContentType("text/plain");
357 exporter = new JRCsvExporter();
358 } else if (format.equals(FORMAT_HTML)) {
359 response.setContentType("text/html");
360
361
362
363 Map imagesMap = new HashMap();
364 request.getSession(true).setAttribute("IMAGES_MAP", imagesMap);
365
366 exporter = new JRHtmlExporter();
367 exporter.setParameter(JRHtmlExporterParameter.IMAGES_MAP, imagesMap);
368 exporter.setParameter(JRHtmlExporterParameter.IMAGES_URI, request.getContextPath() + imageServletUrl);
369
370
371 exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
372 request.getSession().setAttribute("net.sf.jasperreports.j2ee.jasper_print", jasperPrint);
373 } else if (format.equals(FORMAT_XLS)) {
374 response.setContentType("application/vnd.ms-excel");
375 exporter = new JRXlsExporter();
376 } else if (format.equals(FORMAT_XML)) {
377 response.setContentType("text/xml");
378 exporter = new JRXmlExporter();
379 } else if (format.equals(FORMAT_RTF)) {
380 response.setContentType("application/rtf");
381 exporter = new JRRtfExporter();
382 } else {
383 throw new ServletException("Unknown report format: " + format);
384 }
385
386 Map exportParams = (Map) stack.findValue(exportParameters);
387 if (exportParams != null) {
388 LOG.debug("Found export parameters; adding to exporter parameters...");
389 exporter.getParameters().putAll(exportParams);
390 }
391
392 output = exportReportToBytes(jasperPrint, exporter);
393 } catch (JRException e) {
394 String message = "Error producing " + format + " report for uri " + systemId;
395 LOG.error(message, e);
396 throw new ServletException(e.getMessage(), e);
397 }
398
399 response.setContentLength(output.length);
400
401
402 writeReport(response, output);
403 }
404
405 /***
406 * Writes report bytes to response output stream.
407 *
408 * @param response Current response.
409 * @param output Report bytes to write.
410 * @throws ServletException on stream IOException.
411 */
412 private void writeReport(HttpServletResponse response, byte[] output) throws ServletException {
413 ServletOutputStream outputStream = null;
414 try {
415 outputStream = response.getOutputStream();
416 outputStream.write(output);
417 outputStream.flush();
418 } catch (IOException e) {
419 LOG.error("Error writing report output", e);
420 throw new ServletException(e.getMessage(), e);
421 } finally {
422 try {
423 if (outputStream != null) {
424 outputStream.close();
425 }
426 } catch (IOException e) {
427 LOG.error("Error closing report output stream", e);
428 throw new ServletException(e.getMessage(), e);
429 }
430 }
431 }
432
433 /***
434 * Sets up result properties, parsing etc.
435 *
436 * @param invocation Current invocation.
437 * @throws Exception on initialization error.
438 */
439 private void initializeProperties(ActionInvocation invocation) throws Exception {
440 if (dataSource == null && connection == null) {
441 String message = "No dataSource specified...";
442 LOG.error(message);
443 throw new RuntimeException(message);
444 }
445 if (dataSource != null)
446 dataSource = conditionalParse(dataSource, invocation);
447
448 format = conditionalParse(format, invocation);
449 if (StringUtils.isEmpty(format)) {
450 format = FORMAT_PDF;
451 }
452
453 if (contentDisposition != null) {
454 contentDisposition = conditionalParse(contentDisposition, invocation);
455 }
456
457 if (documentName != null) {
458 documentName = conditionalParse(documentName, invocation);
459 }
460
461 reportParameters = conditionalParse(reportParameters, invocation);
462 exportParameters = conditionalParse(exportParameters, invocation);
463 }
464
465 /***
466 * Run a Jasper report to CSV format and put the results in a byte array
467 *
468 * @param jasperPrint The Print object to render as CSV
469 * @param exporter The exporter to use to export the report
470 * @return A CSV formatted report
471 * @throws net.sf.jasperreports.engine.JRException
472 * If there is a problem running the report
473 */
474 private byte[] exportReportToBytes(JasperPrint jasperPrint, JRExporter exporter) throws JRException {
475 byte[] output;
476 ByteArrayOutputStream baos = new ByteArrayOutputStream();
477
478 exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
479 exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, baos);
480 if (delimiter != null) {
481 exporter.setParameter(JRCsvExporterParameter.FIELD_DELIMITER, delimiter);
482 }
483
484 exporter.exportReport();
485
486 output = baos.toByteArray();
487
488 return output;
489 }
490
491 }