/* * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * Copyright 2005 - 2009 Pentaho Corporation. All rights reserved. * * * @created Jul 25, 2005 * @author James Dixon */ package org.pentaho.platform.plugin.action.eclipsebirt; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Locale; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.birt.report.engine.api.EngineConstants; import org.eclipse.birt.report.engine.api.EngineException; import org.eclipse.birt.report.engine.api.HTMLRenderContext; import org.eclipse.birt.report.engine.api.HTMLRenderOption; import org.eclipse.birt.report.engine.api.IGetParameterDefinitionTask; import org.eclipse.birt.report.engine.api.IParameterDefn; import org.eclipse.birt.report.engine.api.IParameterSelectionChoice; import org.eclipse.birt.report.engine.api.IRenderOption; import org.eclipse.birt.report.engine.api.IReportEngine; import org.eclipse.birt.report.engine.api.IReportRunnable; import org.eclipse.birt.report.engine.api.IRunAndRenderTask; import org.eclipse.birt.report.engine.api.IScalarParameterDefn; import org.eclipse.birt.report.engine.api.PDFRenderContext; import org.eclipse.birt.report.engine.api.ReportParameterConverter; import org.eclipse.birt.report.engine.api.impl.ScalarParameterDefn; import org.pentaho.actionsequence.dom.ActionInputConstant; import org.pentaho.actionsequence.dom.IActionOutput; import org.pentaho.actionsequence.dom.actions.BirtReportAction; import org.pentaho.platform.api.engine.IActionParameter; import org.pentaho.platform.api.engine.IActionSequenceResource; import org.pentaho.platform.api.engine.IOutputHandler; import org.pentaho.platform.api.engine.IPentahoSession; import org.pentaho.platform.api.engine.IRuntimeContext; import org.pentaho.platform.api.repository.IContentItem; import org.pentaho.platform.api.repository.ISolutionRepository; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.pentaho.platform.engine.services.solution.ComponentBase; import org.pentaho.platform.plugin.action.messages.Messages; /** * @author James Dixon * * This component uses the BIRT report engine to generate PDF and HTML report * output. It uses inputs provided by the runtime context and writes the output * to the * */ @SuppressWarnings("deprecation") public class BIRTReportComponent extends ComponentBase { protected static final Log logger = LogFactory.getLog(BIRTReportComponent.class); protected static IReportEngine reportEngine = null; private static final long serialVersionUID = -6051215996386090147L; private static final int OUTPUT_TYPE_HTML = 0; private static final int OUTPUT_TYPE_PDF = 1; private static final ArrayList outputTypeStrings = new ArrayList(); private String type; private int typeIdx = -1; private String mimeType; private String extension = ""; //$NON-NLS-1$ private IRenderOption renderOptions; private IGetParameterDefinitionTask parameterDefinitionTask; static { BIRTReportComponent.outputTypeStrings.add("html"); //$NON-NLS-1$ BIRTReportComponent.outputTypeStrings.add("pdf"); //$NON-NLS-1$ } @Override public Log getLogger() { return LogFactory.getLog(BIRTReportComponent.class); } @Override protected boolean validateSystemSettings() { // This component does not have any system settings to validate return true; } /* * This validates that the inputs, outputs, and resoruces that are available * to us are correct. The actual values are not available at this point. * * @see org.pentaho.component.ComponentBase#validate() */ @Override protected boolean validateAction() { boolean actionValidated = true; BirtReportAction reportAction = null; if (getActionDefinition() instanceof BirtReportAction) { reportAction = (BirtReportAction) getActionDefinition(); if (BIRTReportComponent.reportEngine == null) { error(Messages.getInstance().getErrorString("BIRTReportComponent.ERROR_0016_ENGINE_NOT_INITIALIZED")); //$NON-NLS-1$ actionValidated = false; } // Check that we have an input called 'output-typ' that will specified // 'html' or 'pdf' output if (actionValidated) { if (reportAction.getOutputType() != ActionInputConstant.NULL_INPUT) { String outputType = reportAction.getOutputType().getStringValue(); if (!outputType.equals("html") && !outputType.equals("pdf")) { //$NON-NLS-1$//$NON-NLS-2$ actionValidated = false; error(Messages.getInstance().getErrorString("BIRT.ERROR_0003_REPORT_TYPE_NOT_VALID", type)); //$NON-NLS-1$ } } else { // We don't know what kind of output to produce, so return an error actionValidated = false; error(Messages.getInstance().getErrorString("BIRT.ERROR_0001_REPORT_TYPE_NOT_SPECIFIED")); //$NON-NLS-1$ } } } else { actionValidated = false; error(Messages.getInstance().getErrorString( "ComponentBase.ERROR_0001_UNKNOWN_ACTION_TYPE", getActionDefinition().getElement().asXML())); //$NON-NLS-1$ } // Check that we have a resource called 'report-definition' tht should // be the BIRT report definition file if (actionValidated && (reportAction.getReportDefinition() == null)) { // We don't have a report definition resource, so return an error error(Messages.getInstance().getErrorString("BIRT.ERROR_0002_REPORT_DEFINITION_NOT_SPECIFIED")); //$NON-NLS-1$ actionValidated = false; } // Looks like everything is ok so far return actionValidated; } /* * perform any initialization required * * @see org.pentaho.component.ComponentBase#init() */ @Override public boolean init() { // BIRT report engine is initialized by the BIRT System Listener so just return return true; } /* * Execute the BIRT report using the input, resources and outputs defined. * If we require parameters to be selected by the user, generate a form * definition for the user * * @see org.pentaho.component.ComponentBase#execute() */ @Override protected boolean executeAction() { // IRuntimeContext context = getRuntimeContext(); // Get the real location of the report definition file BirtReportAction reportAction = (BirtReportAction) getActionDefinition(); IActionSequenceResource reportDefResource = getResource(reportAction.getReportDefinition().getName()); String reportDefinitionPath = reportDefResource.getAddress(); InputStream reportDefinition; try { reportDefinition = PentahoSystem.get(ISolutionRepository.class, getSession()).getResourceInputStream(reportDefResource, true, ISolutionRepository.ACTION_EXECUTE); } catch (FileNotFoundException e1) { error(e1.getLocalizedMessage()); return false; } // Get the type of the output type = reportAction.getOutputType().getStringValue(); typeIdx = BIRTReportComponent.outputTypeStrings.indexOf(type); if (typeIdx < 0) { error(Messages.getInstance().getErrorString("BIRT.ERROR_0003_REPORT_TYPE_NOT_VALID", type)); //$NON-NLS-1$ return false; } switch (typeIdx) { case OUTPUT_TYPE_HTML: { mimeType = "text/html"; //$NON-NLS-1$ extension = ".html"; //$NON-NLS-1$ renderOptions = new HTMLRenderOption(); break; } case OUTPUT_TYPE_PDF: { mimeType = "application/pdf"; //$NON-NLS-1$ extension = ".pdf"; //$NON-NLS-1$ renderOptions = new HTMLRenderOption(); break; } default: { error(Messages.getInstance().getErrorString("BIRT.ERROR_0003_REPORT_TYPE_NOT_VALID", type)); //$NON-NLS-1$ return false; } } if (ComponentBase.debug) { debug(Messages.getInstance().getString("BIRT.DEBUG_EXECUTING_REPORT", reportDefinitionPath, type)); //$NON-NLS-1$ } String baseUrl = PentahoSystem.getApplicationContext().getBaseUrl(); boolean result = false; try { // TODO support caching or the ReportRunnable object // Create an isntance of a runnable report using the report // definition file provided IReportRunnable design = BIRTReportComponent.reportEngine.openReportDesign(reportDefinition); if (design == null) { return false; } //getParameterDefns //IGetParameterDefinitionTask parameterDefinitionTask = reportEngine.createGetParameterDefinitionTask(design); parameterDefinitionTask = BIRTReportComponent.reportEngine.createGetParameterDefinitionTask(design); // Set up the parameters for the report HashMap parameterMap = new HashMap(); int parameterStatus = setupParameters(parameterDefinitionTask.getParameterDefns(false), parameterMap); if (parameterStatus == IRuntimeContext.PARAMETERS_FAIL) { error(Messages.getInstance().getErrorString("BIRT.ERROR_0005_INVALID_REPORT_PARAMETERS")); //$NON-NLS-1$ return false; } else if (parameterStatus == IRuntimeContext.PARAMETERS_UI_NEEDED) { return true; } parameterDefinitionTask.close(); // Run task IRunAndRenderTask runTask = BIRTReportComponent.reportEngine.createRunAndRenderTask(design); IPentahoSession session = this.getSession(); runTask.setLocale(session.getLocale()); // Get the output stream that the content is going into OutputStream outputStream = null; IContentItem contentItem = null; IActionOutput actionOutput = reportAction.getOutputReport(); if (actionOutput != null) { if (actionOutput.getName().equals(BirtReportAction.REPORT_OUTPUT_ELEMENT)) { contentItem = getOutputItem(reportAction.getOutputReport().getName(), mimeType, extension); } else { contentItem = getOutputContentItem(actionOutput.getName()); contentItem.setMimeType(mimeType); } try { outputStream = contentItem.getOutputStream(getActionName()); } catch (Exception e) { } } else { // There was no output in the action-sequence document, so make // a default // outputStream. warn(Messages.getInstance().getString("Base.WARN_NO_OUTPUT_STREAM")); //$NON-NLS-1$ outputStream = getDefaultOutputStream(mimeType); if (outputStream != null) { setOutputMimeType(mimeType); } } if (outputStream == null) { // We could not get an output stream for the content error(Messages.getInstance().getErrorString("BIRT.ERROR_0006_INVALID_OUTPUT_STREAM")); //$NON-NLS-1$ return false; } // Execute the report try { result = generateReport(parameterMap, outputStream, runTask, baseUrl); } finally { if (contentItem != null) { contentItem.closeOutputStream(); } } } catch (Throwable e) { error(Messages.getInstance().getErrorString("BIRT.ERROR_0007_REPORT_ERRORS_ENCOUNTERED", reportDefinitionPath), e); //$NON-NLS-1$ } return result; } /* * Create an instance of the EngineConfig class for the BIRT report engine. */ /* * Set up the report parameters */ private int setupParameters(final Collection params, final HashMap reportParameterMap) { try { boolean parameterUINeeded = false; if (getOutputPreference() == IOutputHandler.OUTPUT_TYPE_PARAMETERS) { parameterUINeeded = true; } // Process each parameter in turn Iterator it = params.iterator(); while (it.hasNext()) { IParameterDefn param = (IParameterDefn) it.next(); ScalarParameterDefn scalarParm = null; if (param instanceof ScalarParameterDefn) { scalarParm = (ScalarParameterDefn) param; } String paramName = param.getName(); String defaultValue = ""; //$NON-NLS-1$ if (scalarParm != null) { defaultValue = scalarParm.getDefaultValue(); } if (!isDefinedInput(paramName)) { // what do we do here? continue; } String contextValue = getInputStringValue(paramName); // Get the value for the parameter from the current context IActionParameter paramParameter = getInputParameter(paramName); // if(debug) // debug(Messages.getInstance().getString("BIRT.DEBUG_SETTING_PARAMETER", // paramName, paramValue )); //$NON-NLS-1$ // There is no parameter in the runtime or the parameter found // has no value and no selections if ((paramParameter == null) || ((!paramParameter.hasValue() || parameterUINeeded) && !paramParameter.hasSelections())) { if (paramParameter.getPromptStatus() == IActionParameter.PROMPT_PENDING) { parameterUINeeded = true; continue; } else { paramParameter = null; } } if (paramParameter == null) { // Still no parameter, use BIRTs // default way if ((scalarParm != null)) { if (parameterUIOk()) { // The parameter value was not provided, and we are // allowed to create user interface forms int controlType = scalarParm.getControlType(); if ((controlType == IScalarParameterDefn.LIST_BOX)) { ArrayList values = new ArrayList(); HashMap displayNames = new HashMap(); Collection coll = parameterDefinitionTask.getSelectionList(scalarParm.getName()); if (coll != null) { Iterator valueIterator = coll.iterator(); while (valueIterator.hasNext()) { IParameterSelectionChoice selectionItem = (IParameterSelectionChoice) valueIterator.next(); Object value = selectionItem.getValue(); if (value != null) { String label = selectionItem.getLabel(); displayNames.put(value.toString(), (label != null) ? label : value); values.add(value); } } } createFeedbackParameter(param.getName(), /*param.getDisplayName()*/scalarParm.getPromptText(), scalarParm.getHelpText(), defaultValue, values, displayNames, null); } else { createFeedbackParameter(param.getName(), /*param.getDisplayName()*/scalarParm.getPromptText(), scalarParm.getHelpText(), defaultValue, true); } parameterUINeeded = true; continue; } else { return IRuntimeContext.PARAMETERS_FAIL; } } else if (defaultValue.length() == 0) { // If the length of the defaultValue is not zero, then: // 1- It's a scalar parameter // 2- There's a default value expression associated with // the parameter. // Therefore, let BIRT use that expression // automatically. // The parameter was not provided and we are not allowed // to create forms error(Messages.getInstance().getErrorString("BIRT.ERROR_0009_PARAMETER_NOT_PROVIDED", paramName)); //$NON-NLS-1$ return IRuntimeContext.PARAMETERS_FAIL; } } String paramValue = contextValue; if ((paramValue != null) && (!"".equals(paramValue))) { //$NON-NLS-1$ if (scalarParm != null) { String format = scalarParm.getDisplayFormat(); ReportParameterConverter cfgConverter = new ReportParameterConverter(format, Locale.getDefault()); Object configValueObj = cfgConverter.parse(paramValue, scalarParm.getDataType()); reportParameterMap.put(paramName, configValueObj); } else { reportParameterMap.put(paramName, paramValue); } } } if (parameterUINeeded) { return IRuntimeContext.PARAMETERS_UI_NEEDED; } else if (getRuntimeContext().isPromptPending()) { return IRuntimeContext.PARAMETERS_UI_NEEDED; } return IRuntimeContext.PARAMETERS_OK; } catch (Throwable error) { error(Messages.getInstance().getErrorString("BIRT.ERROR_0005_INVALID_REPORT_PARAMETERS"), error); //$NON-NLS-1$ } return IRuntimeContext.PARAMETERS_FAIL; } private boolean parameterUIOk() { /* * See if we are allowed to generate a parameter selection user * interface. If we are being called as part of a process, this will not * be allowed. */ if (!feedbackAllowed()) { return false; } // We need input from the user, we have delivered an input form into the // feeback stream setFeedbackMimeType("text/html"); //$NON-NLS-1$ return true; } /* * Generate the report output */ private boolean generateReport(final HashMap parameterMap, final OutputStream outputStream, final IRunAndRenderTask task, final String baseUrl) { try { HashMap appContextMap = new HashMap(); // Set the output options for the report if (typeIdx == BIRTReportComponent.OUTPUT_TYPE_HTML) { HTMLRenderContext htmlContext = new HTMLRenderContext(); htmlContext.setBaseImageURL(baseUrl + "getImage?image="); //$NON-NLS-1$ htmlContext.setImageDirectory(PentahoSystem.getApplicationContext().getSolutionPath("system/tmp")); //$NON-NLS-1$ htmlContext.setSupportedImageFormats("PNG;GIF;JPG;BMP"); //$NON-NLS-1$ appContextMap.put(EngineConstants.APPCONTEXT_HTML_RENDER_CONTEXT, htmlContext); } else if (typeIdx == BIRTReportComponent.OUTPUT_TYPE_PDF) { PDFRenderContext pdfContext = new PDFRenderContext(); pdfContext.setBaseURL(baseUrl + "getImage?image="); //$NON-NLS-1$ pdfContext.setSupportedImageFormats("PNG;GIF;JPG;BMP"); //$NON-NLS-1$ appContextMap.put(EngineConstants.APPCONTEXT_PDF_RENDER_CONTEXT, pdfContext); } task.setAppContext(appContextMap); task.setRenderOption(renderOptions); renderOptions.setOutputFormat(type); // Set the output stream for the content renderOptions.setOutputStream(outputStream); task.setParameterValues(parameterMap); if (ComponentBase.debug) { debug(task.toString()); } task.run(); outputStream.flush(); outputStream.close(); return true; } catch (IOException ioe) { error(null, ioe); error(Messages.getInstance().getErrorString("BIRT.ERROR_0010_REPORT_COULD_NOT_BE_RUN", ioe.getMessage())); //$NON-NLS-1$ } catch (EngineException ee) { error(null, ee); error(Messages.getInstance().getErrorString("BIRT.ERROR_0010_REPORT_COULD_NOT_BE_RUN", ee.getMessage())); //$NON-NLS-1$ } return false; } /* * Perform any cleanup necessary * * @see org.pentaho.component.ComponentBase#done() */ @Override public void done() { // we don't have anything to clean up to do } /* DM This should be removed when the JPivot/BIRT conflict is resolved * Create an instance of the EngineConfig class for the BIRT report engine. private IReportEngine createBIRTEngine() { try { // Get the global settings for the BIRT engine from our system settings String birtHome = PentahoSystem.getApplicationContext().getSolutionPath("system/BIRT"); //$NON-NLS-1$ birtHome = birtHome.replaceAll("\\\\.\\\\", "\\\\"); //$NON-NLS-1$ //$NON-NLS-2$ if (PentahoSystem.debug) Logger.debug(BirtSystemListener.class.getName(), Messages.getInstance().getString("BIRT.DEBUG_BIRT_HOME", birtHome)); //$NON-NLS-1$ // Create an appropriate Config object EngineConfig config = new EngineConfig(); config.setEngineHome(birtHome); // Configuring where BIRT engine is installed // Set the directory where the BIRT log files will go String logDest = PentahoSystem.getApplicationContext().getFileOutputPath("system/logs/BIRT"); //$NON-NLS-1$ // Set the logging level int loggingLevel = Logger.getLogLevel(); if (loggingLevel == ILogger.TRACE) { config.setLogConfig(logDest, Level.ALL); } else if (loggingLevel == ILogger.DEBUG) { config.setLogConfig(logDest, Level.FINE); } else if (loggingLevel == ILogger.INFO) { config.setLogConfig(logDest, Level.INFO); } else if (loggingLevel == ILogger.WARN) { config.setLogConfig(logDest, Level.WARNING); } else if (loggingLevel == ILogger.ERROR) { config.setLogConfig(logDest, Level.SEVERE); } else if (loggingLevel == ILogger.FATAL) { config.setLogConfig(logDest, Level.SEVERE); } // Register new image handler HTMLEmitterConfig emitterConfig = new HTMLEmitterConfig( ); emitterConfig.setActionHandler( new HTMLActionHandler( ) ); emitterConfig.setImageHandler( new HTMLServerImageHandler( ) ); config.getEmitterConfigs( ).put( RenderOptionBase.OUTPUT_FORMAT_HTML, emitterConfig ); Platform.startup( config ); IReportEngineFactory factory = (IReportEngineFactory) Platform.createFactoryObject( IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY ); IReportEngine engine = factory.createReportEngine( config ); return engine; } catch (Throwable error) { error.printStackTrace(); Logger.error(BirtSystemListener.class.getName(), Messages.getInstance().getErrorString("BIRT.ERROR_0008_INVALID_CONFIGURATION")); //$NON-NLS-1$ } return null; } */ }