/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License, version 2 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/gpl-2.0.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 General Public License for more details.
*
*
* Copyright 2006 - 2013 Pentaho Corporation. All rights reserved.
*/
package org.pentaho.platform.engine.services.actionsequence;
import org.apache.commons.collections.map.ListOrderedMap;
import org.apache.commons.io.FilenameUtils;
import org.dom4j.Document;
import org.dom4j.Node;
import org.pentaho.commons.connection.memory.MemoryResultSet;
import org.pentaho.platform.api.engine.IActionParameter;
import org.pentaho.platform.api.engine.IActionSequence;
import org.pentaho.platform.api.engine.IActionSequenceResource;
import org.pentaho.platform.api.engine.IApplicationContext;
import org.pentaho.platform.api.engine.IConditionalExecution;
import org.pentaho.platform.api.engine.ILogger;
import org.pentaho.platform.api.engine.ISequenceDefinition;
import org.pentaho.platform.api.engine.ISolutionActionDefinition;
import org.pentaho.platform.api.repository2.unified.RepositoryFile;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import org.pentaho.platform.engine.services.messages.Messages;
import org.pentaho.platform.util.logging.Logger;
import org.pentaho.platform.util.xml.dom4j.XmlDom4JHelper;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class SequenceDefinition implements ISequenceDefinition {
private static final boolean debug = PentahoSystem.debug;
private int errorCode;
private String xactionPath;
private String version;
private String title;
private boolean isWebService;
private String cacheLevel;
private int loggingLevel;
private String description;
private String author;
private String help;
private String resultType;
private String iconPath;
private Map outputDefinitions;
private Map inputDefinitions;
private Map resourceDefinitions;
IApplicationContext applicationContext;
ISolutionActionDefinition[] actionDefinitions;
public static IActionSequence ActionSequenceFactory( final Document document, final String solutionPath,
final ILogger logger, final IApplicationContext applicationContext, final int loggingLevel ) {
// Check for a sequence document
Node sequenceDefinitionNode = document.selectSingleNode( "//action-sequence" ); //$NON-NLS-1$
if ( sequenceDefinitionNode == null ) {
logger.error( Messages.getInstance().getErrorString(
"SequenceDefinition.ERROR_0002_NO_ACTION_SEQUENCE_NODE", "", solutionPath, "" ) ); //$NON-NLS-1$
return null;
}
ISequenceDefinition seqDef =
new SequenceDefinition( sequenceDefinitionNode, solutionPath, logger, applicationContext );
Node actionNode = sequenceDefinitionNode.selectSingleNode( "actions" ); //$NON-NLS-1$
return ( SequenceDefinition.getNextLoopGroup( seqDef, actionNode, solutionPath, logger, loggingLevel ) );
}
private static IActionSequence getNextLoopGroup( final ISequenceDefinition seqDef, final Node actionsNode,
final String solutionPath, final ILogger logger, final int loggingLevel ) {
String loopParameterName = XmlDom4JHelper.getNodeText( "@loop-on", actionsNode ); //$NON-NLS-1$
boolean loopUsingPeek = "true".equalsIgnoreCase( XmlDom4JHelper.getNodeText( "@peek-only", actionsNode ) ); //$NON-NLS-1$ //$NON-NLS-2$
Node actionDefinitionNode;
ActionDefinition actionDefinition;
List actionDefinitionList = new ArrayList();
List nodeList = actionsNode.selectNodes( "*" ); //$NON-NLS-1$
Iterator actionDefinitionNodes = nodeList.iterator();
while ( actionDefinitionNodes.hasNext() ) {
actionDefinitionNode = (Node) actionDefinitionNodes.next();
if ( actionDefinitionNode.getName().equals( "actions" ) ) { //$NON-NLS-1$
actionDefinitionList.add( SequenceDefinition.getNextLoopGroup( seqDef, actionDefinitionNode, solutionPath,
logger, loggingLevel ) );
} else if ( actionDefinitionNode.getName().equals( "action-definition" ) ) { //$NON-NLS-1$
actionDefinition = new ActionDefinition( actionDefinitionNode, logger );
actionDefinition.setLoggingLevel( loggingLevel );
actionDefinitionList.add( actionDefinition );
}
}
// action sequences with 0 actions are valid, see: JIRA PLATFORM-837
IConditionalExecution conditionalExecution =
SequenceDefinition.parseConditionalExecution( actionsNode, logger, "condition" ); //$NON-NLS-1$
ActionSequence sequence = new ActionSequence( loopParameterName, seqDef, actionDefinitionList, loopUsingPeek );
sequence.setConditionalExecution( conditionalExecution );
return sequence;
}
private SequenceDefinition( final Node sequenceRootNode, final String solutionPath, final ILogger logger,
final IApplicationContext applicationContext ) {
// initialize this object from the contents of the xml
this.xactionPath = solutionPath;
this.applicationContext = applicationContext;
// get the descriptive entries
version = XmlDom4JHelper.getNodeText( "version", sequenceRootNode ); //$NON-NLS-1$
title = XmlDom4JHelper.getNodeText( "title", sequenceRootNode ); //$NON-NLS-1$
isWebService = "true".equals( XmlDom4JHelper.getNodeText( "web-service", sequenceRootNode ) ); //$NON-NLS-1$ //$NON-NLS-2$
loggingLevel = Logger.getLogLevel( XmlDom4JHelper.getNodeText( "logging-level", sequenceRootNode ) ); //$NON-NLS-1$
description = XmlDom4JHelper.getNodeText( "documentation/description", sequenceRootNode ); //$NON-NLS-1$
help = XmlDom4JHelper.getNodeText( "documentation/help", sequenceRootNode ); //$NON-NLS-1$
author = XmlDom4JHelper.getNodeText( "documentation/author", sequenceRootNode ); //$NON-NLS-1$
resultType = XmlDom4JHelper.getNodeText( "documentation/result-type", sequenceRootNode ); //$NON-NLS-1$
iconPath = XmlDom4JHelper.getNodeText( "documentation/icon", sequenceRootNode ); //$NON-NLS-1$
// get the input parameter definitions
inputDefinitions = new ListOrderedMap();
errorCode = SequenceDefinition.parseParameters( sequenceRootNode, logger, "inputs/*", inputDefinitions, null, true ); //$NON-NLS-1$
// get the ouput definitions
outputDefinitions = new ListOrderedMap();
errorCode =
SequenceDefinition.parseParameters( sequenceRootNode, logger, "outputs/*", outputDefinitions, null, false ); //$NON-NLS-1$
if ( errorCode != ISequenceDefinition.ACTION_SEQUENCE_DEFINITION_OK ) {
logger.info( Messages.getInstance().getString( "SequenceDefinition.INFO_OUTPUT_PARAMETERS_NOT_DEFINED" ) ); //$NON-NLS-1$
}
// get the resource definitions
errorCode = parseResourceDefinitions( sequenceRootNode, logger );
if ( errorCode != ISequenceDefinition.ACTION_SEQUENCE_DEFINITION_OK ) {
logger.info( Messages.getInstance().getString( "SequenceDefinition.INFO_RESOURCES_PARAMETERS_NOT_DEFINED" ) ); //$NON-NLS-1$
}
}
public String getVersion() {
return version;
}
public boolean isWebService() {
return isWebService;
}
public String getCacheLevel() {
return cacheLevel;
}
public int getErrorCode() {
return errorCode;
}
static IConditionalExecution parseConditionalExecution( final Node actionRootNode, final ILogger logger,
final String nodePath ) {
try {
Node condition = actionRootNode.selectSingleNode( nodePath );
if ( condition == null ) {
return null;
}
String script = condition.getText();
IConditionalExecution ce = PentahoSystem.get( IConditionalExecution.class, null );
ce.setScript( script );
return ce;
} catch ( Exception ex ) {
logger.error( Messages.getInstance().getErrorString( "SequenceDefinition.ERROR_0005_PARSING_PARAMETERS" ), ex ); //$NON-NLS-1$
}
return null;
}
static int parseParameters( final Node actionRootNode, final ILogger logger, final String nodePath,
final Map parameterMap, final Map mapTo, final boolean inputVar ) {
try {
List parameters = actionRootNode.selectNodes( nodePath );
// TODO create objects to represent the types
// TODO need source variable list
Iterator parametersIterator = parameters.iterator();
Node parameterNode;
String parameterName;
String parameterType;
ActionParameter parameter;
List variableNodes;
List variables;
Node variableNode;
Iterator variablesIterator;
String variableSource;
String variableName;
int variableIdx;
Object defaultValue = null;
while ( parametersIterator.hasNext() ) {
parameterNode = (Node) parametersIterator.next();
parameterName = parameterNode.getName();
parameterType = XmlDom4JHelper.getNodeText( "@type", parameterNode ); //$NON-NLS-1$
if ( mapTo != null ) {
mapTo.put( parameterName, XmlDom4JHelper.getNodeText( "@mapping", parameterNode, parameterName ) ); //$NON-NLS-1$
}
defaultValue = SequenceDefinition.getDefaultValue( parameterNode );
// get the list of sources for this parameter
variableNodes = parameterNode.selectNodes( ( inputVar ) ? "sources/*" : "destinations/*" ); //$NON-NLS-1$ //$NON-NLS-2$
variablesIterator = variableNodes.iterator();
variableIdx = 1;
variables = new ArrayList();
while ( variablesIterator.hasNext() ) {
variableNode = (Node) variablesIterator.next();
// try to resolve the parameter value for this
try {
variableSource = variableNode.getName();
variableName = variableNode.getText();
ActionParameterSource variable = new ActionParameterSource( variableSource, variableName );
if ( SequenceDefinition.debug ) {
logger.debug( Messages.getInstance().getString(
"SequenceDefinition.DEBUG_ADDING_SOURCE_FOR_PARAMETER", variableSource, parameterName ) ); //$NON-NLS-1$
}
variables.add( variable );
} catch ( Exception e ) {
logger
.error(
Messages
.getInstance()
.getErrorString(
"SequenceDefinition.ERROR_0004_VARIABLE_SOURCE_NOT_VALID", Integer.toString( variableIdx ), parameterName ), e ); //$NON-NLS-1$
}
variableIdx++;
}
if ( defaultValue != null ) {
if ( SequenceDefinition.debug ) {
logger.debug( Messages.getInstance().getString(
"SequenceDefinition.DEBUG_USING_DEFAULT_VALUE", defaultValue.toString(), parameterName ) ); //$NON-NLS-1$
}
}
boolean isOutputParameter =
Boolean.parseBoolean( XmlDom4JHelper.getNodeText( "@is-output-parameter", parameterNode, "true" ) ); //$NON-NLS-1$ //$NON-NLS-2$
parameter = new ActionParameter( parameterName, parameterType, null, variables, defaultValue );
parameter.setOutputParameter( isOutputParameter );
parameterMap.put( parameterName, parameter );
}
return ISequenceDefinition.ACTION_SEQUENCE_DEFINITION_OK;
} catch ( Exception e ) {
logger.error( Messages.getInstance().getErrorString( "SequenceDefinition.ERROR_0005_PARSING_PARAMETERS" ), e ); //$NON-NLS-1$
}
return ISequenceDefinition.ACTION_SEQUENCE_DEFINITION_INVALID_ACTION_DOC;
}
private int parseResourceDefinitions( final Node actionRootNode, final ILogger logger ) {
resourceDefinitions = new ListOrderedMap();
try {
List resources = actionRootNode.selectNodes( "resources/*" ); //$NON-NLS-1$
// TODO create objects to represent the types
// TODO need source variable list
Iterator resourcesIterator = resources.iterator();
Node resourceNode;
String resourceName;
String resourceTypeName;
String resourceMimeType;
int resourceType;
ActionSequenceResource resource;
Node typeNode, mimeNode;
while ( resourcesIterator.hasNext() ) {
resourceNode = (Node) resourcesIterator.next();
typeNode = resourceNode.selectSingleNode( "./*" ); //$NON-NLS-1$
if ( typeNode != null ) {
resourceName = resourceNode.getName();
resourceTypeName = typeNode.getName();
resourceType = ActionSequenceResource.getResourceType( resourceTypeName );
String resourceLocation = XmlDom4JHelper.getNodeText( "location", typeNode ); //$NON-NLS-1$
if ( ( resourceType == IActionSequenceResource.SOLUTION_FILE_RESOURCE )
|| ( resourceType == IActionSequenceResource.FILE_RESOURCE ) ) {
if ( resourceLocation == null ) {
logger.error( Messages.getInstance().getErrorString(
"SequenceDefinition.ERROR_0008_RESOURCE_NO_LOCATION", resourceName ) ); //$NON-NLS-1$
continue;
}
} else if ( resourceType == IActionSequenceResource.STRING ) {
resourceLocation = XmlDom4JHelper.getNodeText( "string", resourceNode ); //$NON-NLS-1$
} else if ( resourceType == IActionSequenceResource.XML ) {
//resourceLocation = XmlHelper.getNodeText("xml", resourceNode); //$NON-NLS-1$
Node xmlNode = typeNode.selectSingleNode( "./location/*" ); //$NON-NLS-1$
// Danger, we have now lost the character encoding of the XML in this node
// see BISERVER-895
resourceLocation = ( xmlNode == null ) ? "" : xmlNode.asXML(); //$NON-NLS-1$
}
mimeNode = typeNode.selectSingleNode( "mime-type" ); //$NON-NLS-1$
if ( mimeNode != null ) {
resourceMimeType = mimeNode.getText();
if ( ( resourceType == IActionSequenceResource.SOLUTION_FILE_RESOURCE )
|| ( resourceType == IActionSequenceResource.FILE_RESOURCE ) ) {
resourceLocation = FilenameUtils.separatorsToUnix( resourceLocation );
if ( !resourceLocation.startsWith( "/" ) ) { //$NON-NLS-1$
String parentDir = FilenameUtils.getFullPathNoEndSeparator( xactionPath );
if ( parentDir.length() == 0 ) {
parentDir = RepositoryFile.SEPARATOR;
}
resourceLocation = FilenameUtils.separatorsToUnix( FilenameUtils.concat( parentDir,
resourceLocation ) );
}
}
resource = new ActionSequenceResource( resourceName, resourceType, resourceMimeType, resourceLocation );
resourceDefinitions.put( resourceName, resource );
} else {
logger.error( Messages.getInstance().getErrorString(
"SequenceDefinition.ERROR_0007_RESOURCE_NO_MIME_TYPE", resourceName ) ); //$NON-NLS-1$
}
}
// input = new ActionParameter( resourceName, resourceType, null
// );
// resourceDefinitions.put( inputName, input );
}
return ISequenceDefinition.ACTION_SEQUENCE_DEFINITION_OK;
} catch ( Exception e ) {
logger.error( Messages.getInstance().getErrorString( "SequenceDefinition.ERROR_0006_PARSING_RESOURCE" ), e ); //$NON-NLS-1$
}
return ISequenceDefinition.ACTION_SEQUENCE_DEFINITION_INVALID_ACTION_DOC;
}
/**
* sbarkdull: method appears to never be used anywhere
*
* @param actionRootNode
* @param logger
* @param nodePath
* @param mapTo
* @return
*/
static int parseActionResourceDefinitions( final Node actionRootNode, final ILogger logger, final String nodePath,
final Map mapTo ) {
try {
List resources = actionRootNode.selectNodes( nodePath );
// TODO create objects to represent the types
// TODO need source variable list
Iterator resourcesIterator = resources.iterator();
Node resourceNode;
String resourceName;
while ( resourcesIterator.hasNext() ) {
resourceNode = (Node) resourcesIterator.next();
resourceName = resourceNode.getName();
if ( mapTo != null ) {
mapTo.put( resourceName, XmlDom4JHelper.getNodeText( "@mapping", resourceNode, resourceName ) ); //$NON-NLS-1$
}
}
return ISequenceDefinition.ACTION_SEQUENCE_DEFINITION_OK;
} catch ( Exception e ) {
logger.error( Messages.getInstance().getErrorString( "SequenceDefinition.ERROR_0006_PARSING_RESOURCE" ), e ); //$NON-NLS-1$
}
return ISequenceDefinition.ACTION_SEQUENCE_DEFINITION_INVALID_ACTION_DOC;
}
private static Object getDefaultValue( final Node parameterNode ) {
Node rootNode = parameterNode.selectSingleNode( "default-value" ); //$NON-NLS-1$
if ( rootNode == null ) {
return ( null );
}
String dataType = XmlDom4JHelper.getNodeText( "@type", rootNode ); //$NON-NLS-1$
if ( dataType == null ) {
dataType = XmlDom4JHelper.getNodeText( "@type", parameterNode ); //$NON-NLS-1$
}
if ( "string-list".equals( dataType ) ) { //$NON-NLS-1$
List nodes = rootNode.selectNodes( "list-item" ); //$NON-NLS-1$
if ( nodes == null ) {
return ( null );
}
ArrayList rtnList = new ArrayList();
for ( Iterator it = nodes.iterator(); it.hasNext(); ) {
rtnList.add( ( (Node) it.next() ).getText() );
}
return ( rtnList );
} else if ( "property-map-list".equals( dataType ) ) { //$NON-NLS-1$
List nodes = rootNode.selectNodes( "property-map" ); //$NON-NLS-1$
if ( nodes == null ) {
return ( null );
}
ArrayList rtnList = new ArrayList();
for ( Iterator it = nodes.iterator(); it.hasNext(); ) {
Node mapNode = (Node) it.next();
rtnList.add( SequenceDefinition.getMapFromNode( mapNode ) );
}
return ( rtnList );
} else if ( "property-map".equals( dataType ) ) { //$NON-NLS-1$
return ( SequenceDefinition.getMapFromNode( rootNode.selectSingleNode( "property-map" ) ) ); //$NON-NLS-1$
} else if ( "long".equals( dataType ) ) { //$NON-NLS-1$
try {
return ( new Long( rootNode.getText() ) );
} catch ( Exception e ) {
//ignore
}
return ( null );
} else if ( "result-set".equals( dataType ) ) { //$NON-NLS-1$
return ( MemoryResultSet.createFromActionSequenceInputsNode( parameterNode ) );
} else { // Assume String
return ( rootNode.getText() );
}
}
private static Map getMapFromNode( final Node mapNode ) {
Map rtnMap = new ListOrderedMap();
if ( mapNode != null ) {
List nodes = mapNode.selectNodes( "entry" ); //$NON-NLS-1$
if ( nodes != null ) {
for ( Iterator it = nodes.iterator(); it.hasNext(); ) {
Node entryNode = (Node) it.next();
rtnMap.put( XmlDom4JHelper.getNodeText( "@key", entryNode ), entryNode.getText() ); //$NON-NLS-1$
}
}
}
return ( rtnMap );
}
/*
* (non-Javadoc)
*
* @see org.pentaho.newcode.IActionDefinition#getParamDefs()
*/
public Map getInputDefinitions() {
return inputDefinitions;
}
public Map getInputDefinitionsForParameterProvider( final String parameterProviderName ) {
Map rtnMap = new ListOrderedMap();
Map paramList = getInputDefinitions();
for ( Iterator it = paramList.values().iterator(); it.hasNext(); ) {
IActionParameter actionParameter = (IActionParameter) it.next();
List vars = actionParameter.getVariables();
for ( int i = 0; i < vars.size(); i++ ) {
ActionParameterSource source = (ActionParameterSource) ( vars.get( i ) );
if ( source.getSourceName().equals( parameterProviderName ) ) {
rtnMap.put( source.getValue(), actionParameter );
}
}
}
return ( rtnMap );
}
/*
* (non-Javadoc)
*
* @see org.pentaho.newcode.IActionDefinition#getOutputDefs()
*/
public Map getOutputDefinitions() {
return outputDefinitions;
}
/*
* (non-Javadoc)
*
* @see org.pentaho.newcode.IActionDefinition#getResourceDefs()
*/
public Map getResourceDefinitions() {
return resourceDefinitions;
}
public String getSequenceName() {
return FilenameUtils.getName( xactionPath );
}
public String getAuthor() {
return author;
}
public String getDescription() {
return description;
}
public String getResultType() {
return resultType;
}
public String getHelp() {
return help;
}
public String getTitle() {
return title;
}
public String getSolutionName() {
return "";
}
public String getSolutionPath() {
return xactionPath;
}
public int getLoggingLevel() {
return ( loggingLevel );
}
public String getIcon() {
return iconPath;
}
}