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