001: /*
002: * Copyright 2006 Pentaho Corporation. All rights reserved.
003: * This software was developed by Pentaho Corporation and is provided under the terms
004: * of the Mozilla Public License, Version 1.1, or any later version. You may not use
005: * this file except in compliance with the license. If you need a copy of the license,
006: * please go to http://www.mozilla.org/MPL/MPL-1.1.txt. The Original Code is the Pentaho
007: * BI Platform. The Initial Developer is Pentaho Corporation.
008: *
009: * Software distributed under the Mozilla Public License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to
011: * the license for the specific language governing your rights and limitations.
012: */
013: package org.pentaho.plugin.jasperreports;
014:
015: import java.io.File;
016: import java.io.OutputStream;
017: import java.sql.Connection;
018: import java.sql.DriverManager;
019: import java.sql.SQLException;
020: import java.util.ArrayList;
021: import java.util.HashMap;
022: import java.util.HashSet;
023: import java.util.Iterator;
024: import java.util.List;
025: import java.util.Map;
026:
027: import javax.naming.NamingException;
028: import javax.sql.DataSource;
029:
030: import net.sf.jasperreports.engine.JRException;
031: import net.sf.jasperreports.engine.JRExporter;
032: import net.sf.jasperreports.engine.JRExporterParameter;
033: import net.sf.jasperreports.engine.JRParameter;
034: import net.sf.jasperreports.engine.JasperCompileManager;
035: import net.sf.jasperreports.engine.JasperFillManager;
036: import net.sf.jasperreports.engine.JasperPrint;
037: import net.sf.jasperreports.engine.JasperReport;
038: import net.sf.jasperreports.engine.export.JRCsvExporter;
039: import net.sf.jasperreports.engine.export.JRHtmlExporter;
040: import net.sf.jasperreports.engine.export.JRHtmlExporterParameter;
041: import net.sf.jasperreports.engine.export.JRPdfExporter;
042: import net.sf.jasperreports.engine.export.JRXlsExporter;
043: import net.sf.jasperreports.engine.export.JRXlsExporterParameter;
044: import net.sf.jasperreports.engine.export.JRXmlExporter;
045: import net.sf.jasperreports.engine.util.JRLoader;
046:
047: import org.apache.commons.logging.Log;
048: import org.apache.commons.logging.LogFactory;
049: import org.pentaho.actionsequence.dom.ActionInput;
050: import org.pentaho.actionsequence.dom.ActionResource;
051: import org.pentaho.actionsequence.dom.ActionSequenceResource;
052: import org.pentaho.actionsequence.dom.IActionInputValueProvider;
053: import org.pentaho.actionsequence.dom.actions.JasperReportAction;
054: import org.pentaho.core.repository.IContentItem;
055: import org.pentaho.core.runtime.IActionParameter;
056: import org.pentaho.core.solution.IActionResource;
057: import org.pentaho.core.system.PentahoSystem;
058: import org.pentaho.core.util.DatasourceHelper;
059: import org.pentaho.messages.Messages;
060: import org.pentaho.plugin.ComponentBase;
061:
062: /**
063: * @author James Dixon and Barry Klawans
064: *
065: * JasperReports runner for Pentaho.
066: *
067: * This class implements a Pentaho Component that runs JasperReports. It
068: * includes full support for parameterization and output in PDF and HTML.
069: *
070: * This is a Beta version, with a number of open issues. 1) Data sources must be
071: * specified in the config file for EACH report. There should be a default
072: * source in the jasper reports configuration. 2) Support for JNDI defined data
073: * source has not been added yet. 3) Images are written to the Pentaho temp
074: * directory and never cleaned up. They should probably be moved into Session
075: * related storage. 4) Support should be added so a Filled report can be
076: * persisited into the Pentaho repository and exported repeatedly.
077: *
078: * @author Radek Maciaszek <radek@m3.net>
079: *
080: * Added handling multiple reports in batch mode which allow to create excel reports with
081: * multiple worksheets
082: */
083: public class JasperReportsComponent extends ComponentBase {
084:
085: private static final String REMOVE_EMPTY_ROWS = "remove-empty-rows"; //$NON-NLS-1$
086:
087: private static final String IMAGE_URL = "image-url"; //$NON-NLS-1$
088:
089: private static final String IMAGE_DIR = "image-dir"; //$NON-NLS-1$
090:
091: private static final String TEXT_HTML = "text/html"; //$NON-NLS-1$
092: private static final String CONFIG_XML = "jasperreports/jasperreports-conf.xml"; //$NON-NLS-1$
093: private static final String HTML = "html"; //$NON-NLS-1$
094: private static final String PDF = "pdf"; //$NON-NLS-1$
095: private static final String XML = "xml"; //$NON-NLS-1$
096: private static final String XLS = "xls"; //$NON-NLS-1$
097: private static final String CSV = "csv"; //$NON-NLS-1$
098: private static final String NEED_TO_PROMPT = "NeedToPrompt"; //$NON-NLS-1$
099: private static final String RETURN_IMMEDIATELY = "ReturnImmediately"; //$NON-NLS-1$
100:
101: private static final long serialVersionUID = -4422766007912720969L;
102:
103: /**
104: * The extension of the JasperReports reports to run. Currently only designs
105: * saved as an XML file are supported, not searilized JasperDesign objects.
106: */
107: public final static String JASPER_REPORTS_DESIGN_EXTENSION = ".jrxml"; //$NON-NLS-1$
108:
109: private static Log logger = LogFactory
110: .getLog(JasperReportsComponent.class);
111:
112: /**
113: * The extension of a compiled JasperReports file.
114: */
115: public final static String COMPILED_JASPER_REPORTS_EXTENSION = ".jasper"; //$NON-NLS-1$
116:
117: public Log getLogger() {
118: return logger;
119: }
120:
121: /**
122: * Validates the settings in the jasper reports configuration file.
123: * <p>
124: * Currently the mandatory settings are imageHandling/imageUrl and
125: * imageHandling/imageDir, which specify where to store images generated by
126: * the report (ie charts) and how to access the images inside an HTML page.
127: */
128: protected boolean validateSystemSettings() {
129: // make sure that the system settings are valid.
130: // In production this will only be called when they have changed, for
131: // development purposes we are calling it every time
132:
133: // get and validate system settings
134: String imageUrl = PentahoSystem.getSystemSetting(CONFIG_XML,
135: "jasperreports/imageHandling/imageUrl", null); //$NON-NLS-1$ //$NON-NLS-2$
136: String imageDir = PentahoSystem.getSystemSetting(CONFIG_XML,
137: "jasperreports/imageHandling/imageDir", null); //$NON-NLS-1$ //$NON-NLS-2$
138: String removeEmptyRows = PentahoSystem
139: .getSystemSetting(
140: CONFIG_XML,
141: "jasperreports/htmlExportOptions/removeEmptySpaceBetweenRows", "false"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
142:
143: if (debug) {
144: debug(Messages.getString("JasperReport.DEBUG_IMAGE_URL") + imageUrl); //$NON-NLS-1$
145: debug(Messages
146: .getString("JasperReport.DEBUG_IMAGE_DIRECTORY") + imageDir); //$NON-NLS-1$
147: debug(Messages
148: .getString("JasperReport.DEBUG_REMOVE_EMPTRY_ROWS") + removeEmptyRows); //$NON-NLS-1$
149: }
150:
151: if (imageUrl == null) {
152: error(Messages
153: .getErrorString("JasperReport.ERROR_0001_IMAGE_URL_NOT_DEFINED")); //$NON-NLS-1$
154: return false;
155: }
156: if (imageDir == null) {
157: error(Messages
158: .getErrorString("JasperReport.ERROR_0002_IMAGE_DIRECTORY_INVALID")); //$NON-NLS-1$
159: return false;
160: }
161:
162: saveSetting(IMAGE_URL, imageUrl);
163: saveSetting(IMAGE_DIR, imageDir);
164: saveSetting(REMOVE_EMPTY_ROWS, removeEmptyRows);
165:
166: // set a property for these, they will be cached and be available at
167: // execution time
168: return true;
169: }
170:
171: /**
172: * NOTE: The comments from Pentaho state that in production this will not be
173: * called during execution. If so, this info needs to be moved into the
174: * execute path as well.
175: */
176: public boolean validateAction() {
177: // In production this will only be called during validation and publish,
178: // it will not be called before report executions
179: // for now it is called before every execution
180: boolean actionValidated = true;
181: JasperReportAction reportAction = (JasperReportAction) getActionDefinition();
182:
183: // get report connection setting
184: if (getActionDefinition() instanceof JasperReportAction) {
185:
186: // Validate settings are passed. Treat the password as optional.
187: if (reportAction.getJndi() == IActionInputValueProvider.NULL_INPUT) {
188: if (reportAction.getDriver() != IActionInputValueProvider.NULL_INPUT) {
189: error(Messages
190: .getErrorString("JasperReport.ERROR_0003_JDBC_DRIVER_NOT_SPECIFIED")); //$NON-NLS-1$
191: actionValidated = false;
192: }
193: if (actionValidated
194: && reportAction.getConnection() == IActionInputValueProvider.NULL_INPUT) {
195: error(Messages
196: .getErrorString("JasperReport.ERROR_0004_JDBC_CONNECTION_NOT_SPECIFIED")); //$NON-NLS-1$
197: actionValidated = false;
198: }
199: if (actionValidated
200: && reportAction.getUserId() == IActionInputValueProvider.NULL_INPUT) {
201: error(Messages
202: .getErrorString("JasperReport.ERROR_0005_JDBC_USER_NOT_SPECIFIED")); //$NON-NLS-1$
203: actionValidated = false;
204: }
205: }
206: // check the inputs, we cannot reply on input values during validation
207: if (actionValidated
208: && reportAction.getOutputType() == IActionInputValueProvider.NULL_INPUT) { //$NON-NLS-1$
209: error(Messages
210: .getErrorString("JasperReport.ERROR_0006_OUTPUT_TYPE_NOT_SPECIFIED")); //$NON-NLS-1$
211: actionValidated = false;
212: }
213:
214: // check the resources
215: if (actionValidated
216: && reportAction.getReportDefinition() == null) {
217: error(Messages
218: .getErrorString("JasperReport.ERROR_0007_REPORT_DEFINITION_NOT_SPECIFIED")); //$NON-NLS-1$
219: actionValidated = false;
220: }
221: } else {
222: actionValidated = false;
223: error(Messages
224: .getErrorString(
225: "ComponentBase.ERROR_0001_UNKNOWN_ACTION_TYPE", getActionDefinition().getElement().asXML())); //$NON-NLS-1$
226: }
227: return actionValidated;
228: }
229:
230: /**
231: * Creates and initializes the appropriate exporter for producing output.
232: * All channel specific export options will be set for the exporter.
233: *
234: * @param outputType
235: * the channel (pdf or html) to use when exporting
236: * @param reportName
237: * used to create a unique name for the directory to store images
238: * in. Should be something unique to the invocation, such as the
239: * session id, but I don't know how to get that from the Pentaho
240: * API yet.
241: *
242: * @return the exporter to use, or <code>null</code> if the output type is
243: * not valid.
244: *
245: * TODO: replace reportName with something unique, like session id.
246: */
247: private JRExporter getExporter(String outputType, String reportName) {
248: JRExporter exporter = null;
249: if (HTML.equals(outputType)) { //$NON-NLS-1$
250: String removeEmptyRows = getStringSetting("removeEmptyRows"); //$NON-NLS-1$
251: exporter = new JRHtmlExporter();
252: exporter.setParameter(
253: JRHtmlExporterParameter.IS_USING_IMAGES_TO_ALIGN,
254: new Boolean(false));
255: if (removeEmptyRows != null
256: && "true".equalsIgnoreCase(removeEmptyRows)) { //$NON-NLS-1$
257: exporter
258: .setParameter(
259: JRHtmlExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_ROWS,
260: new Boolean(true));
261: }
262: String imageUrl = PentahoSystem.getApplicationContext()
263: .getBaseUrl()
264: + getStringSetting(IMAGE_URL);
265: String imagePath = PentahoSystem.getApplicationContext()
266: .getSolutionPath(getStringSetting(IMAGE_DIR));
267: if (!imagePath.endsWith(File.separator))
268: imagePath = imagePath + File.separator;
269: imagePath = imagePath + reportName + File.separator;
270: File imageDir = new File(imagePath);
271: imageDir.mkdirs();
272: exporter.setParameter(JRHtmlExporterParameter.IMAGES_DIR,
273: imageDir);
274: // exporter.setParameter(JRHtmlExporter.IMAGES_URI, imageUrl +
275: // reportName ); //$NON-NLS-1$
276: exporter.setParameter(JRHtmlExporterParameter.IMAGES_URI,
277: imageUrl + reportName + "/"); //$NON-NLS-1$
278: exporter.setParameter(
279: JRHtmlExporterParameter.IS_OUTPUT_IMAGES_TO_DIR,
280: new Boolean(true));
281: if (debug)
282: debug(Messages
283: .getString(
284: "JasperReport.DEBUG_IMAGE_DIRECTORY", imagePath)); //$NON-NLS-1$
285: } else if (PDF.equals(outputType)) { //$NON-NLS-1$
286: exporter = new JRPdfExporter();
287: } else if (XLS.equals(outputType)) { //$NON-NLS-1$
288: exporter = new JRXlsExporter();
289: // Some cleaning in order to make excel reports look better
290: exporter.setParameter(
291: JRXlsExporterParameter.IS_AUTO_DETECT_CELL_TYPE,
292: new Boolean(true));
293: exporter
294: .setParameter(
295: JRXlsExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_ROWS,
296: new Boolean(true));
297: exporter.setParameter(
298: JRXlsExporterParameter.IS_WHITE_PAGE_BACKGROUND,
299: new Boolean(true));
300: exporter.setParameter(
301: JRXlsExporterParameter.IS_ONE_PAGE_PER_SHEET,
302: Boolean.FALSE);
303: } else if (CSV.equals(outputType)) { //$NON-NLS-1$
304: exporter = new JRCsvExporter();
305: } else if (XML.equals(outputType)) { //$NON-NLS-1$
306: exporter = new JRXmlExporter();
307: }
308: return exporter;
309: }
310:
311: /**
312: * Runs a report.
313: */
314: public boolean executeAction() {
315: JasperReportAction reportAction = (JasperReportAction) getActionDefinition();
316:
317: // perform runtime validation of the output type
318: String reportOutputType = reportAction.getOutputType()
319: .getStringValue();
320: if (debug)
321: debug(Messages.getString(
322: "JasperReport.DEBUG_OUTPUT_TYPE", reportOutputType)); //$NON-NLS-1$
323:
324: String mimeType = getMimeType(reportOutputType);
325: if (mimeType == null) {
326: error(Messages
327: .getErrorString("JasperReport.ERROR_0011_OUTPUT_TYPE_INVALID")); //$NON-NLS-1$
328: return false;
329: }
330:
331: String extension = "." + reportOutputType; //$NON-NLS-1$
332:
333: HashSet compiledReportPaths = new HashSet();
334: JasperReport jrreport = null;
335: // Store parameters for all reports
336: HashMap allReportParameters = new HashMap();
337:
338: ActionResource reportDefinition = reportAction
339: .getReportDefinition();
340: if (reportDefinition != null) {
341: IActionResource resource = getResource(reportDefinition
342: .getName());
343: HashMap returnImmediately = new HashMap();
344: processCurrentReport(resource, compiledReportPaths,
345: jrreport, allReportParameters, returnImmediately);
346:
347: if (!returnImmediately.isEmpty()
348: && returnImmediately
349: .containsKey(RETURN_IMMEDIATELY)) {
350: return true;
351: }
352:
353: } else {
354: // This else statement is here to support old action sequence functionality.
355: ActionSequenceResource[] actionSequenceResources = reportAction
356: .getDocument().getResources();
357: for (int i = 0; i < actionSequenceResources.length; i++) {
358: IActionResource resource = getResource(actionSequenceResources[i]
359: .getName());
360: HashMap returnImmediately = new HashMap();
361: // Compile every report
362: processCurrentReport(resource, compiledReportPaths,
363: jrreport, allReportParameters,
364: returnImmediately);
365:
366: if (!returnImmediately.isEmpty()
367: && returnImmediately
368: .containsKey(RETURN_IMMEDIATELY)) {
369: return true;
370: }
371: }
372: }
373:
374: // Try to get the output from the action-sequence document.
375: // execute the report here...
376: IContentItem contentItem = null;
377: OutputStream outputStream = getOutputStream(mimeType,
378: extension, contentItem);
379: if (outputStream == null) {
380: error(Messages
381: .getErrorString("JasperReport.ERROR_0013_OUTPUT_STREAM_INVALID")); //$NON-NLS-1$
382: return false;
383: }
384:
385: return exportReport(compiledReportPaths, allReportParameters,
386: reportOutputType, outputStream, contentItem);
387: }
388:
389: /*
390: * This function does the actual meat work. This method was created to reduce redundancy
391: * in code, that was a part of supporting the new way and old way.
392: * NOTE: The input params compiledReportPaths, jrreport and allReportParameters are being updated in the given method.
393: * They are in essence being used as inout variables / referene vars.
394: */
395: private boolean processCurrentReport(IActionResource resource,
396: HashSet compiledReportPaths, JasperReport jrreport,
397: HashMap allReportParameters, HashMap returnImmediately) {
398: boolean continueProcessing = true;
399: String reportDefinitionPath = getReportDefinitionPath(resource);
400: String compiledReportPath = "";
401:
402: if (reportDefinitionPath == null) {
403: error(Messages
404: .getErrorString("JasperReport.ERROR_0008_REPORT_DEFINITION_UNREADABLE")); //$NON-NLS-1$
405: continueProcessing = false;
406: }
407:
408: if (continueProcessing) {
409: compiledReportPath = getCompiledReportPath(reportDefinitionPath);
410: if (null == compiledReportPath) {
411: continueProcessing = false;
412: }
413: }
414:
415: if (continueProcessing) {
416: compiledReportPaths.add(compiledReportPath);
417: continueProcessing = didReportCompile(reportDefinitionPath,
418: compiledReportPath);
419: }
420:
421: if (continueProcessing) {
422: jrreport = loadJasperReport(compiledReportPath);
423: if (null == jrreport) {
424: continueProcessing = false;
425: }
426: }
427:
428: if (continueProcessing) {
429: // We have a compiled reports, ready to run.
430: JRParameter[] jrparams = jrreport.getParameters();
431:
432: if (debug)
433: debug(Messages
434: .getString(
435: "JasperReport.DEBUG_LOADED_DESIGN", Integer.toString(jrparams.length))); //$NON-NLS-1$
436:
437: HashMap needToPrompt = new HashMap();
438: Map reportParameters = getReportParameters(jrparams,
439: needToPrompt);
440: allReportParameters.put(compiledReportPath,
441: reportParameters);
442: // If we have parameters to prompt for, have Pentaho create the
443: // parameter
444: // page for us.
445: if (!needToPrompt.isEmpty()
446: && needToPrompt.containsKey(NEED_TO_PROMPT)) {
447: // make sure the type parameter comes back to us...
448: // context.createFeedbackParameter( "type", "type",
449: // reportOutputType, false ); //$NON-NLS-1$ //$NON-NLS-1$
450: // //$NON-NLS-2$
451: // Isn't the parameter page always HTML?
452: setFeedbackMimeType("text/html"); //$NON-NLS-1$
453: /*
454: * Creation of the key RETURN IMMEDIATELY is important here.
455: * We would like to return because we need to prompt the
456: * user for input at this point.
457: */
458: returnImmediately.put(RETURN_IMMEDIATELY, null);
459: } else if (getRuntimeContext().isPromptPending()) {
460: returnImmediately.put(RETURN_IMMEDIATELY, null);
461: }
462: }
463:
464: return continueProcessing;
465: }
466:
467: /*
468: * Simply exports the report once we have teh all the report parameters, the location
469: * of the compiled report.
470: */
471: private boolean exportReport(HashSet compiledReportPaths,
472: Map allReportParameters, String reportOutputType,
473: OutputStream outputStream, IContentItem contentItem) {
474: Connection conn = getConnection();
475: if (conn == null)
476: return false;
477:
478: try {
479: String reportBaseName = new String();
480: List jasperPrintList = new ArrayList();
481:
482: Iterator compiledReportPathIterator = compiledReportPaths
483: .iterator();
484:
485: while (compiledReportPathIterator.hasNext()) {
486: String compiledReportPath = (String) compiledReportPathIterator
487: .next();
488:
489: // Fill reports
490: // TODO: Read reportParameters from HashMap
491: Map reportParameters = (Map) allReportParameters
492: .get(compiledReportPath);
493: JasperPrint jrprint = JasperFillManager.fillReport(
494: compiledReportPath, reportParameters, conn);
495: //jasperPrintList.add(JRLoader.loadObject(compiledReportPath));
496: jasperPrintList.add(jrprint);
497:
498: // Get a configure exporter for the desired output format.
499: String lastReportBaseName = compiledReportPath;
500: int extensionIdx = lastReportBaseName.lastIndexOf("."); //$NON-NLS-1$
501: if (extensionIdx > 0)
502: lastReportBaseName = lastReportBaseName.substring(
503: 0, extensionIdx);
504:
505: reportBaseName = reportBaseName
506: .concat(lastReportBaseName);
507: }
508:
509: JRExporter exporter = getExporter(reportOutputType,
510: reportBaseName);
511:
512: // Reverse jasperPrintList order so first resource will appear as first report
513: List jasperPrintListReversed = new ArrayList();
514: for (int i = jasperPrintList.size() - 1; i >= 0; i--) {
515: jasperPrintListReversed.add(jasperPrintList.get(i));
516: }
517:
518: // Set the filled report and output stream.
519: exporter.setParameter(
520: JRExporterParameter.JASPER_PRINT_LIST,
521: jasperPrintListReversed);
522: exporter.setParameter(JRExporterParameter.OUTPUT_STREAM,
523: outputStream);
524:
525: // Go!
526: exporter.exportReport();
527: } catch (JRException jre) {
528: error(
529: Messages
530: .getErrorString("JasperReport.ERROR_0014_REPORT_EXECUTION_FAILED"), jre); //$NON-NLS-1$
531: return false;
532: } finally {
533: try {
534: conn.close();
535: if (contentItem != null) {
536: contentItem.closeOutputStream();
537: }
538: } catch (SQLException ignored) {
539: }
540: }
541: return true;
542: }
543:
544: /*
545: * Gets all the report parameters required for the report.
546: */
547: private Map getReportParameters(JRParameter[] jrparams,
548: HashMap needToPrompt) {
549:
550: Map reportParameters = new HashMap();
551: /*
552: * Process all the parameters in the report.
553: * If the param is system defined then ignore it.
554: * Else check if the parameter value is defined in the input
555: * -- If so the get the param value and update the report params hash map.
556: * If not then prompt for user input for the given parameter.
557: */
558: // TODO: Handle non-String type parameters
559: for (int i = 0; i < jrparams.length; i++) {
560: JRParameter param = jrparams[i];
561: if (param.isSystemDefined())
562: continue;
563:
564: String parameterName = param.getName();
565: String parameterValue = null;
566: ActionInput actionInput = getActionDefinition()
567: .getInputParam(parameterName);
568: if (actionInput != null) {
569: parameterValue = actionInput.getStringValue();
570: }
571: if (parameterValue != null && parameterValue.length() != 0) {
572: // give the parameter value to the report engine...
573: if (debug)
574: debug(Messages
575: .getString(
576: "JasperReport.DEBUG_ADDING_PARAMETER", parameterName, parameterValue)); //$NON-NLS-1$
577:
578: reportParameters.put(parameterName, parameterValue);
579: }
580: // Check for unspecified parameters that need to be specified
581: else if (param.isForPrompting()) {
582: if (debug)
583: debug(Messages
584: .getString(
585: "JasperReport.DEBUG_PARAMETER_NEEDED", parameterName)); //$NON-NLS-1$
586: // see if we can prompt for this...
587: if (feedbackAllowed()) {
588:
589: IActionParameter paramParameter = getInputParameter(parameterName);
590: if (paramParameter.getPromptStatus() != IActionParameter.PROMPT_PENDING) {
591: String displayName = param.getDescription();
592: if (displayName == null
593: || displayName.trim().length() == 0)
594: displayName = parameterName;
595: String defaultValue = ""; //$NON-NLS-1$
596: createFeedbackParameter(parameterName,
597: displayName, "", defaultValue, true); //$NON-NLS-1$
598: }
599: needToPrompt.put(NEED_TO_PROMPT, Boolean.TRUE);
600: }
601: }
602: }
603:
604: return reportParameters;
605: }
606:
607: /*
608: * Load the current jasper report defined by the compiled report path.
609: */
610: private JasperReport loadJasperReport(String compiledReportPath) {
611: if (debug)
612: debug(Messages
613: .getString("JasperReport.DEBUG_LOADING_REPORT_DESIGN")); //$NON-NLS-1$
614:
615: JasperReport jrreport = null;
616: try {
617: jrreport = (JasperReport) JRLoader
618: .loadObject(compiledReportPath);
619: } catch (JRException jre) {
620: jrreport = null;
621: error(
622: Messages
623: .getErrorString(
624: "JasperReport.ERROR_0012_REPORT_DESIGN_NO_LOADABLE", compiledReportPath), jre); //$NON-NLS-1$
625: }
626:
627: return jrreport;
628: }
629:
630: /*
631: * Get the location of the compiled report.
632: */
633: private String getCompiledReportPath(String reportDefinitionPath) {
634: String compiledReportPath = null;
635:
636: if (debug)
637: debug(Messages
638: .getString(
639: "Getting compiled report path.", reportDefinitionPath)); //$NON-NLS-1$
640:
641: // See if we have a compiled report or a report definition. If its a
642: // definition, see if a compiled version exists, and if not, compile it.
643: // get the base name of the report.
644: // Parse the path and get the name of the report.
645: File sourceFile = new File(reportDefinitionPath);
646: String reportBaseName = sourceFile.getName();
647: int extensionIdx = reportBaseName.lastIndexOf("."); //$NON-NLS-1$
648: if (extensionIdx > 0)
649: reportBaseName = reportBaseName.substring(0, extensionIdx);
650:
651: // If we are handed a .jasper file, just use it as the compiled report
652: // definition.
653: if (reportDefinitionPath
654: .endsWith(COMPILED_JASPER_REPORTS_EXTENSION)) {
655: compiledReportPath = reportDefinitionPath;
656: } else {
657: // Assume its a .jrxml
658: if (!sourceFile.exists()) {
659: compiledReportPath = null;
660: error(Messages
661: .getErrorString(
662: "JasperReport.ERROR_0009_REPORT_DEFINITION_MISSING", reportDefinitionPath)); //$NON-NLS-1$
663: } else {
664: if (debug)
665: debug(Messages
666: .getString("JasperReport.DEBUG_REPORT_FILE_FOUND")); //$NON-NLS-1$
667: // Get the directory where the report source is, and compile to
668: // same directory
669: String reportDirectory = sourceFile.getParent();
670: if (!reportDirectory.endsWith(File.separator)) {
671: reportDirectory = reportDirectory + File.separator;
672: }
673: compiledReportPath = reportDirectory + reportBaseName
674: + COMPILED_JASPER_REPORTS_EXTENSION;
675: }
676: }
677: return compiledReportPath;
678: }
679:
680: /*
681: * Compile the report and see if the report compiles successfully.
682: */
683: private boolean didReportCompile(String reportDefinitionPath,
684: String compiledReportPath) {
685: boolean reportCompiled = true;
686:
687: if (debug) {
688: debug(Messages
689: .getString(
690: "JasperReport.DEBUG_RUNNING_REPORT", reportDefinitionPath)); //$NON-NLS-1$
691: debug(Messages
692: .getString(
693: "JasperReport.DEBUG_COMPILED_REPORT_LOCATION", compiledReportPath)); //$NON-NLS-1$
694: }
695:
696: // make sure the report is compiled
697: File compiledReportFile = new File(compiledReportPath);
698: File sourceFile = new File(reportDefinitionPath);
699:
700: // We compile if the compiled file doesn't exist, or the source is
701: // newer
702: if (!compiledReportFile.exists()
703: || sourceFile.lastModified() > compiledReportFile
704: .lastModified()) {
705: if (debug)
706: debug(Messages
707: .getString("JasperReport.DEBUG_COMPILING_REPORT")); //$NON-NLS-1$
708: // We are currently ignoring any error conditions with compiled
709: // files
710: // that exist but aren't readable.
711:
712: // Use the jdt compiler
713: System
714: .setProperty(
715: "jasper.reports.compiler.class", "net.sf.jasperreports.engine.design.JRJdtCompiler"); //$NON-NLS-1$ //$NON-NLS-2$
716:
717: // Compile the report design
718: try {
719: JasperCompileManager.compileReportToFile(
720: reportDefinitionPath, compiledReportPath);
721: } catch (JRException jre) {
722: error(
723: Messages
724: .getErrorString(
725: "JasperReport.ERROR_0010_UNABLE_TO_COMPILE", reportDefinitionPath, compiledReportPath), jre); //$NON-NLS-1$
726: reportCompiled = false;
727: }
728:
729: if (debug && reportCompiled)
730: debug(Messages
731: .getString("JasperReport.DEBUG_COMPILED_OK")); //$NON-NLS-1$
732: }
733:
734: return reportCompiled;
735: }
736:
737: /*
738: *
739: */
740: private String getReportDefinitionPath(IActionResource resource) {
741: String reportDefinitionPath = null;
742:
743: if (resource.getSourceType() == IActionResource.SOLUTION_FILE_RESOURCE) {
744: reportDefinitionPath = PentahoSystem
745: .getApplicationContext().getSolutionPath(
746: resource.getAddress());
747: } else {
748: reportDefinitionPath = resource.getAddress();
749: }
750: return reportDefinitionPath;
751: }
752:
753: private String getMimeType(String reportOutputType) {
754: String mimeType = null;
755:
756: if (HTML.equals(reportOutputType)) { //$NON-NLS-1$
757: mimeType = TEXT_HTML; //$NON-NLS-1$
758: } else if (PDF.equals(reportOutputType)) { //$NON-NLS-1$
759: mimeType = "application/pdf"; //$NON-NLS-1$
760: } else if (XLS.equals(reportOutputType)) { //$NON-NLS-1$
761: mimeType = "application/vnd.ms-excel"; //$NON-NLS-1$
762: } else if (CSV.equals(reportOutputType)) { //$NON-NLS-1$
763: mimeType = "text/text"; //$NON-NLS-1$
764: } else if (XML.equals(reportOutputType)) { //$NON-NLS-1$
765: mimeType = "text/xml"; //$NON-NLS-1$
766: }
767: return mimeType;
768: }
769:
770: private OutputStream getOutputStream(String mimeType,
771: String extension, IContentItem contentItem) {
772: OutputStream outputStream = null;
773: JasperReportAction reportAction = (JasperReportAction) getActionDefinition();
774: if (reportAction.getOutputReport() != null) {
775: //contentItem = getOutputItem(JasperReportAction.REPORT_OUTPUT_ELEMENT, mimeType, extension);
776: contentItem = getOutputItem(reportAction.getOutputReport()
777: .getName(), mimeType, extension);
778: try {
779: outputStream = contentItem
780: .getOutputStream(getActionName());
781: } catch (Exception e) {
782: }
783: } else if (getOutputNames().size() == 1) {
784: String outputName = (String) getOutputNames().iterator()
785: .next();
786: contentItem = getOutputContentItem(outputName, mimeType);
787: try {
788: outputStream = contentItem
789: .getOutputStream(getActionName());
790: } catch (Exception e) {
791: }
792: } else {
793: // There was no output in the action-sequence document, so make a
794: // default
795: // outputStream.
796: warn(Messages.getString("Base.WARN_NO_OUTPUT_STREAM")); //$NON-NLS-1$
797: outputStream = getDefaultOutputStream(mimeType);
798: if (outputStream != null) {
799: setOutputMimeType(mimeType);
800: }
801: }
802: return outputStream;
803: }
804:
805: public boolean init() {
806: // TODO any initialization you need to do before execution
807:
808: return true;
809: }
810:
811: public void done() {
812: // perform any cleanup necessary
813: }
814:
815: private Connection getConnection() {
816: try {
817: // get the report settings
818: JasperReportAction reportAction = (JasperReportAction) getActionDefinition();
819: String jndiUrl = reportAction.getJndi().getStringValue();
820: Connection conn = null;
821: if (jndiUrl != null) {
822: DataSource ds = DatasourceHelper
823: .getDataSourceFromJndi(jndiUrl);
824: return ds.getConnection();
825: } else {
826: String driver = reportAction.getDriver()
827: .getStringValue();
828: String connectString = reportAction.getConnection()
829: .getStringValue();
830: String user = reportAction.getUserId().getStringValue();
831: String password = reportAction.getPassword()
832: .getStringValue();
833: Class.forName(driver);
834: conn = DriverManager.getConnection(connectString, user,
835: password);
836: }
837: return conn;
838: } catch (ClassNotFoundException cnfe) {
839: error(Messages
840: .getErrorString("JasperReport.ERROR_0015_JDBC_DRIVER_LOAD_FAILED")); //$NON-NLS-1$
841: } catch (SQLException se) {
842: error(
843: Messages
844: .getErrorString("JasperReport.ERROR_0016_DATABASE_CONNECTION_FAILED"), se); //$NON-NLS-1$
845: } catch (NamingException ne) {
846: error(
847: Messages
848: .getErrorString("JasperReport.ERROR_0016_DATABASE_CONNECTION_FAILED"), ne); //$NON-NLS-1$
849: }
850: return null;
851: }
852: }
|