/* * 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 2006 - 2009 Pentaho Corporation. All rights reserved. * * * @created Jan 2, 2006 * @author Matt Casters */ package org.pentaho.platform.plugin.action.kettle; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.dom4j.Node; import org.pentaho.commons.connection.memory.MemoryMetaData; import org.pentaho.commons.connection.memory.MemoryResultSet; import org.pentaho.di.core.exception.KettleException; import org.pentaho.di.core.exception.KettleStepException; import org.pentaho.di.core.exception.KettleValueException; import org.pentaho.di.core.logging.Log4jStringAppender; import org.pentaho.di.core.logging.LogWriter; import org.pentaho.di.core.parameters.UnknownParamException; import org.pentaho.di.core.row.RowMetaInterface; import org.pentaho.di.core.row.ValueMetaInterface; import org.pentaho.di.core.xml.XMLHandlerCache; import org.pentaho.di.job.Job; import org.pentaho.di.job.JobMeta; import org.pentaho.di.repository.RepositoriesMeta; import org.pentaho.di.repository.Repository; import org.pentaho.di.repository.RepositoryDirectory; import org.pentaho.di.repository.RepositoryMeta; import org.pentaho.di.repository.UserInfo; import org.pentaho.di.trans.StepLoader; import org.pentaho.di.trans.Trans; import org.pentaho.di.trans.TransMeta; import org.pentaho.di.trans.step.RowListener; import org.pentaho.di.trans.step.StepMetaDataCombi; import org.pentaho.platform.api.engine.IActionSequenceResource; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.pentaho.platform.engine.services.solution.ComponentBase; import org.pentaho.platform.plugin.action.messages.Messages; import org.pentaho.platform.util.xml.w3c.XmlW3CHelper; /** * KettleComponent shows a list of available transformations in the root of the choosen repository. * * @author Matt */ /* Legitimate outputs: * EXECUTION_STATUS_OUTPUT - (execution-status) * [JOB | TRANS] Returns the resultant execution status * * EXECUTION_LOG_OUTPUT - (execution-log) * [JOB | TRANS] Returns the resultant log * * TRANSFORM_SUCCESS_OUTPUT - (transformation-written) * [Requires MONITORSTEP to be defined] * [TRANS] Returns a "result-set" for all successful rows written (Unless * error handling is not defined for the specified step, in which case ALL * rows are returned here) * * TRANSFORM_ERROR_OUTPUT - (transformation-errors) * [Requires MONITORSTEP to be defined] * [TRANS] Returns a "result-set" for all rows written that have caused an error * * TRANSFORM_SUCCESS_COUNT_OUTPUT - (transformation-written-count) * [Requires MONITORSTEP to be defined] * [TRANS] Returns a count of all rows returned in TRANSFORM_SUCCESS_OUTPUT * * TRANSFORM_ERROR_COUNT_OUTPUT - (transformation-errors-count) * [Requires MONITORSTEP to be defined] * [TRANS] Returns a count of all rows returned in TRANSFORM_ERROR_OUTPUT * * Legitimate inputs: * MONITORSTEP * Takes the name of the step from which success and error rows can be detected * * KETTLELOGLEVEL * Sets the logging level to be used in the EXECUTION_LOG_OUTPUT * Valid settings: * basic * detail * error * debug * minimal * rowlevel * */ public class KettleComponent extends ComponentBase implements RowListener { private static final long serialVersionUID = 8217343898202366129L; private static final String DIRECTORY = "directory"; //$NON-NLS-1$ private static final String TRANSFORMATION = "transformation"; //$NON-NLS-1$ private static final String JOB = "job"; //$NON-NLS-1$ private static final String TRANSFORMFILE = "transformation-file"; //$NON-NLS-1$ private static final String JOBFILE = "job-file"; //$NON-NLS-1$ //IMPORTSTEP here for backwards compatibility; Superceded by MONITORSTEP private static final String IMPORTSTEP = "importstep"; //$NON-NLS-1$ private static final String MONITORSTEP = "monitor-step"; //$NON-NLS-1$ private static final String KETTLELOGLEVEL = "kettle-logging-level"; //$NON-NLS-1$ private static final String EXECUTION_STATUS_OUTPUT = "kettle-execution-status"; //$NON-NLS-1$ private static final String EXECUTION_LOG_OUTPUT = "kettle-execution-log"; //$NON-NLS-1$ private static final String TRANSFORM_SUCCESS_OUTPUT = "transformation-output-rows"; //$NON-NLS-1$ private static final String TRANSFORM_ERROR_OUTPUT = "transformation-output-error-rows"; //$NON-NLS-1$ private static final String TRANSFORM_SUCCESS_COUNT_OUTPUT = "transformation-output-rows-count"; //$NON-NLS-1$ private static final String TRANSFORM_ERROR_COUNT_OUTPUT = "transformation-output-error-rows-count"; //$NON-NLS-1$ public static final String PARAMETER_MAP_CMD_ARG = "set-argument"; //$NON-NLS-1$ public static final String PARAMETER_MAP_VARIABLE = "set-variable"; //$NON-NLS-1$ public static final String PARAMETER_MAP_PARAMETER = "set-parameter"; //$NON-NLS-1$ private static final ArrayList<String> outputParams = new ArrayList<String>(Arrays.asList(EXECUTION_STATUS_OUTPUT, EXECUTION_LOG_OUTPUT, TRANSFORM_SUCCESS_OUTPUT, TRANSFORM_ERROR_OUTPUT, TRANSFORM_SUCCESS_COUNT_OUTPUT, TRANSFORM_ERROR_COUNT_OUTPUT)); /** * The repositories.xml file location, if empty take the default $HOME/.kettle/repositories.xml */ private String repositoriesXMLFile; /** The name of the repository to use */ private String repositoryName; /** The username to login with */ private String username; private MemoryResultSet results; private MemoryResultSet errorResults; private String executionStatus; private String executionLog; /** The password to login with */ private String password; private Log4jStringAppender kettleUserAppender; @Override public Log getLogger() { return LogFactory.getLog(KettleComponent.class); } @Override protected boolean validateSystemSettings() { // set pentaho.solutionpath so that it can be used in file paths boolean useRepository = PentahoSystem .getSystemSetting("kettle/settings.xml", "repository.type", "files").equals("rdbms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ if (useRepository) { repositoriesXMLFile = PentahoSystem.getSystemSetting("kettle/settings.xml", "repositories.xml.file", null); //$NON-NLS-1$ //$NON-NLS-2$ repositoryName = PentahoSystem.getSystemSetting("kettle/settings.xml", "repository.name", null); //$NON-NLS-1$ //$NON-NLS-2$ username = PentahoSystem.getSystemSetting("kettle/settings.xml", "repository.userid", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ password = PentahoSystem.getSystemSetting("kettle/settings.xml", "repository.password", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ // Check the Kettle settings... if ("".equals(repositoryName) || username.equals("")) { //$NON-NLS-1$ //$NON-NLS-2$ // looks like the Kettle stuff is not configured yet... // see if we can provide feedback to the user... error(Messages.getInstance().getErrorString("Kettle.ERROR_0001_SERVER_SETTINGS_NOT_SET")); //$NON-NLS-1$ return false; } boolean ok = ((repositoryName != null) && (repositoryName.length() > 0)); ok = ok || ((username != null) && (username.length() > 0)); return ok; } return true; } @Override public boolean init() { return true; } private boolean checkMapping(Node name, Node mapping) { if(name == null){ error(Messages.getInstance().getErrorString("Kettle.ERROR_0031_NAME_ELEMENT_MISSING_FROM_MAPPING")); //$NON-NLS-1$ return false; } if(mapping == null){ error(Messages.getInstance().getErrorString("Kettle.ERROR_0032_MAPPING_ELEMENT_MISSING_FROM_MAPPING")); //$NON-NLS-1$ return false; } // Make sure the mapping field is available as an input if(!isDefinedInput(mapping.getText())){ error(Messages.getInstance().getErrorString("Kettle.ERROR_0033_MAPPING_NOT_FOUND_IN_ACTION_INPUTS", mapping.getText())); //$NON-NLS-1$ return false; } return true; } @SuppressWarnings("unchecked") @Override public boolean validateAction() { // If there are any mappings, validate their xml and values if(getComponentDefinition().selectNodes(PARAMETER_MAP_CMD_ARG + " | " + PARAMETER_MAP_VARIABLE + " | " + PARAMETER_MAP_PARAMETER).size() > 0){ //$NON-NLS-1$ //$NON-NLS-2$ Map<String, String> argumentMap = null; Node name = null, mapping = null; // Extract all mapping elements from component-definition and verify they have a 'name' and 'mapping' child element for(Node n : (List<Node>)getComponentDefinition().selectNodes(PARAMETER_MAP_CMD_ARG)){ name = n.selectSingleNode("name"); //$NON-NLS-1$ mapping = n.selectSingleNode("mapping"); //$NON-NLS-1$ if (checkMapping(name, mapping)) { if (argumentMap == null) { argumentMap = new HashMap<String, String>(); } argumentMap.put(name.getText(), applyInputsToFormat(getInputStringValue(mapping.getText()))); } else { return false; } } for(Node n : (List<Node>)getComponentDefinition().selectNodes(PARAMETER_MAP_VARIABLE)){ name = n.selectSingleNode("name"); //$NON-NLS-1$ mapping = n.selectSingleNode("mapping"); //$NON-NLS-1$ if (!checkMapping(name, mapping)) { return false; } } for(Node n : (List<Node>)getComponentDefinition().selectNodes(PARAMETER_MAP_PARAMETER)){ name = n.selectSingleNode("name"); //$NON-NLS-1$ mapping = n.selectSingleNode("mapping"); //$NON-NLS-1$ if (!checkMapping(name, mapping)) { return false; } } // Make sure all of the arguments are present, correctly labeled and that there are not more then 10 (currently supported by Kettle) if (argumentMap != null) { String val = null; for(int i = 1; i <= argumentMap.size(); i++){ val = argumentMap.get(Integer.toString(i)); if(val == null){ error(Messages.getInstance().getErrorString("Kettle.ERROR_0030_INVALID_ARGUMENT_MAPPING")); //$NON-NLS-1$ return false; } } } } if (isDefinedResource(KettleComponent.TRANSFORMFILE) || isDefinedResource(KettleComponent.JOBFILE)) { return true; } boolean useRepository = PentahoSystem .getSystemSetting("kettle/settings.xml", "repository.type", "files").equals("rdbms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ if (!useRepository) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0019_REPOSITORY_TYPE_FILES")); //$NON-NLS-1$ return false; } if (isDefinedInput(KettleComponent.DIRECTORY) && (isDefinedInput(KettleComponent.TRANSFORMATION) || isDefinedInput(KettleComponent.JOB))) { return true; } if (!isDefinedInput(KettleComponent.DIRECTORY)) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0002_DIR_OR_FILE__NOT_DEFINED", getActionName())); //$NON-NLS-1$ return false; } else { if (!isDefinedInput(KettleComponent.TRANSFORMATION)) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0003_TRANS_NOT_DEFINED", getActionName())); //$NON-NLS-1$ return false; } } return false; } /** * Execute the specified transformation in the chosen repository. */ @Override public boolean executeAction() { if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_START")); //$NON-NLS-1$ } TransMeta transMeta = null; JobMeta jobMeta = null; LogWriter logWriter = null; kettleUserAppender = LogWriter.createStringAppender(); try { if (isDefinedInput(KettleComponent.KETTLELOGLEVEL)) { String logLevel = getInputStringValue(KettleComponent.KETTLELOGLEVEL); if(logLevel.equalsIgnoreCase("basic")){ //$NON-NLS-1$ logWriter = LogWriter.getInstance("Kettle-pentaho", false, LogWriter.LOG_LEVEL_BASIC); //$NON-NLS-1$ } else if(logLevel.equalsIgnoreCase("detail")){ //$NON-NLS-1$ logWriter = LogWriter.getInstance("Kettle-pentaho", false, LogWriter.LOG_LEVEL_DETAILED); //$NON-NLS-1$ } else if(logLevel.equalsIgnoreCase("error")){ //$NON-NLS-1$ logWriter = LogWriter.getInstance("Kettle-pentaho", false, LogWriter.LOG_LEVEL_ERROR); //$NON-NLS-1$ } else if(logLevel.equalsIgnoreCase("debug")){ //$NON-NLS-1$ logWriter = LogWriter.getInstance("Kettle-pentaho", false, LogWriter.LOG_LEVEL_DEBUG); //$NON-NLS-1$ } else if(logLevel.equalsIgnoreCase("minimal")){ //$NON-NLS-1$ logWriter = LogWriter.getInstance("Kettle-pentaho", false, LogWriter.LOG_LEVEL_MINIMAL); //$NON-NLS-1$ } else if(logLevel.equalsIgnoreCase("rowlevel")){ //$NON-NLS-1$ logWriter = LogWriter.getInstance("Kettle-pentaho", false, LogWriter.LOG_LEVEL_ROWLEVEL); //$NON-NLS-1$ } else { error(Messages.getInstance().getErrorString("Kettle.ERROR_0024_BAD_LOGGING_LEVEL", logLevel)); //$NON-NLS-1$ logWriter = LogWriter.getInstance("Kettle-pentaho", false, LogWriter.LOG_LEVEL_BASIC); //$NON-NLS-1$ } } else { logWriter = LogWriter.getInstance("Kettle-pentaho", false, LogWriter.LOG_LEVEL_BASIC); //$NON-NLS-1$ } } catch (Exception e) { } // Build lists of parameters, variables and command line arguments Map<String, String> argumentMap = new HashMap<String, String>(); Map<String, String> variableMap = new HashMap<String, String>(); Map<String, String> parameterMap = new HashMap<String, String>(); for(Node n : (List<Node>)getComponentDefinition().selectNodes(PARAMETER_MAP_CMD_ARG)){ argumentMap.put(n.selectSingleNode("name").getText(), applyInputsToFormat(getInputStringValue(n.selectSingleNode("mapping").getText()))); //$NON-NLS-1$ //$NON-NLS-2$ } for(Node n : (List<Node>)getComponentDefinition().selectNodes(PARAMETER_MAP_VARIABLE)){ variableMap.put(n.selectSingleNode("name").getText(), applyInputsToFormat(getInputStringValue(n.selectSingleNode("mapping").getText()))); //$NON-NLS-1$ //$NON-NLS-2$ } for(Node n : (List<Node>)getComponentDefinition().selectNodes(PARAMETER_MAP_PARAMETER)){ parameterMap.put(n.selectSingleNode("name").getText(), applyInputsToFormat(getInputStringValue(n.selectSingleNode("mapping").getText()))); //$NON-NLS-1$ //$NON-NLS-2$ } String arguments[] = null; // If no mappings are provided, assume all inputs are command line arguments (This supports the legacy method) if(argumentMap.size() <= 0 && variableMap.size() <= 0 && parameterMap.size() <= 0){ // this use is now considered obsolete, as we prefer the action-sequence inputs since they // now maintain order boolean running = true; int index = 1; ArrayList<String> argumentList = new ArrayList<String>(); while (running) { if (isDefinedInput("parameter" + index)) { //$NON-NLS-1$ String value = null; String inputName = getInputStringValue("parameter" + index); //$NON-NLS-1$ // see if we have an input with this name if (isDefinedInput(inputName)) { value = getInputStringValue(inputName); } argumentList.add(value); } else { running = false; } index++; } // this is the preferred way to provide inputs to the KetteComponent, the order of inputs is now preserved Iterator inputNamesIter = getInputNames().iterator(); while (inputNamesIter.hasNext()) { String name = (String) inputNamesIter.next(); argumentList.add(getInputStringValue(name)); } arguments = (String[]) argumentList.toArray(new String[argumentList.size()]); } else { // Extract arguments from argumentMap (Throw an error if the sequential ordering is broken) arguments = new String[argumentMap.size()]; for(int i = 0; i < argumentMap.size(); i++){ arguments[i] = argumentMap.get(Integer.toString(i + 1)); // Mapping is 1 based to match Kettle UI if(arguments[i] == null){ error(Messages.getInstance().getErrorString("Kettle.ERROR_0030_INVALID_ARGUMENT_MAPPING")); //$NON-NLS-1$ } } } // initialize environment variables KettleSystemListener.environmentInit(getSession()); String solutionPath = PentahoSystem.getApplicationContext().getFileOutputPath(""); //$NON-NLS-1$ solutionPath = solutionPath.replaceAll("\\\\", "\\\\\\\\"); //$NON-NLS-1$ //$NON-NLS-2$ solutionPath = solutionPath.replaceAll("\\$", "\\\\\\$"); //$NON-NLS-1$ //$NON-NLS-2$ Repository repository = connectToRepository(logWriter); boolean result = false; logWriter.addAppender(kettleUserAppender); try { if (isDefinedInput(KettleComponent.DIRECTORY)) { String directoryName = getInputStringValue(KettleComponent.DIRECTORY); if (repository == null) { return false; } if (isDefinedInput(KettleComponent.TRANSFORMATION)) { String transformationName = getInputStringValue(KettleComponent.TRANSFORMATION); transMeta = loadTransformFromRepository(directoryName, transformationName, repository, logWriter); if (transMeta != null) { try { for(String key : parameterMap.keySet()){ transMeta.setParameterValue(key, parameterMap.get(key)); } for(String key : variableMap.keySet()){ transMeta.setVariable(key, variableMap.get(key)); } } catch (UnknownParamException e) { error(e.getMessage()); } transMeta.setArguments(arguments); } else { return false; } } else if (isDefinedInput(KettleComponent.JOB)) { String jobName = getInputStringValue(KettleComponent.JOB); jobMeta = loadJobFromRepository(directoryName, jobName, repository, logWriter); if (jobMeta != null) { try { for(String key : parameterMap.keySet()){ jobMeta.setParameterValue(key, parameterMap.get(key)); } for(String key : variableMap.keySet()){ jobMeta.setVariable(key, variableMap.get(key)); } } catch (UnknownParamException e) { error(e.getMessage()); } jobMeta.setArguments(arguments); } else { return false; } } } else if (isDefinedResource(KettleComponent.TRANSFORMFILE)) { IActionSequenceResource transformResource = getResource(KettleComponent.TRANSFORMFILE); String fileAddress = getActualFileName(transformResource); try { if (fileAddress != null) { // We have an actual loadable filesystem and file transMeta = new TransMeta(fileAddress, repository, true); transMeta.setFilename(fileAddress); } else { String jobXmlStr = getResourceAsString(getResource(KettleComponent.TRANSFORMFILE)); jobXmlStr = jobXmlStr.replaceAll("\\$\\{pentaho.solutionpath\\}", solutionPath); //$NON-NLS-1$ jobXmlStr = jobXmlStr.replaceAll("\\%\\%pentaho.solutionpath\\%\\%", solutionPath); //$NON-NLS-1$ org.w3c.dom.Document doc = XmlW3CHelper.getDomFromString(jobXmlStr); // create a tranformation from the document transMeta = new TransMeta(doc.getFirstChild(), repository); } } catch (Exception e) { error( Messages.getInstance().getErrorString("Kettle.ERROR_0015_BAD_RESOURCE", KettleComponent.TRANSFORMFILE, fileAddress), e); //$NON-NLS-1$ return false; } if (transMeta == null) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0015_BAD_RESOURCE", KettleComponent.TRANSFORMFILE, fileAddress)); //$NON-NLS-1$ debug(kettleUserAppender.getBuffer().toString()); return false; } else { // Don't forget to set the parameters here as well... try { for(String key : parameterMap.keySet()){ transMeta.setParameterValue(key, parameterMap.get(key)); } for(String key : variableMap.keySet()){ transMeta.setVariable(key, variableMap.get(key)); } } catch (UnknownParamException e) { error(e.getMessage()); } transMeta.setArguments(arguments); /* * We do not need to concatenate the solutionPath info as the fileAddress has the complete location of the file * from start to end. This is to resolve BISERVER-502. */ transMeta.setFilename(fileAddress); } } else if (isDefinedResource(KettleComponent.JOBFILE)) { String fileAddress = ""; //$NON-NLS-1$ try { fileAddress = getResource(KettleComponent.JOBFILE).getAddress(); String jobXmlStr = getResourceAsString(getResource(KettleComponent.JOBFILE)); // String jobXmlStr = XmlW3CHelper.getContentFromSolutionResource(fileAddress); jobXmlStr = jobXmlStr.replaceAll("\\$\\{pentaho.solutionpath\\}", solutionPath); //$NON-NLS-1$ jobXmlStr = jobXmlStr.replaceAll("\\%\\%pentaho.solutionpath\\%\\%", solutionPath); //$NON-NLS-1$ org.w3c.dom.Document doc = XmlW3CHelper.getDomFromString(jobXmlStr); if (doc == null) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0015_BAD_RESOURCE", KettleComponent.JOBFILE, fileAddress)); //$NON-NLS-1$ debug(kettleUserAppender.getBuffer().toString()); return false; } // create a job from the document try { repository = connectToRepository(logWriter); // if we get a valid repository its great, if not try it without jobMeta = new JobMeta(logWriter, doc.getFirstChild(), repository, null); jobMeta.setFilename(solutionPath + fileAddress); } catch (Exception e) { error(Messages.getInstance().getString("Kettle.ERROR_0023_NO_META"), e); //$NON-NLS-1$ } finally { if (repository != null) { if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_DISCONNECTING")); //$NON-NLS-1$ } repository.disconnect(); } } } catch (Exception e) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0015_BAD_RESOURCE", KettleComponent.JOBFILE, fileAddress), e); //$NON-NLS-1$ return false; } if (jobMeta == null) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0015_BAD_RESOURCE", KettleComponent.JOBFILE, fileAddress)); //$NON-NLS-1$ debug(kettleUserAppender.getBuffer().toString()); return false; } else { try { for(String key : parameterMap.keySet()){ jobMeta.setParameterValue(key, parameterMap.get(key)); } for(String key : variableMap.keySet()){ jobMeta.setVariable(key, variableMap.get(key)); } } catch (UnknownParamException e) { error(e.getMessage()); } jobMeta.setArguments(arguments); jobMeta.setFilename(solutionPath + fileAddress); } } // OK, we have the information, let's load and execute the // transformation or job if (transMeta != null) { result = executeTransformation(transMeta, logWriter); } if (jobMeta != null) { result = executeJob(jobMeta, repository, logWriter); } } finally { logWriter.removeAppender(kettleUserAppender); if (repository != null) { if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_DISCONNECTING")); //$NON-NLS-1$ } repository.disconnect(); } } if(isDefinedOutput(EXECUTION_LOG_OUTPUT)){ setOutputValue(EXECUTION_LOG_OUTPUT, executionLog); } if(isDefinedOutput(EXECUTION_STATUS_OUTPUT)){ setOutputValue(EXECUTION_STATUS_OUTPUT, executionStatus); } XMLHandlerCache.getInstance().clear(); return result; } private String getActualFileName(final IActionSequenceResource resource) { String fileAddress = null; // Is it a hardcoded path? if ((resource.getSourceType() == IActionSequenceResource.FILE_RESOURCE)) { fileAddress = resource.getAddress(); } // Is it a solution relative path? else if (resource.getSourceType() == IActionSequenceResource.SOLUTION_FILE_RESOURCE) { fileAddress = PentahoSystem.getApplicationContext().getSolutionPath(resource.getAddress()); } // Can it be loaded? this may not be true if using the DB Based repos if (fileAddress != null) { File file = new File(fileAddress); if (!file.exists() || !file.isFile()) { fileAddress = null; } } return (fileAddress); } protected boolean customizeTrans( Trans trans, LogWriter logWriter ) { // override this to customize the transformation before it runs // by default there is no transformation return true; } private boolean executeTransformation(final TransMeta transMeta, final LogWriter logWriter){ boolean success = true; Trans trans = null; try{ if (transMeta != null) { try { trans = new Trans(transMeta); } catch (Exception e) { throw new KettleComponentException(Messages.getInstance().getErrorString("Kettle.ERROR_0010_BAD_TRANSFORMATION_METADATA"), e); //$NON-NLS-1$ } } if (trans == null) { throw new KettleComponentException(Messages.getInstance().getErrorString("Kettle.ERROR_0010_BAD_TRANSFORMATION_METADATA")); //$NON-NLS-1$ } if (trans != null) { // OK, we have the transformation, now run it! if( !customizeTrans( trans, logWriter ) ) { throw new KettleComponentException(Messages.getInstance().getErrorString("Kettle.ERROR_0028_CUSTOMIZATION_FUNCITON_FAILED")); //$NON-NLS-1$ } debug(Messages.getInstance().getString("Kettle.DEBUG_PREPARING_TRANSFORMATION")); //$NON-NLS-1$ try { trans.prepareExecution(transMeta.getArguments()); } catch (Exception e) { throw new KettleComponentException(Messages.getInstance().getErrorString("Kettle.ERROR_0011_TRANSFORMATION_PREPARATION_FAILED"), e); //$NON-NLS-1$ } String stepName = null; String outputName = null; try { debug(Messages.getInstance().getString("Kettle.DEBUG_FINDING_STEP_IMPORTER")); //$NON-NLS-1$ stepName = getMonitorStepName(); outputName = getTransformSuccessOutputName(); if(outputName != null){ registerAsStepListener(stepName, trans); } } catch (Exception e) { throw new KettleComponentException(Messages.getInstance().getErrorString("Kettle.ERROR_0012_ROW_LISTENER_CREATE_FAILED"), e); //$NON-NLS-1$ } try { debug(Messages.getInstance().getString("Kettle.DEBUG_STARTING_TRANSFORMATION")); //$NON-NLS-1$ trans.startThreads(); } catch (Exception e) { throw new KettleComponentException(Messages.getInstance().getErrorString("Kettle.ERROR_0013_TRANSFORMATION_START_FAILED"), e); //$NON-NLS-1$ } try { // It's running in a separate thread to allow monitoring, etc. debug(Messages.getInstance().getString("Kettle.DEBUG_TRANSFORMATION_RUNNING")); //$NON-NLS-1$ trans.waitUntilFinished(); trans.endProcessing("end"); //$NON-NLS-1$ } catch (Exception e) { throw new KettleComponentException(Messages.getInstance().getErrorString("Kettle.ERROR_0014_ERROR_DURING_EXECUTE"), e); //$NON-NLS-1$ } // Dump the Kettle log... debug(kettleUserAppender.getBuffer().toString()); //Build written row output if(results != null){ if (outputName != null) { setOutputValue(outputName, results); } if(isDefinedOutput(TRANSFORM_SUCCESS_COUNT_OUTPUT)){ setOutputValue(TRANSFORM_SUCCESS_COUNT_OUTPUT, results.getRowCount()); } } //Build error row output if(errorResults != null){ if(isDefinedOutput(TRANSFORM_ERROR_OUTPUT)){ setOutputValue(TRANSFORM_ERROR_OUTPUT, errorResults); } if(isDefinedOutput(TRANSFORM_ERROR_COUNT_OUTPUT)){ setOutputValue(TRANSFORM_ERROR_COUNT_OUTPUT, errorResults.getRowCount()); } } } } catch (KettleComponentException e){ success = false; error(Messages.getInstance().getErrorString("Kettle.ERROR_0008_ERROR_RUNNING", e.toString()), e); //$NON-NLS-1$ } prepareKettleOutput(trans); //TODO: return success; } private boolean registerAsStepListener(String stepName, Trans trans) throws KettleComponentException{ boolean success = false; try{ if(trans != null){ List<StepMetaDataCombi> stepList = trans.getSteps(); // find the specified step for (StepMetaDataCombi step : stepList) { if (step.stepname.equals(stepName)) { if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_FOUND_STEP_IMPORTER")); //$NON-NLS-1$ } // this is the step we are looking for if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_GETTING_STEP_METADATA")); //$NON-NLS-1$ } RowMetaInterface row = trans.getTransMeta().getStepFields(stepName); // create the metadata that the Pentaho result sets need String fieldNames[] = row.getFieldNames(); String columns[][] = new String[1][fieldNames.length]; for (int column = 0; column < fieldNames.length; column++) { columns[0][column] = fieldNames[column]; } if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_CREATING_RESULTSET_METADATA")); //$NON-NLS-1$ } MemoryMetaData metaData = new MemoryMetaData(columns, null); results = new MemoryResultSet(metaData); errorResults = new MemoryResultSet(metaData); // add ourself as a row listener step.step.addRowListener(this); success = true; break; } } } } catch (Exception e){ throw new KettleComponentException(Messages.getInstance().getString("Kettle.ERROR_0027_ERROR_INIT_STEP",stepName), e); //$NON-NLS-1$ } return success; } private String getMonitorStepName(){ String result = null; // Supporting "importstep" for backwards compatibility if (isDefinedInput(KettleComponent.IMPORTSTEP)) { result = getInputStringValue(KettleComponent.IMPORTSTEP); } else if(isDefinedInput(KettleComponent.MONITORSTEP)){ result = getInputStringValue(KettleComponent.MONITORSTEP); } return result; } private String getTransformSuccessOutputName(){ String result = null; // Supporting "importstep" for backwards compatibility if (isDefinedInput(KettleComponent.IMPORTSTEP)) { if (getOutputNames().size() == 1) { result = (String) getOutputNames().iterator().next(); } else { // Need to find the name that does not match one of the predefined output parameters result = getUndefinedOutputParameter(getOutputNames().iterator()); if(result == null){ // Use the new TRANSFORM_SUCCESS_OUTPUT to send the output to, if it is present if(isDefinedOutput(TRANSFORM_SUCCESS_OUTPUT)){ result = TRANSFORM_SUCCESS_OUTPUT; } } } } else if(isDefinedOutput(TRANSFORM_SUCCESS_OUTPUT)){ result = TRANSFORM_SUCCESS_OUTPUT; } return result; } private void prepareKettleOutput(Trans trans){ extractKettleStatus(trans); extractKettleLog(); } private void prepareKettleOutput(Job job){ extractKettleStatus(job); extractKettleLog(); } private void extractKettleStatus(Trans trans){ if(trans != null) { executionStatus = trans.getStatus(); } else { executionStatus = Messages.getInstance().getErrorString("Kettle.ERROR_0025_TRANSFORMATION_NOT_LOADED"); //$NON-NLS-1$ } } private void extractKettleStatus(Job job){ if(job != null) { executionStatus = job.getStatus(); } else { executionStatus = Messages.getInstance().getErrorString("Kettle.ERROR_0026_JOB_NOT_LOADED"); //$NON-NLS-1$ } } private void extractKettleLog(){ executionLog = kettleUserAppender.getBuffer().toString(); } private String getUndefinedOutputParameter(Iterator<String> outputNames) { String tempName = null; while(outputNames.hasNext()){ tempName = (String) outputNames.next(); if(!outputParams.contains(tempName)){ //Found user defined named return(tempName); } } return null; } private boolean executeJob(final JobMeta jobMeta, final Repository repository, final LogWriter logWriter) { boolean success = true; Job job = null; try { if (jobMeta != null) { try { job = new Job(logWriter, StepLoader.getInstance(), repository, jobMeta); } catch (Exception e) { throw new KettleComponentException(Messages.getInstance().getErrorString("Kettle.ERROR_0021_BAD_JOB_METADATA"), e); //$NON-NLS-1$ } } if (job == null) { debug(kettleUserAppender.getBuffer().toString()); throw new KettleComponentException(Messages.getInstance().getErrorString("Kettle.ERROR_0021_BAD_JOB_METADATA")); //$NON-NLS-1$ } if (job != null) { try { if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_STARTING_JOB")); //$NON-NLS-1$ } job.start(); } catch (Exception e) { throw new KettleComponentException(Messages.getInstance().getErrorString("Kettle.ERROR_0022_JOB_START_FAILED"), e); //$NON-NLS-1$ } try { // It's running in a separate tread to allow monitoring, etc. if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_JOB_RUNNING")); //$NON-NLS-1$ } job.waitUntilFinished(5000000); job.endProcessing("end", job.getResult()); //$NON-NLS-1$ if (job.getResult().getNrErrors() > 0) { debug(kettleUserAppender.getBuffer().toString()); throw new KettleComponentException(Messages.getInstance().getErrorString("Kettle.ERROR_0014_ERROR_DURING_EXECUTE")); //$NON-NLS-1$ } } catch (Exception e) { throw new KettleComponentException(Messages.getInstance().getErrorString("Kettle.ERROR_0014_ERROR_DURING_EXECUTE"), e); //$NON-NLS-1$ } // Dump the Kettle log... debug(kettleUserAppender.getBuffer().toString()); } } catch (KettleComponentException e) { success = false; error(Messages.getInstance().getErrorString("Kettle.ERROR_0008_ERROR_RUNNING", e.toString()), e); //$NON-NLS-1$ } prepareKettleOutput(job); return success; } private TransMeta loadTransformFromRepository(final String directoryName, final String transformationName, final Repository repository, final LogWriter logWriter) { if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_DIRECTORY", directoryName)); //$NON-NLS-1$ } if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_TRANSFORMATION", transformationName)); //$NON-NLS-1$ } TransMeta transMeta = null; try { if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_FINDING_DIRECTORY")); //$NON-NLS-1$ } // Find the directory specified. RepositoryDirectory repositoryDirectory = null; try { repositoryDirectory = repository.getDirectoryTree().findDirectory(directoryName); } catch (Exception e) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0006_DIRECTORY_NOT_FOUND", directoryName), e); //$NON-NLS-1$ return null; } if (repositoryDirectory == null) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0006_DIRECTORY_NOT_FOUND", directoryName)); //$NON-NLS-1$ return null; } if (repositoryDirectory != null) { if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_GETTING_TRANSFORMATION_METADATA")); //$NON-NLS-1$ } try { // Load the transformation from the repository transMeta = new TransMeta(repository, transformationName, repositoryDirectory); } catch (Exception e) { error(Messages.getInstance().getErrorString( "Kettle.ERROR_0009_TRANSFROMATION_METADATA_NOT_FOUND", repositoryDirectory + "/" + transformationName), e); //$NON-NLS-1$ //$NON-NLS-2$ return null; } if (transMeta == null) { error(Messages.getInstance().getErrorString( "Kettle.ERROR_0009_TRANSFROMATION_METADATA_NOT_FOUND", repositoryDirectory + "/" + transformationName)); //$NON-NLS-1$ //$NON-NLS-2$ debug(kettleUserAppender.getBuffer().toString()); return null; } else { return transMeta; } } if (ComponentBase.debug) { debug(kettleUserAppender.getBuffer().toString()); // OK, close shop! } } catch (Exception e) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0008_ERROR_RUNNING", e.toString()), e); //$NON-NLS-1$ } return null; } private JobMeta loadJobFromRepository(final String directoryName, final String jobName, final Repository repository, final LogWriter logWriter) { if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_DIRECTORY", directoryName)); //$NON-NLS-1$ } if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_JOB", jobName)); //$NON-NLS-1$ } JobMeta jobMeta = null; try { if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_FINDING_DIRECTORY")); //$NON-NLS-1$ } // Find the directory specified. RepositoryDirectory repositoryDirectory = null; try { repositoryDirectory = repository.getDirectoryTree().findDirectory(directoryName); } catch (Exception e) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0006_DIRECTORY_NOT_FOUND", directoryName), e); //$NON-NLS-1$ return null; } if (repositoryDirectory == null) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0006_DIRECTORY_NOT_FOUND", directoryName)); //$NON-NLS-1$ return null; } if (repositoryDirectory != null) { if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_GETTING_JOB_METADATA")); //$NON-NLS-1$ } try { // Load the transformation from the repository jobMeta = new JobMeta(logWriter, repository, jobName, repositoryDirectory); } catch (Exception e) { error(Messages.getInstance() .getErrorString("Kettle.ERROR_0020_JOB_METADATA_NOT_FOUND", repositoryDirectory + "/" + jobName), e); //$NON-NLS-1$ //$NON-NLS-2$ return null; } if (jobMeta == null) { error(Messages.getInstance() .getErrorString("Kettle.ERROR_0020_JOB_METADATA_NOT_FOUND", repositoryDirectory + "/" + jobName)); //$NON-NLS-1$ //$NON-NLS-2$ debug(kettleUserAppender.getBuffer().toString()); return null; } else { return jobMeta; } } if (ComponentBase.debug) { debug(kettleUserAppender.getBuffer().toString()); // OK, close shop! } } catch (Exception e) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0008_ERROR_RUNNING", e.toString()), e); //$NON-NLS-1$ } return null; } private Repository connectToRepository(final LogWriter logWriter) { boolean useRepository = PentahoSystem .getSystemSetting("kettle/settings.xml", "repository.type", "files").equals("rdbms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ if (!useRepository) { return null; } try { if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_META_REPOSITORY")); //$NON-NLS-1$ } RepositoriesMeta repositoriesMeta = null; try { repositoriesMeta = new RepositoriesMeta(logWriter); } catch (Exception e) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0007_BAD_META_REPOSITORY"), e); //$NON-NLS-1$ return null; } if (repositoriesMeta == null) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0007_BAD_META_REPOSITORY")); //$NON-NLS-1$ debug(kettleUserAppender.getBuffer().toString()); return null; } if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_POPULATING_META")); //$NON-NLS-1$ } try { // TODO: add support for specified repositories.xml files... repositoriesMeta.readData(); // Read from the default $HOME/.kettle/repositories.xml file. } catch (Exception e) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0018_META_REPOSITORY_NOT_POPULATED"), e); //$NON-NLS-1$ return null; } if ((repositoriesXMLFile != null) && !"".equals(repositoriesXMLFile)) //$NON-NLS-1$ { error(Messages.getInstance().getErrorString("Kettle.ERROR_0017_XML_REPOSITORY_NOT_SUPPORTED")); //$NON-NLS-1$ debug(kettleUserAppender.getBuffer().toString()); return null; } if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_FINDING_REPOSITORY")); //$NON-NLS-1$ } // Find the specified repository. RepositoryMeta repositoryMeta = null; try { repositoryMeta = repositoriesMeta.findRepository(repositoryName); } catch (Exception e) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0004_REPOSITORY_NOT_FOUND", repositoryName), e); //$NON-NLS-1$ return null; } if (repositoryMeta == null) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0004_REPOSITORY_NOT_FOUND", repositoryName)); //$NON-NLS-1$ debug(kettleUserAppender.getBuffer().toString()); return null; } if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_GETTING_REPOSITORY")); //$NON-NLS-1$ } Repository repository = null; UserInfo userInfo = null; try { repository = new Repository(logWriter, repositoryMeta, userInfo); } catch (Exception e) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0016_COULD_NOT_GET_REPOSITORY_INSTANCE"), e); //$NON-NLS-1$ return null; } // OK, now try the username and password if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_CONNECTING")); //$NON-NLS-1$ } if (repository.connect(getClass().getName())) { try { userInfo = new UserInfo(repository, username, password); } catch (KettleException e) { userInfo = null; } finally { } } else { error(Messages.getInstance().getErrorString("Kettle.ERROR_0005_LOGIN_FAILED")); //$NON-NLS-1$ debug(kettleUserAppender.getBuffer().toString()); return null; } // OK, the repository is open and ready to use. if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_FINDING_DIRECTORY")); //$NON-NLS-1$ } return repository; } catch (Exception e) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0008_ERROR_RUNNING", e.toString()), e); //$NON-NLS-1$ } return null; } @Override public void done() { } public void rowReadEvent(final RowMetaInterface row, final Object[] values) { } public void rowWrittenEvent(final RowMetaInterface rowMeta, final Object[] row) throws KettleStepException { processRow(results, rowMeta, row); } public void errorRowWrittenEvent(final RowMetaInterface rowMeta, final Object[] row) throws KettleStepException { processRow(errorResults, rowMeta, row); } public void processRow(MemoryResultSet memResults, final RowMetaInterface rowMeta, final Object[] row) throws KettleStepException { if (memResults == null) { return; } try { Object pentahoRow[] = new Object[memResults.getColumnCount()]; for (int columnNo = 0; columnNo < memResults.getColumnCount(); columnNo++) { ValueMetaInterface valueMeta = rowMeta.getValueMeta(columnNo); switch (valueMeta.getType()) { case ValueMetaInterface.TYPE_BIGNUMBER: pentahoRow[columnNo] = rowMeta.getBigNumber(row, columnNo); break; case ValueMetaInterface.TYPE_BOOLEAN: pentahoRow[columnNo] = rowMeta.getBoolean(row, columnNo); break; case ValueMetaInterface.TYPE_DATE: pentahoRow[columnNo] = rowMeta.getDate(row, columnNo); break; case ValueMetaInterface.TYPE_INTEGER: pentahoRow[columnNo] = rowMeta.getInteger(row, columnNo); break; case ValueMetaInterface.TYPE_NONE: pentahoRow[columnNo] = rowMeta.getString(row, columnNo); break; case ValueMetaInterface.TYPE_NUMBER: pentahoRow[columnNo] = rowMeta.getNumber(row, columnNo); break; case ValueMetaInterface.TYPE_STRING: pentahoRow[columnNo] = rowMeta.getString(row, columnNo); break; default: pentahoRow[columnNo] = rowMeta.getString(row, columnNo); } } memResults.addRow(pentahoRow); } catch (KettleValueException e) { throw new KettleStepException(e); } } }