Java tutorial
/*! * 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 (c) 2002-2013 Pentaho Corporation.. All rights reserved. */ package org.pentaho.platform.plugin.action.kettle; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang.StringUtils; 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.KettleLogStore; import org.pentaho.di.core.logging.LogChannel; import org.pentaho.di.core.logging.LogLevel; import org.pentaho.di.core.logging.LoggingObjectInterface; import org.pentaho.di.core.logging.LoggingRegistry; import org.pentaho.di.core.parameters.UnknownParamException; import org.pentaho.di.core.plugins.PluginRegistry; import org.pentaho.di.core.plugins.RepositoryPluginType; 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.RepositoryDirectoryInterface; import org.pentaho.di.repository.RepositoryMeta; 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.api.engine.ILogger; 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; 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; /** * 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; /** * The log channel ID of the executing transformation or job */ private String logChannelId; @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") //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ .equals("rdbms"); //$NON-NLS-1$ if (useRepository) { repositoriesXMLFile = PentahoSystem.getSystemSetting("kettle/settings.xml", "repositories.xml.file", //$NON-NLS-1$//$NON-NLS-2$ null); 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() { LogChannel kettleComponentChannel = new LogChannel("Kettle platform component"); logChannelId = kettleComponentChannel.getLogChannelId(); 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", //$NON-NLS-1$ mapping.getText())); 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) //$NON-NLS-1$//$NON-NLS-2$ .size() > 0) { 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") //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ .equals("rdbms"); //$NON-NLS-1$ 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", //$NON-NLS-1$ getActionName())); return false; } else { if (!isDefinedInput(KettleComponent.TRANSFORMATION)) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0003_TRANS_NOT_DEFINED", //$NON-NLS-1$ getActionName())); return false; } } return false; } /** * Execute the specified transformation in the chosen repository. */ @SuppressWarnings("unchecked") @Override public boolean executeAction() { if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_START")); //$NON-NLS-1$ } TransMeta transMeta = null; JobMeta jobMeta = null; // 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 try { KettleSystemListener.environmentInit(getSession()); } catch (KettleException ke) { error(ke.getMessage(), ke); } String solutionPath = "solution:"; Repository repository = connectToRepository(); boolean result = false; 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); 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); 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 if (repository != null && repository.isConnected()) { fileAddress = transformResource.getAddress(); // load transformation resource from kettle/settings.xml configured repository transMeta = loadTransformFromRepository(FilenameUtils.getPathNoEndSeparator(fileAddress), FilenameUtils.getBaseName(fileAddress), repository); } 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", //$NON-NLS-1$ KettleComponent.TRANSFORMFILE, fileAddress), e); return false; } /* * Unreachable code below... if (transMeta == null) { * error(Messages.getInstance().getErrorString("Kettle.ERROR_0015_BAD_RESOURCE", KettleComponent.TRANSFORMFILE, * fileAddress)); //$NON-NLS-1$ debug(getKettleLog(true)); return false; } */ // 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(); if (repository != null && repository.isConnected()) { solutionPath = StringUtils.EMPTY; // load job resource from kettle/settings.xml configured repository jobMeta = loadJobFromRepository(FilenameUtils.getPathNoEndSeparator(fileAddress), FilenameUtils.getBaseName(fileAddress), repository); } else { 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", //$NON-NLS-1$ KettleComponent.JOBFILE, fileAddress)); debug(getKettleLog(true)); return false; } // create a job from the document try { repository = connectToRepository(); // if we get a valid repository its great, if not try it // without jobMeta = new JobMeta(solutionPath + fileAddress, repository); } 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", //$NON-NLS-1$ KettleComponent.JOBFILE, fileAddress), e); return false; } if (jobMeta == null) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0015_BAD_RESOURCE", //$NON-NLS-1$ KettleComponent.JOBFILE, fileAddress)); debug(getKettleLog(true)); 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); } if (jobMeta != null) { result = executeJob(jobMeta, repository); } } finally { if (repository != null) { if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_DISCONNECTING")); //$NON-NLS-1$ } try { repository.disconnect(); } catch (Exception ignored) { //ignore } } if (transMeta != null) { try { cleanLogChannel(transMeta); transMeta.clear(); } catch (Exception ignored) { //ignore } transMeta = null; } if (jobMeta != null) { try { cleanLogChannel(jobMeta); jobMeta.clear(); } catch (Exception ignored) { //ignored } // Can't do anything about an exception here. jobMeta = null; } } 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 void cleanLogChannel(LoggingObjectInterface loi) { try { cleanLogChannelFromMap(loi); KettleLogStore.getAppender().removeChannelFromBuffer(loi.getLogChannelId()); } catch (Exception ignored) { //ignored } // Nothing I can do here... } private void cleanLogChannelFromMap(LoggingObjectInterface loi) { String logChannelId = loi.getLogChannelId(); Map<String, LoggingObjectInterface> logChannelMap = LoggingRegistry.getInstance().getMap(); if ((logChannelMap != null)) { List<String> logKids = LoggingRegistry.getInstance().getLogChannelChildren(logChannelId); if (logKids != null) { for (int i = 0; i < logKids.size(); i++) { logChannelMap.remove(logKids.get(i)); } } logChannelMap.remove(logChannelId); } } private String getActualFileName(final IActionSequenceResource resource) { String fileAddress = null; // Is it a hardcoded path? if ((resource.getSourceType() == IActionSequenceResource.FILE_RESOURCE)) { fileAddress = resource.getAddress(); } else if (resource.getSourceType() == IActionSequenceResource.SOLUTION_FILE_RESOURCE) { fileAddress = 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) { // override this to customize the transformation before it runs // by default there is no transformation return true; } private boolean executeTransformation(final TransMeta transMeta) { 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"), //$NON-NLS-1$ e); } } if (trans == null) { throw new KettleComponentException( Messages.getInstance().getErrorString("Kettle.ERROR_0010_BAD_TRANSFORMATION_METADATA")); //$NON-NLS-1$ } // Remember where to get our execution logging from // logChannelId = trans.getLogChannelId(); // OK, we have the transformation, now run it! if (!customizeTrans(trans)) { 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 { LogLevel lvl = getLogLevel(); trans.setLogLevel(lvl); 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(); cleanLogChannel(trans); trans.cleanup(); } catch (Exception e) { throw new KettleComponentException( Messages.getInstance().getErrorString("Kettle.ERROR_0014_ERROR_DURING_EXECUTE"), e); //$NON-NLS-1$ } // Dump the Kettle log... debug(getKettleLog(false)); // 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); 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 LogLevel getLogLevel() { if (isDefinedInput(KettleComponent.KETTLELOGLEVEL)) { String logLevelStr = getInputStringValue(KettleComponent.KETTLELOGLEVEL); try { return LogLevel.valueOf(logLevelStr); } catch (Exception ex) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0024_BAD_LOGGING_LEVEL", logLevelStr)); //$NON-NLS-1$ return LogLevel.BASIC; } } else { // If not defined in the component, translate // xaction logging level to PDI Logging Level switch (loggingLevel) { case ILogger.DEBUG: return LogLevel.DEBUG; case ILogger.ERROR: return LogLevel.ERROR; case ILogger.FATAL: return LogLevel.ERROR; case ILogger.INFO: return LogLevel.MINIMAL; case ILogger.WARN: return LogLevel.BASIC; case ILogger.TRACE: return LogLevel.ROWLEVEL; default: return LogLevel.BASIC; } } } @SuppressWarnings("unchecked") 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 String getKettleLog(boolean includeGeneral) { StringBuffer logText = KettleLogStore.getAppender().getBuffer(logChannelId, includeGeneral); return logText.toString(); } private void extractKettleLog() { executionLog = getKettleLog(false); } 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) { boolean success = true; Job job = null; try { if (jobMeta != null) { try { job = new Job(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(getKettleLog(true)); throw new KettleComponentException( Messages.getInstance().getErrorString("Kettle.ERROR_0021_BAD_JOB_METADATA")); //$NON-NLS-1$ } // Remember where to get our execution logging from // logChannelId = job.getLogChannelId(); try { if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_STARTING_JOB")); //$NON-NLS-1$ } LogLevel lvl = getLogLevel(); job.setLogLevel(lvl); 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(); if (job.getResult().getNrErrors() > 0) { debug(getKettleLog(true)); 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$ } finally { if (job != null) { cleanLogChannel(job); } } // Dump the Kettle log... debug(getKettleLog(false)); } 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) { 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$ } if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_GETTING_TRANSFORMATION_METADATA")); //$NON-NLS-1$ } try { // Load the transformation from the repository RepositoryDirectoryInterface repositoryDirectory = repository.loadRepositoryDirectoryTree() .findDirectory(directoryName); transMeta = repository.loadTransformation(transformationName, repositoryDirectory, null, true, null); } catch (Exception e) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0009_TRANSFROMATION_METADATA_NOT_FOUND", //$NON-NLS-1$ directoryName + "/" + transformationName), e); //$NON-NLS-1$ return null; } if (transMeta == null) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0009_TRANSFROMATION_METADATA_NOT_FOUND", //$NON-NLS-1$ directoryName + "/" + transformationName)); //$NON-NLS-1$ debug(getKettleLog(true)); return null; } else { return transMeta; } } 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) { 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$ } if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_GETTING_JOB_METADATA")); //$NON-NLS-1$ } try { // Load the job from the repository RepositoryDirectoryInterface repositoryDirectory = repository.loadRepositoryDirectoryTree() .findDirectory(directoryName); jobMeta = repository.loadJob(jobName, repositoryDirectory, null, null); } catch (Exception e) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0020_JOB_METADATA_NOT_FOUND", //$NON-NLS-1$ directoryName + "/" + jobName), e); //$NON-NLS-1$ return null; } if (jobMeta == null) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0020_JOB_METADATA_NOT_FOUND", //$NON-NLS-1$ directoryName + "/" + jobName)); //$NON-NLS-1$ debug(getKettleLog(true)); return null; } else { return jobMeta; } } catch (Exception e) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0008_ERROR_RUNNING", e.toString()), e); //$NON-NLS-1$ } return null; } private Repository connectToRepository() { boolean useRepository = PentahoSystem.getSystemSetting("kettle/settings.xml", "repository.type", "files") //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ .equals("rdbms"); //$NON-NLS-1$ 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(); } catch (Exception e) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0007_BAD_META_REPOSITORY"), e); //$NON-NLS-1$ return null; } /* * Unreachable code below if (repositoriesMeta == null) { * error(Messages.getInstance().getErrorString("Kettle.ERROR_0007_BAD_META_REPOSITORY")); //$NON-NLS-1$ * debug(getKettleLog(true)); 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(getKettleLog(true)); 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", //$NON-NLS-1$ repositoryName), e); return null; } if (repositoryMeta == null) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0004_REPOSITORY_NOT_FOUND", //$NON-NLS-1$ repositoryName)); debug(getKettleLog(true)); return null; } if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_GETTING_REPOSITORY")); //$NON-NLS-1$ } Repository repository = null; try { repository = PluginRegistry.getInstance().loadClass(RepositoryPluginType.class, repositoryMeta.getId(), Repository.class); repository.init(repositoryMeta); } catch (Exception e) { error(Messages.getInstance().getErrorString("Kettle.ERROR_0016_COULD_NOT_GET_REPOSITORY_INSTANCE"), //$NON-NLS-1$ e); return null; } // OK, now try the username and password if (ComponentBase.debug) { debug(Messages.getInstance().getString("Kettle.DEBUG_CONNECTING")); //$NON-NLS-1$ } repository.connect(username, password); // 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); } } }