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