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: * @created Jul 25, 2005
014: * @author James Dixon
015: */
016:
017: package org.pentaho.plugin.eclipsebirt;
018:
019: import java.io.FileNotFoundException;
020: import java.io.InputStream;
021: import java.io.OutputStream;
022: import java.util.ArrayList;
023: import java.util.Collection;
024: import java.util.HashMap;
025: import java.util.Iterator;
026: import java.util.Locale;
027:
028: import org.apache.commons.logging.Log;
029: import org.apache.commons.logging.LogFactory;
030: import org.eclipse.birt.report.engine.api.EngineConstants;
031: import org.eclipse.birt.report.engine.api.HTMLRenderContext;
032: import org.eclipse.birt.report.engine.api.HTMLRenderOption;
033: import org.eclipse.birt.report.engine.api.IGetParameterDefinitionTask;
034: import org.eclipse.birt.report.engine.api.IParameterDefn;
035: import org.eclipse.birt.report.engine.api.IParameterSelectionChoice;
036: import org.eclipse.birt.report.engine.api.IRenderOption;
037: import org.eclipse.birt.report.engine.api.IReportEngine;
038: import org.eclipse.birt.report.engine.api.IReportRunnable;
039: import org.eclipse.birt.report.engine.api.IRunAndRenderTask;
040: import org.eclipse.birt.report.engine.api.IScalarParameterDefn;
041: import org.eclipse.birt.report.engine.api.PDFRenderContext;
042: import org.eclipse.birt.report.engine.api.ReportParameterConverter;
043: import org.eclipse.birt.report.engine.api.impl.ScalarParameterDefn;
044: import org.pentaho.actionsequence.dom.ActionOutput;
045: import org.pentaho.actionsequence.dom.IActionInputVariable;
046: import org.pentaho.actionsequence.dom.actions.BirtReportAction;
047: import org.pentaho.core.repository.IContentItem;
048: import org.pentaho.core.runtime.IActionParameter;
049: import org.pentaho.core.runtime.IRuntimeContext;
050: import org.pentaho.core.session.IPentahoSession;
051: import org.pentaho.core.solution.IActionResource;
052: import org.pentaho.core.solution.IOutputHandler;
053: import org.pentaho.core.system.PentahoSystem;
054: import org.pentaho.messages.Messages;
055: import org.pentaho.plugin.ComponentBase;
056:
057: /**
058: * @author James Dixon
059: *
060: * This component uses the BIRT report engine to generate PDF and HTML report
061: * output. It uses inputs provided by the runtime context and writes the output
062: * to the
063: *
064: */
065: public class BIRTReportComponent extends ComponentBase {
066:
067: protected static IReportEngine reportEngine = null;
068:
069: private static final long serialVersionUID = -6051215996386090147L;
070:
071: private static final int OUTPUT_TYPE_HTML = 0;
072:
073: private static final int OUTPUT_TYPE_PDF = 1;
074:
075: private static final ArrayList outputTypeStrings = new ArrayList();
076:
077: private String type;
078:
079: private int typeIdx = -1;
080:
081: private String mimeType;
082:
083: private String extension = ""; //$NON-NLS-1$
084:
085: private IRenderOption renderOptions;
086:
087: private IGetParameterDefinitionTask parameterDefinitionTask;
088:
089: static {
090: outputTypeStrings.add("html"); //$NON-NLS-1$
091: outputTypeStrings.add("pdf"); //$NON-NLS-1$
092: }
093:
094: public Log getLogger() {
095: return LogFactory.getLog(BIRTReportComponent.class);
096: }
097:
098: protected boolean validateSystemSettings() {
099: // This component does not have any system settings to validate
100: return true;
101: }
102:
103: /*
104: * This validates that the inputs, outputs, and resoruces that are available
105: * to us are correct. The actual values are not available at this point.
106: *
107: * @see org.pentaho.component.ComponentBase#validate()
108: */
109: protected boolean validateAction() {
110:
111: boolean actionValidated = true;
112: BirtReportAction reportAction = null;
113:
114: if (getActionDefinition() instanceof BirtReportAction) {
115: reportAction = (BirtReportAction) getActionDefinition();
116: if (reportEngine == null) {
117: error(Messages
118: .getErrorString("BIRTReportComponent.ERROR_0016_ENGINE_NOT_INITIALIZED")); //$NON-NLS-1$
119: actionValidated = false;
120: }
121:
122: // Check that we have an input called 'output-typ' that will specified
123: // 'html' or 'pdf' output
124: if (actionValidated) {
125: if (reportAction.getOutputType() != IActionInputVariable.NULL_INPUT) {
126: String outputType = reportAction.getOutputType()
127: .getStringValue();
128:
129: if (!outputType.equals("html")
130: && !outputType.equals("pdf")) {
131: actionValidated = false;
132: error(Messages
133: .getErrorString(
134: "BIRT.ERROR_0003_REPORT_TYPE_NOT_VALID", type)); //$NON-NLS-1$
135: }
136: } else {
137: // We don't know what kind of output to produce, so return an error
138: actionValidated = false;
139: error(Messages
140: .getErrorString("BIRT.ERROR_0001_REPORT_TYPE_NOT_SPECIFIED")); //$NON-NLS-1$
141: }
142: }
143: } else {
144: actionValidated = false;
145: error(Messages
146: .getErrorString(
147: "ComponentBase.ERROR_0001_UNKNOWN_ACTION_TYPE", getActionDefinition().getElement().asXML())); //$NON-NLS-1$
148: }
149:
150: // Check that we have a resource called 'report-definition' tht should
151: // be the BIRT report definition file
152: if (actionValidated
153: && reportAction.getReportDefinition() == null) {
154: // We don't have a report definition resource, so return an error
155: error(Messages
156: .getErrorString("BIRT.ERROR_0002_REPORT_DEFINITION_NOT_SPECIFIED")); //$NON-NLS-1$
157: actionValidated = false;
158: }
159: // Looks like everything is ok so far
160: return actionValidated;
161: }
162:
163: /*
164: * perform any initialization required
165: *
166: * @see org.pentaho.component.ComponentBase#init()
167: */
168: public boolean init() {
169: // BIRT report engine is initialized by the BIRT System Listener so just return
170: return true;
171: }
172:
173: /*
174: * Execute the BIRT report using the input, resources and outputs defined.
175: * If we require parameters to be selected by the user, generate a form
176: * definition for the user
177: *
178: * @see org.pentaho.component.ComponentBase#execute()
179: */
180: protected boolean executeAction() {
181:
182: // IRuntimeContext context = getRuntimeContext();
183:
184: // Get the real location of the report definition file
185: BirtReportAction reportAction = (BirtReportAction) getActionDefinition();
186: IActionResource reportDefResource = getResource(reportAction
187: .getReportDefinition().getName());
188: String reportDefinitionPath = reportDefResource.getAddress();
189:
190: InputStream reportDefinition;
191: try {
192: reportDefinition = PentahoSystem.getSolutionRepository(
193: getSession()).getResourceInputStream(
194: reportDefResource, true);
195: } catch (FileNotFoundException e1) {
196: error(e1.getLocalizedMessage());
197: return false;
198: }
199:
200: // Get the type of the output
201: type = reportAction.getOutputType().getStringValue();
202: typeIdx = outputTypeStrings.indexOf(type);
203: if (typeIdx < 0) {
204: error(Messages.getErrorString(
205: "BIRT.ERROR_0003_REPORT_TYPE_NOT_VALID", type)); //$NON-NLS-1$
206: return false;
207: }
208:
209: switch (typeIdx) {
210: case OUTPUT_TYPE_HTML: {
211: mimeType = "text/html"; //$NON-NLS-1$
212: extension = ".html"; //$NON-NLS-1$
213: renderOptions = new HTMLRenderOption();
214: break;
215: }
216: case OUTPUT_TYPE_PDF: {
217: mimeType = "application/pdf"; //$NON-NLS-1$
218: extension = ".pdf"; //$NON-NLS-1$
219: renderOptions = new HTMLRenderOption();
220: break;
221: }
222: default: {
223: error(Messages.getErrorString(
224: "BIRT.ERROR_0003_REPORT_TYPE_NOT_VALID", type)); //$NON-NLS-1$
225: return false;
226: }
227: }
228:
229: if (debug)
230: debug(Messages
231: .getString(
232: "BIRT.DEBUG_EXECUTING_REPORT", reportDefinitionPath, type)); //$NON-NLS-1$
233:
234: String baseUrl = PentahoSystem.getApplicationContext()
235: .getBaseUrl();
236:
237: boolean result = false;
238: try {
239:
240: // TODO support caching or the ReportRunnable object
241: // Create an isntance of a runnable report using the report
242: // definition file provided
243: IReportRunnable design = reportEngine
244: .openReportDesign(reportDefinition);
245: if (design == null) {
246: return false;
247: }
248:
249: //getParameterDefns
250: //IGetParameterDefinitionTask parameterDefinitionTask = reportEngine.createGetParameterDefinitionTask(design);
251: parameterDefinitionTask = reportEngine
252: .createGetParameterDefinitionTask(design);
253:
254: // Set up the parameters for the report
255: HashMap parameterMap = new HashMap();
256: int parameterStatus = setupParameters(
257: parameterDefinitionTask.getParameterDefns(false),
258: parameterMap);
259: if (parameterStatus == IRuntimeContext.PARAMETERS_FAIL) {
260: error(Messages
261: .getErrorString("BIRT.ERROR_0005_INVALID_REPORT_PARAMETERS")); //$NON-NLS-1$
262: return false;
263: } else if (parameterStatus == IRuntimeContext.PARAMETERS_UI_NEEDED) {
264: return true;
265: }
266: parameterDefinitionTask.close();
267:
268: // Run task
269: IRunAndRenderTask runTask = reportEngine
270: .createRunAndRenderTask(design);
271: IPentahoSession session = this .getSession();
272: runTask.setLocale(session.getLocale());
273:
274: // Get the output stream that the content is going into
275: OutputStream outputStream = null;
276: IContentItem contentItem = null;
277: ActionOutput actionOutput = reportAction.getOutputReport();
278: if (actionOutput != null) {
279: if (actionOutput.getName().equals(
280: BirtReportAction.REPORT_OUTPUT_ELEMENT)) {
281: contentItem = getOutputItem(reportAction
282: .getOutputReport().getName(), mimeType,
283: extension);
284: } else {
285: contentItem = getOutputContentItem(actionOutput
286: .getName());
287: contentItem.setMimeType(mimeType);
288: }
289: try {
290: outputStream = contentItem
291: .getOutputStream(getActionName());
292: } catch (Exception e) {
293: }
294: } else {
295: // There was no output in the action-sequence document, so make
296: // a default
297: // outputStream.
298: warn(Messages.getString("Base.WARN_NO_OUTPUT_STREAM")); //$NON-NLS-1$
299: outputStream = getDefaultOutputStream(mimeType);
300: if (outputStream != null) {
301: setOutputMimeType(mimeType);
302: }
303: }
304:
305: if (outputStream == null) {
306: // We could not get an output stream for the content
307: error(Messages
308: .getErrorString("BIRT.ERROR_0006_INVALID_OUTPUT_STREAM")); //$NON-NLS-1$
309: return false;
310: }
311:
312: // Execute the report
313: try {
314: result = generateReport(parameterMap, outputStream,
315: runTask, baseUrl);
316: } finally {
317: if (contentItem != null) {
318: contentItem.closeOutputStream();
319: }
320: }
321: } catch (Throwable e) {
322: error(
323: Messages
324: .getErrorString(
325: "BIRT.ERROR_0007_REPORT_ERRORS_ENCOUNTERED", reportDefinitionPath), e); //$NON-NLS-1$
326: }
327:
328: return result;
329: }
330:
331: /*
332: * Create an instance of the EngineConfig class for the BIRT report engine.
333: */
334: /*
335: * Set up the report parameters
336: */
337: private int setupParameters(Collection params,
338: HashMap reportParameterMap) {
339: try {
340:
341: boolean parameterUINeeded = false;
342: if (getOutputPreference() == IOutputHandler.OUTPUT_TYPE_PARAMETERS) {
343: parameterUINeeded = true;
344: }
345:
346: // Process each parameter in turn
347: Iterator it = params.iterator();
348: while (it.hasNext()) {
349: IParameterDefn param = (IParameterDefn) it.next();
350: ScalarParameterDefn scalarParm = null;
351: if (param instanceof ScalarParameterDefn) {
352: scalarParm = (ScalarParameterDefn) param;
353: }
354: String paramName = param.getName();
355: String defaultValue = ""; //$NON-NLS-1$
356: if (scalarParm != null) {
357: defaultValue = scalarParm.getDefaultValue();
358: }
359: if (!isDefinedInput(paramName)) {
360: // what do we do here?
361: continue;
362: }
363: String contextValue = getInputStringValue(paramName);
364:
365: // Get the value for the parameter from the current context
366: IActionParameter paramParameter = getInputParameter(paramName);
367: // if(debug)
368: // debug(Messages.getString("BIRT.DEBUG_SETTING_PARAMETER",
369: // paramName, paramValue )); //$NON-NLS-1$
370:
371: // There is no parameter in the runtime or the parameter found
372: // has no value and no selections
373: if (paramParameter == null
374: || ((!paramParameter.hasValue() || parameterUINeeded) && !paramParameter
375: .hasSelections())) {
376:
377: if (paramParameter.getPromptStatus() == IActionParameter.PROMPT_PENDING) {
378: parameterUINeeded = true;
379: continue;
380: } else {
381: paramParameter = null;
382: }
383: }
384:
385: if (paramParameter == null) { // Still no parameter, use BIRTs
386: // default way
387: if ((scalarParm != null)) {
388: if (parameterUIOk()) {
389: // The parameter value was not provided, and we are
390: // allowed to create user interface forms
391: int controlType = scalarParm
392: .getControlType();
393: if ((controlType == IScalarParameterDefn.LIST_BOX)) {
394: ArrayList values = new ArrayList();
395: HashMap displayNames = new HashMap();
396: Collection coll = parameterDefinitionTask
397: .getSelectionList(scalarParm
398: .getName());
399: if (coll != null) {
400: Iterator valueIterator = coll
401: .iterator();
402: while (valueIterator.hasNext()) {
403: IParameterSelectionChoice selectionItem = (IParameterSelectionChoice) valueIterator
404: .next();
405: Object value = selectionItem
406: .getValue();
407: if (value != null) {
408: String label = selectionItem
409: .getLabel();
410: displayNames
411: .put(
412: value
413: .toString(),
414: (label != null) ? label
415: : value);
416: values.add(value);
417: }
418: }
419:
420: }
421: createFeedbackParameter(
422: param.getName(), /*param.getDisplayName()*/
423: scalarParm.getPromptText(),
424: scalarParm.getHelpText(),
425: defaultValue, values,
426: displayNames, null);
427: } else {
428: createFeedbackParameter(
429: param.getName(), /*param.getDisplayName()*/
430: scalarParm.getPromptText(),
431: scalarParm.getHelpText(),
432: defaultValue, true);
433: }
434: parameterUINeeded = true;
435: continue;
436: } else {
437: return IRuntimeContext.PARAMETERS_FAIL;
438: }
439: } else if (defaultValue.length() == 0) {
440: // If the length of the defaultValue is not zero, then:
441: // 1- It's a scalar parameter
442: // 2- There's a default value expression associated with
443: // the parameter.
444: // Therefore, let BIRT use that expression
445: // automatically.
446:
447: // The parameter was not provided and we are not allowed
448: // to create forms
449: error(Messages
450: .getErrorString(
451: "BIRT.ERROR_0009_PARAMETER_NOT_PROVIDED", paramName)); //$NON-NLS-1$
452: return IRuntimeContext.PARAMETERS_FAIL;
453: }
454: }
455: String paramValue = contextValue;
456: if ((paramValue != null) && (!"".equals(paramValue))) { //$NON-NLS-1$
457: if (scalarParm != null) {
458: String format = scalarParm.getDisplayFormat();
459: ReportParameterConverter cfgConverter = new ReportParameterConverter(
460: format, Locale.getDefault());
461: Object configValueObj = cfgConverter.parse(
462: paramValue, scalarParm.getDataType());
463: reportParameterMap.put(paramName,
464: configValueObj);
465: } else {
466: reportParameterMap.put(paramName, paramValue);
467: }
468: }
469: }
470: if (parameterUINeeded) {
471: return IRuntimeContext.PARAMETERS_UI_NEEDED;
472: } else if (getRuntimeContext().isPromptPending()) {
473: return IRuntimeContext.PARAMETERS_UI_NEEDED;
474: }
475: return IRuntimeContext.PARAMETERS_OK;
476: } catch (Throwable error) {
477: error(
478: Messages
479: .getErrorString("BIRT.ERROR_0005_INVALID_REPORT_PARAMETERS"), error); //$NON-NLS-1$
480: }
481: return IRuntimeContext.PARAMETERS_FAIL;
482: }
483:
484: private boolean parameterUIOk() {
485: /*
486: * See if we are allowed to generate a parameter selection user
487: * interface. If we are being called as part of a process, this will not
488: * be allowed.
489: */
490:
491: if (!feedbackAllowed()) {
492: return false;
493: }
494: // We need input from the user, we have delivered an input form into the
495: // feeback stream
496: setFeedbackMimeType("text/html"); //$NON-NLS-1$
497: return true;
498: }
499:
500: /*
501: * Generate the report output
502: */
503: private boolean generateReport(HashMap parameterMap,
504: OutputStream outputStream, IRunAndRenderTask task,
505: String baseUrl) {
506:
507: try {
508: HashMap appContextMap = new HashMap();
509: // Set the output options for the report
510: if (typeIdx == OUTPUT_TYPE_HTML) {
511: HTMLRenderContext htmlContext = new HTMLRenderContext();
512: htmlContext
513: .setBaseImageURL(baseUrl + "getImage?image="); //$NON-NLS-1$
514: htmlContext.setImageDirectory(PentahoSystem
515: .getApplicationContext().getSolutionPath(
516: "system/tmp")); //$NON-NLS-1$
517: htmlContext.setSupportedImageFormats("PNG;GIF;JPG;BMP"); //$NON-NLS-1$
518: appContextMap.put(
519: EngineConstants.APPCONTEXT_HTML_RENDER_CONTEXT,
520: htmlContext);
521: } else if (typeIdx == OUTPUT_TYPE_PDF) {
522: PDFRenderContext pdfContext = new PDFRenderContext();
523: pdfContext.setBaseURL(baseUrl + "getImage?image="); //$NON-NLS-1$
524: pdfContext.setSupportedImageFormats("PNG;GIF;JPG;BMP"); //$NON-NLS-1$
525: appContextMap.put(
526: EngineConstants.APPCONTEXT_PDF_RENDER_CONTEXT,
527: pdfContext);
528: }
529:
530: task.setAppContext(appContextMap);
531: task.setRenderOption(renderOptions);
532: renderOptions.setOutputFormat(type);
533:
534: // Set the output stream for the content
535: renderOptions.setOutputStream(outputStream);
536: task.setParameterValues(parameterMap);
537: if (debug) {
538: debug(task.toString());
539: }
540: task.run();
541:
542: outputStream.flush();
543: outputStream.close();
544: return true;
545: } catch (Exception e) {
546: e.printStackTrace();
547: error(Messages
548: .getErrorString(
549: "BIRT.ERROR_0010_REPORT_COULD_NOT_BE_RUN", e.getMessage())); //$NON-NLS-1$
550: }
551:
552: return false;
553: }
554:
555: /*
556: * Perform any cleanup necessary
557: *
558: * @see org.pentaho.component.ComponentBase#done()
559: */
560: public void done() {
561: // we don't have anything to clean up to do
562: }
563:
564: /* DM This should be removed when the JPivot/BIRT conflict is resolved
565: * Create an instance of the EngineConfig class for the BIRT report engine.
566:
567: private IReportEngine createBIRTEngine() {
568:
569: try {
570: // Get the global settings for the BIRT engine from our system settings
571: String birtHome = PentahoSystem.getApplicationContext().getSolutionPath("system/BIRT"); //$NON-NLS-1$
572: birtHome = birtHome.replaceAll("\\\\.\\\\", "\\\\"); //$NON-NLS-1$ //$NON-NLS-2$
573:
574: if (PentahoSystem.debug)
575: Logger.debug(BirtSystemListener.class.getName(), Messages.getString("BIRT.DEBUG_BIRT_HOME", birtHome)); //$NON-NLS-1$
576:
577: // Create an appropriate Config object
578: EngineConfig config = new EngineConfig();
579: config.setEngineHome(birtHome); // Configuring where BIRT engine is installed
580:
581: // Set the directory where the BIRT log files will go
582: String logDest = PentahoSystem.getApplicationContext().getFileOutputPath("system/logs/BIRT"); //$NON-NLS-1$
583:
584: // Set the logging level
585: int loggingLevel = Logger.getLogLevel();
586: if (loggingLevel == ILogger.TRACE) {
587: config.setLogConfig(logDest, Level.ALL);
588: } else if (loggingLevel == ILogger.DEBUG) {
589: config.setLogConfig(logDest, Level.FINE);
590: } else if (loggingLevel == ILogger.INFO) {
591: config.setLogConfig(logDest, Level.INFO);
592: } else if (loggingLevel == ILogger.WARN) {
593: config.setLogConfig(logDest, Level.WARNING);
594: } else if (loggingLevel == ILogger.ERROR) {
595: config.setLogConfig(logDest, Level.SEVERE);
596: } else if (loggingLevel == ILogger.FATAL) {
597: config.setLogConfig(logDest, Level.SEVERE);
598: }
599:
600: // Register new image handler
601: HTMLEmitterConfig emitterConfig = new HTMLEmitterConfig( );
602: emitterConfig.setActionHandler( new HTMLActionHandler( ) );
603: emitterConfig.setImageHandler( new HTMLServerImageHandler( ) );
604: config.getEmitterConfigs( ).put( RenderOptionBase.OUTPUT_FORMAT_HTML, emitterConfig );
605:
606: Platform.startup( config );
607:
608: IReportEngineFactory factory = (IReportEngineFactory) Platform.createFactoryObject( IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY );
609: IReportEngine engine = factory.createReportEngine( config );
610: return engine;
611: }
612: catch (Throwable error) {
613: error.printStackTrace();
614: Logger.error(BirtSystemListener.class.getName(), Messages.getErrorString("BIRT.ERROR_0008_INVALID_CONFIGURATION")); //$NON-NLS-1$
615: }
616: return null;
617: }
618: */
619:
620: }
|