/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2016 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package org.pentaho.di.repository.kdr.delegates;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.pentaho.di.core.util.Utils;
import org.pentaho.di.core.RowMetaAndData;
import org.pentaho.di.core.database.DatabaseMeta;
import org.pentaho.di.core.exception.KettleDatabaseException;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.gui.Point;
import org.pentaho.di.core.plugins.PluginInterface;
import org.pentaho.di.core.plugins.PluginRegistry;
import org.pentaho.di.core.plugins.StepPluginType;
import org.pentaho.di.core.row.RowMetaInterface;
import org.pentaho.di.core.row.value.ValueMetaBoolean;
import org.pentaho.di.core.row.value.ValueMetaInteger;
import org.pentaho.di.core.row.value.ValueMetaString;
import org.pentaho.di.core.variables.VariableSpace;
import org.pentaho.di.i18n.BaseMessages;
import org.pentaho.di.partition.PartitionSchema;
import org.pentaho.di.repository.LongObjectId;
import org.pentaho.di.repository.ObjectId;
import org.pentaho.di.repository.kdr.KettleDatabaseRepository;
import org.pentaho.di.trans.step.RowDistributionInterface;
import org.pentaho.di.trans.step.RowDistributionPluginType;
import org.pentaho.di.trans.step.StepErrorMeta;
import org.pentaho.di.trans.step.StepMeta;
import org.pentaho.di.trans.step.StepMetaInterface;
import org.pentaho.di.trans.step.StepPartitioningMeta;
import org.pentaho.di.trans.steps.missing.MissingTrans;
public class KettleDatabaseRepositoryStepDelegate extends KettleDatabaseRepositoryBaseDelegate {
private static Class<?> PKG = StepMeta.class; // for i18n purposes, needed by Translator2!!
public static final String STEP_ATTRIBUTE_PREFIX = "_ATTR_" + '\t';
public KettleDatabaseRepositoryStepDelegate( KettleDatabaseRepository repository ) {
super( repository );
}
public synchronized ObjectId getStepTypeID( String code ) throws KettleException {
return repository.connectionDelegate.getIDWithValue(
quoteTable( KettleDatabaseRepository.TABLE_R_STEP_TYPE ),
quote( KettleDatabaseRepository.FIELD_STEP_TYPE_ID_STEP_TYPE ),
quote( KettleDatabaseRepository.FIELD_STEP_TYPE_CODE ), code );
}
public ObjectId[] getStepTypeIDs( String[] codes, int amount ) throws KettleException {
if ( amount != codes.length ) {
String[] tmp = new String[ amount ];
System.arraycopy( codes, 0, tmp, 0, amount );
codes = tmp;
}
return repository.connectionDelegate.getIDsWithValues(
quoteTable( KettleDatabaseRepository.TABLE_R_STEP_TYPE ),
quote( KettleDatabaseRepository.FIELD_STEP_TYPE_ID_STEP_TYPE ),
quote( KettleDatabaseRepository.FIELD_STEP_TYPE_CODE ), codes );
}
public Map<String, LongObjectId> getStepTypeCodeToIdMap() throws KettleException {
return repository.connectionDelegate.getValueToIdMap(
quoteTable( KettleDatabaseRepository.TABLE_R_STEP_TYPE ),
quote( KettleDatabaseRepository.FIELD_STEP_TYPE_ID_STEP_TYPE ),
quote( KettleDatabaseRepository.FIELD_STEP_TYPE_CODE ) );
}
public synchronized ObjectId getStepID( String name, ObjectId id_transformation ) throws KettleException {
return repository.connectionDelegate.getIDWithValue(
quoteTable( KettleDatabaseRepository.TABLE_R_STEP ), quote( KettleDatabaseRepository.FIELD_STEP_ID_STEP ),
quote( KettleDatabaseRepository.FIELD_STEP_NAME ), name,
quote( KettleDatabaseRepository.FIELD_STEP_ID_TRANSFORMATION ), id_transformation );
}
public synchronized String getStepTypeCode( ObjectId id_database_type ) throws KettleException {
return repository.connectionDelegate.getStringWithID(
quoteTable( KettleDatabaseRepository.TABLE_R_STEP_TYPE ),
quote( KettleDatabaseRepository.FIELD_STEP_TYPE_ID_STEP_TYPE ), id_database_type,
quote( KettleDatabaseRepository.FIELD_STEP_TYPE_CODE ) );
}
public RowMetaAndData getStep( ObjectId id_step ) throws KettleException {
return repository.connectionDelegate.getOneRow(
quoteTable( KettleDatabaseRepository.TABLE_R_STEP ), quote( KettleDatabaseRepository.FIELD_STEP_ID_STEP ),
id_step );
}
public RowMetaAndData getStepType( ObjectId id_step_type ) throws KettleException {
return repository.connectionDelegate.getOneRow(
quoteTable( KettleDatabaseRepository.TABLE_R_STEP_TYPE ),
quote( KettleDatabaseRepository.FIELD_STEP_TYPE_ID_STEP_TYPE ), id_step_type );
}
public RowMetaAndData getStepAttribute( ObjectId id_step_attribute ) throws KettleException {
return repository.connectionDelegate.getOneRow(
quoteTable( KettleDatabaseRepository.TABLE_R_STEP_ATTRIBUTE ),
quote( KettleDatabaseRepository.FIELD_STEP_ATTRIBUTE_ID_STEP_ATTRIBUTE ), id_step_attribute );
}
/**
* Create a new step by loading the metadata from the specified repository.
*
* @param rep
* @param stepId
* @param databases
* @param counters
* @param partitionSchemas
* @throws KettleException
*/
public StepMeta loadStepMeta( ObjectId stepId, List<DatabaseMeta> databases,
List<PartitionSchema> partitionSchemas ) throws KettleException {
StepMeta stepMeta = new StepMeta();
PluginRegistry registry = PluginRegistry.getInstance();
try {
RowMetaAndData r = getStep( stepId );
if ( r != null ) {
stepMeta.setObjectId( stepId );
stepMeta.setName( r.getString( KettleDatabaseRepository.FIELD_STEP_NAME, null ) );
stepMeta.setDescription( r.getString( KettleDatabaseRepository.FIELD_STEP_DESCRIPTION, null ) );
long id_step_type = r.getInteger( KettleDatabaseRepository.FIELD_STEP_ID_STEP_TYPE, -1L );
RowMetaAndData steptyperow = getStepType( new LongObjectId( id_step_type ) );
stepMeta.setStepID( steptyperow.getString( KettleDatabaseRepository.FIELD_STEP_TYPE_CODE, null ) );
stepMeta.setDistributes( r.getBoolean( KettleDatabaseRepository.FIELD_STEP_DISTRIBUTE, true ) );
int copies = (int) r.getInteger( KettleDatabaseRepository.FIELD_STEP_COPIES, 1 );
String copiesString = r.getString( KettleDatabaseRepository.FIELD_STEP_COPIES_STRING, null );
if ( !Utils.isEmpty( copiesString ) ) {
stepMeta.setCopiesString( copiesString );
} else {
stepMeta.setCopies( copies );
}
int x = (int) r.getInteger( KettleDatabaseRepository.FIELD_STEP_GUI_LOCATION_X, 0 );
int y = (int) r.getInteger( KettleDatabaseRepository.FIELD_STEP_GUI_LOCATION_Y, 0 );
stepMeta.setLocation( new Point( x, y ) );
stepMeta.setDraw( r.getBoolean( KettleDatabaseRepository.FIELD_STEP_GUI_DRAW, false ) );
// Generate the appropriate class...
PluginInterface sp = registry.findPluginWithId( StepPluginType.class, stepMeta.getStepID() );
if ( sp == null ) {
stepMeta.setStepMetaInterface( new MissingTrans( stepMeta.getName(), stepMeta.getStepID() ) );
} else {
stepMeta.setStepMetaInterface( (StepMetaInterface) registry.loadClass( sp ) );
}
if ( stepMeta.getStepMetaInterface() != null ) {
// Read the step info from the repository!
readRepCompatibleStepMeta(
stepMeta.getStepMetaInterface(), repository, stepMeta.getObjectId(), databases );
stepMeta.getStepMetaInterface().readRep(
repository, repository.metaStore, stepMeta.getObjectId(), databases );
}
// Get the partitioning as well...
//
stepMeta.setStepPartitioningMeta( loadStepPartitioningMeta( stepMeta.getObjectId() ) );
stepMeta.getStepPartitioningMeta().setPartitionSchemaAfterLoading( partitionSchemas );
// Get the cluster schema name
//
stepMeta.setClusterSchemaName( repository.getStepAttributeString( stepId, "cluster_schema" ) );
// Are we using a custom row distribution plugin?
//
String rowDistributionCode = repository.getStepAttributeString( stepId, 0, "row_distribution_code" );
RowDistributionInterface rowDistribution =
PluginRegistry.getInstance().loadClass(
RowDistributionPluginType.class, rowDistributionCode, RowDistributionInterface.class );
stepMeta.setRowDistribution( rowDistribution );
// Load the attribute groups map
//
stepMeta.setAttributesMap( loadStepAttributesMap( stepId ) );
// Done!
//
return stepMeta;
} else {
throw new KettleException( BaseMessages.getString(
PKG, "StepMeta.Exception.StepInfoCouldNotBeFound", String.valueOf( stepId ) ) );
}
} catch ( KettleDatabaseException dbe ) {
throw new KettleException( BaseMessages.getString( PKG, "StepMeta.Exception.StepCouldNotBeLoaded", String
.valueOf( stepMeta.getObjectId() ) ), dbe );
}
}
/**
* Compatible loading of metadata for v4 style plugins using deprecated methods.
*
* @param stepMetaInterface
* @param repository
* @param objectId
* @param databases
* @throws KettleException
*/
@SuppressWarnings( "deprecation" )
private void readRepCompatibleStepMeta( StepMetaInterface stepMetaInterface,
KettleDatabaseRepository repository, ObjectId objectId, List<DatabaseMeta> databases ) throws KettleException {
stepMetaInterface.readRep( repository, objectId, databases, null );
}
public void saveStepMeta( StepMeta stepMeta, ObjectId transformationId ) throws KettleException {
try {
log.logDebug( BaseMessages.getString( PKG, "StepMeta.Log.SaveNewStep" ) );
// Insert new Step in repository
stepMeta.setObjectId( insertStep(
transformationId, stepMeta.getName(), stepMeta.getDescription(), stepMeta.getStepID(), stepMeta
.isDistributes(), stepMeta.getCopies(), stepMeta.getLocation() == null
? -1 : stepMeta.getLocation().x, stepMeta.getLocation() == null ? -1 : stepMeta.getLocation().y,
stepMeta.isDrawn(), stepMeta.getCopiesString() ) );
// Save partitioning selection for the step
//
repository.stepDelegate.saveStepPartitioningMeta(
stepMeta.getStepPartitioningMeta(), transformationId, stepMeta.getObjectId() );
// The id_step is known, as well as the id_transformation
// This means we can now save the attributes of the step...
//
log.logDebug( BaseMessages.getString( PKG, "StepMeta.Log.SaveStepDetails" ) );
compatibleSaveRep( stepMeta.getStepMetaInterface(), repository, transformationId, stepMeta.getObjectId() );
stepMeta.getStepMetaInterface().saveRep(
repository, repository.metaStore, transformationId, stepMeta.getObjectId() );
// Save the name of the clustering schema that was chosen.
//
repository.saveStepAttribute( transformationId, stepMeta.getObjectId(), "cluster_schema", stepMeta
.getClusterSchema() == null ? "" : stepMeta.getClusterSchema().getName() );
// Save the row distribution code (plugin ID)
//
repository.saveStepAttribute( transformationId, stepMeta.getObjectId(), "row_distribution_code", stepMeta
.getRowDistribution() == null ? null : stepMeta.getRowDistribution().getCode() );
// Save the attribute groups map
//
saveAttributesMap( transformationId, stepMeta.getObjectId(), stepMeta.getAttributesMap() );
} catch ( KettleException e ) {
throw new KettleException( BaseMessages.getString( PKG, "StepMeta.Exception.UnableToSaveStepInfo", String
.valueOf( transformationId ) ), e );
}
}
@SuppressWarnings( "deprecation" )
private void compatibleSaveRep( StepMetaInterface stepMetaInterface, KettleDatabaseRepository repository,
ObjectId id_transformation, ObjectId objectId ) throws KettleException {
stepMetaInterface.saveRep( repository, id_transformation, objectId );
}
public void saveStepErrorMeta( StepErrorMeta meta, ObjectId id_transformation, ObjectId id_step ) throws KettleException {
repository.saveStepAttribute( id_transformation, id_step, "step_error_handling_source_step", meta
.getSourceStep() != null ? meta.getSourceStep().getName() : "" );
repository.saveStepAttribute( id_transformation, id_step, "step_error_handling_target_step", meta
.getTargetStep() != null ? meta.getTargetStep().getName() : "" );
repository.saveStepAttribute( id_transformation, id_step, "step_error_handling_is_enabled", meta.isEnabled() );
repository.saveStepAttribute( id_transformation, id_step, "step_error_handling_nr_valuename", meta
.getNrErrorsValuename() );
repository.saveStepAttribute( id_transformation, id_step, "step_error_handling_descriptions_valuename", meta
.getErrorDescriptionsValuename() );
repository.saveStepAttribute( id_transformation, id_step, "step_error_handling_fields_valuename", meta
.getErrorFieldsValuename() );
repository.saveStepAttribute( id_transformation, id_step, "step_error_handling_codes_valuename", meta
.getErrorCodesValuename() );
repository.saveStepAttribute( id_transformation, id_step, "step_error_handling_max_errors", meta
.getMaxErrors() );
repository.saveStepAttribute( id_transformation, id_step, "step_error_handling_max_pct_errors", meta
.getMaxPercentErrors() );
repository.saveStepAttribute( id_transformation, id_step, "step_error_handling_min_pct_rows", meta
.getMinPercentRows() );
}
public StepErrorMeta loadStepErrorMeta( VariableSpace variables, StepMeta stepMeta, List<StepMeta> steps ) throws KettleException {
StepErrorMeta meta = new StepErrorMeta( variables, stepMeta );
meta.setTargetStep( StepMeta.findStep( steps, repository.getStepAttributeString(
stepMeta.getObjectId(), "step_error_handling_target_step" ) ) );
meta
.setEnabled( repository.getStepAttributeBoolean( stepMeta.getObjectId(), "step_error_handling_is_enabled" ) );
meta.setNrErrorsValuename( repository.getStepAttributeString(
stepMeta.getObjectId(), "step_error_handling_nr_valuename" ) );
meta.setErrorDescriptionsValuename( repository.getStepAttributeString(
stepMeta.getObjectId(), "step_error_handling_descriptions_valuename" ) );
meta.setErrorFieldsValuename( repository.getStepAttributeString(
stepMeta.getObjectId(), "step_error_handling_fields_valuename" ) );
meta.setErrorCodesValuename( repository.getStepAttributeString(
stepMeta.getObjectId(), "step_error_handling_codes_valuename" ) );
meta.setMaxErrors( repository
.getStepAttributeString( stepMeta.getObjectId(), "step_error_handling_max_errors" ) );
meta.setMaxPercentErrors( repository.getStepAttributeString(
stepMeta.getObjectId(), "step_error_handling_max_pct_errors" ) );
meta.setMinPercentRows( repository.getStepAttributeString(
stepMeta.getObjectId(), "step_error_handling_min_pct_rows" ) );
return meta;
}
public StepPartitioningMeta loadStepPartitioningMeta( ObjectId id_step ) throws KettleException {
StepPartitioningMeta stepPartitioningMeta = new StepPartitioningMeta();
stepPartitioningMeta.setPartitionSchemaName( repository
.getStepAttributeString( id_step, "PARTITIONING_SCHEMA" ) );
String methodCode = repository.getStepAttributeString( id_step, "PARTITIONING_METHOD" );
stepPartitioningMeta.setMethod( StepPartitioningMeta.getMethod( methodCode ) );
if ( stepPartitioningMeta.getPartitioner() != null ) {
stepPartitioningMeta.getPartitioner().loadRep( repository, id_step );
}
stepPartitioningMeta.hasChanged( true );
return stepPartitioningMeta;
}
/**
* Saves partitioning properties in the repository for the given step.
*
* @param meta
* the partitioning metadata to store.
* @param id_transformation
* the ID of the transformation
* @param id_step
* the ID of the step
* @throws KettleDatabaseException
* In case anything goes wrong
*
*/
public void saveStepPartitioningMeta( StepPartitioningMeta meta, ObjectId id_transformation, ObjectId id_step ) throws KettleException {
repository.saveStepAttribute(
id_transformation, id_step, "PARTITIONING_SCHEMA", meta.getPartitionSchema() != null ? meta
.getPartitionSchema().getName() : "" ); // selected schema
repository.saveStepAttribute( id_transformation, id_step, "PARTITIONING_METHOD", meta.getMethodCode() );
if ( meta.getPartitioner() != null ) {
meta.getPartitioner().saveRep( repository, id_transformation, id_step );
}
}
//CHECKSTYLE:LineLength:OFF
public synchronized ObjectId insertStep( ObjectId id_transformation, String name, String description,
String steptype, boolean distribute, long copies, long gui_location_x, long gui_location_y,
boolean gui_draw, String copiesString ) throws KettleException {
ObjectId id = repository.connectionDelegate.getNextStepID();
ObjectId id_step_type = getStepTypeID( steptype );
RowMetaAndData table = new RowMetaAndData();
table.addValue( new ValueMetaInteger( KettleDatabaseRepository.FIELD_STEP_ID_STEP ), id );
table.addValue( new ValueMetaInteger( KettleDatabaseRepository.FIELD_STEP_ID_TRANSFORMATION ), id_transformation );
table.addValue( new ValueMetaString( KettleDatabaseRepository.FIELD_STEP_NAME ), name );
table.addValue( new ValueMetaString( KettleDatabaseRepository.FIELD_STEP_DESCRIPTION ), description );
table.addValue( new ValueMetaInteger( KettleDatabaseRepository.FIELD_STEP_ID_STEP_TYPE ), id_step_type );
table.addValue( new ValueMetaBoolean( KettleDatabaseRepository.FIELD_STEP_DISTRIBUTE ), Boolean.valueOf( distribute ) );
table.addValue( new ValueMetaInteger( KettleDatabaseRepository.FIELD_STEP_COPIES ), new Long( copies ) );
table.addValue( new ValueMetaInteger( KettleDatabaseRepository.FIELD_STEP_GUI_LOCATION_X ), new Long( gui_location_x ) );
table.addValue( new ValueMetaInteger( KettleDatabaseRepository.FIELD_STEP_GUI_LOCATION_Y ), new Long( gui_location_y ) );
table.addValue( new ValueMetaBoolean( KettleDatabaseRepository.FIELD_STEP_GUI_DRAW ), Boolean.valueOf( gui_draw ) );
table.addValue( new ValueMetaString( KettleDatabaseRepository.FIELD_STEP_COPIES_STRING ), copiesString );
repository.connectionDelegate.getDatabase().prepareInsert( table.getRowMeta(), KettleDatabaseRepository.TABLE_R_STEP );
repository.connectionDelegate.getDatabase().setValuesInsert( table );
repository.connectionDelegate.getDatabase().insertRow();
repository.connectionDelegate.getDatabase().closeInsert();
return id;
}
public synchronized int getNrSteps( ObjectId id_transformation ) throws KettleException {
int retval = 0;
RowMetaAndData par = repository.connectionDelegate.getParameterMetaData( id_transformation );
String sql =
"SELECT COUNT(*) FROM "
+ quoteTable( KettleDatabaseRepository.TABLE_R_STEP ) + " WHERE "
+ quote( KettleDatabaseRepository.FIELD_STEP_ID_TRANSFORMATION ) + " = ? ";
RowMetaAndData r = repository.connectionDelegate.getOneRow( sql, par.getRowMeta(), par.getData() );
if ( r != null ) {
retval = (int) r.getInteger( 0, 0L );
}
return retval;
}
public synchronized int getNrStepAttributes( ObjectId id_step ) throws KettleException {
int retval = 0;
RowMetaAndData par = repository.connectionDelegate.getParameterMetaData( id_step );
String sql =
"SELECT COUNT(*) FROM "
+ quoteTable( KettleDatabaseRepository.TABLE_R_STEP_ATTRIBUTE ) + " WHERE "
+ quote( KettleDatabaseRepository.FIELD_STEP_ATTRIBUTE_ID_STEP ) + " = ? ";
RowMetaAndData r = repository.connectionDelegate.getOneRow( sql, par.getRowMeta(), par.getData() );
if ( r != null ) {
retval = (int) r.getInteger( 0, 0L );
}
return retval;
}
private void saveAttributesMap( ObjectId transformationId, ObjectId stepId,
Map<String, Map<String, String>> attributesMap ) throws KettleException {
for ( final String groupName : attributesMap.keySet() ) {
Map<String, String> attributes = attributesMap.get( groupName );
for ( final String key : attributes.keySet() ) {
final String value = attributes.get( key );
if ( key != null && value != null ) {
repository.connectionDelegate.insertStepAttribute( transformationId, stepId, 0, STEP_ATTRIBUTE_PREFIX
+ groupName + '\t' + key, 0, value );
}
}
}
}
private Map<String, Map<String, String>> loadStepAttributesMap( ObjectId stepId ) throws KettleException {
Map<String, Map<String, String>> attributesMap = new HashMap<String, Map<String, String>>();
List<Object[]> attributeRows = repository.connectionDelegate.getStepAttributesBuffer();
RowMetaInterface rowMeta = repository.connectionDelegate.getStepAttributesRowMeta();
for ( Object[] attributeRow : attributeRows ) {
String code = rowMeta.getString( attributeRow, KettleDatabaseRepository.FIELD_STEP_ATTRIBUTE_CODE, null );
if ( code != null && code.startsWith( STEP_ATTRIBUTE_PREFIX ) ) {
String value =
rowMeta.getString( attributeRow, KettleDatabaseRepository.FIELD_STEP_ATTRIBUTE_VALUE_STR, null );
if ( value != null ) {
code = code.substring( STEP_ATTRIBUTE_PREFIX.length() );
int tabIndex = code.indexOf( '\t' );
if ( tabIndex > 0 ) {
String groupName = code.substring( 0, tabIndex );
String key = code.substring( tabIndex + 1 );
Map<String, String> attributes = attributesMap.get( groupName );
if ( attributes == null ) {
attributes = new HashMap<String, String>();
attributesMap.put( groupName, attributes );
}
attributes.put( key, value );
}
}
}
}
return attributesMap;
}
}