/* * This file is part of PaloKettlePlugin. * * PaloKettlePlugin is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * PaloKettlePlugin 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. * * You should have received a copy of the GNU Lesser General Public License * along with PaloKettlePlugin. If not, see <http://www.gnu.org/licenses/>. * * Portions Copyright 2008 Stratebi Business Solutions, S.L. * Portions Copyright 2011 De Bortoli Wines Pty Limited (Australia) * Portions Copyright 2011 - 2016 Pentaho Corporation */ package org.pentaho.di.trans.steps.palo.celloutput; import java.util.ArrayList; import java.util.List; import org.pentaho.di.core.CheckResult; import org.pentaho.di.core.CheckResultInterface; import org.pentaho.di.core.Const; import org.pentaho.di.core.util.Utils; import org.pentaho.di.core.annotations.Step; import org.pentaho.di.core.database.DatabaseMeta; import org.pentaho.di.core.exception.KettleException; import org.pentaho.di.core.exception.KettleXMLException; import org.pentaho.di.core.logging.DefaultLogLevel; import org.pentaho.di.core.row.RowMetaInterface; import org.pentaho.di.core.variables.VariableSpace; import org.pentaho.di.core.xml.XMLHandler; import org.pentaho.di.palo.core.DimensionField; import org.pentaho.di.palo.core.PaloHelper; import org.pentaho.di.repository.ObjectId; import org.pentaho.di.repository.Repository; import org.pentaho.di.shared.SharedObjectInterface; import org.pentaho.di.trans.Trans; import org.pentaho.di.trans.TransMeta; import org.pentaho.di.trans.step.BaseStepMeta; import org.pentaho.di.trans.step.StepDataInterface; import org.pentaho.di.trans.step.StepInterface; import org.pentaho.di.trans.step.StepMeta; import org.pentaho.di.trans.step.StepMetaInterface; import org.pentaho.metastore.api.IMetaStore; import org.w3c.dom.Node; @Step( id = "PaloCellOutput", image = "PaloCellOutput.svg", i18nPackageName = "org.pentaho.di.trans.steps.palo.celloutput", name = "PaloCellOutput.TransName", description = "PaloCellOutput.TransDescription", documentationUrl = "http://wiki.pentaho.com/display/EAI/Palo+Cell+Output", categoryDescription = "i18n:org.pentaho.di.trans.step:BaseStep.Category.Palo" ) public class PaloCellOutputMeta extends BaseStepMeta implements StepMetaInterface { private DatabaseMeta databaseMeta; private String cube = ""; private String measureType = ""; private String updateMode = "SET"; private String splashMode = "DISABLED"; private boolean clearCube; private List<DimensionField> fields = new ArrayList<DimensionField>(); private DimensionField measureField = new DimensionField( "", "", "" ); private int commitSize = 1000; private boolean enableDimensionCache = true; private boolean preloadDimensionCache = true; public PaloCellOutputMeta() { super(); } public void setDefault() { if ( updateMode == null ) { updateMode = "SET"; } if ( splashMode == null ) { splashMode = "DISABLED"; } } /** * @return Returns the database. */ public final DatabaseMeta getDatabaseMeta() { return databaseMeta; } /** * @param database The database to set. */ public final void setDatabaseMeta( final DatabaseMeta database ) { this.databaseMeta = database; } public final void loadXML( final Node stepnode, final List<DatabaseMeta> databases, final IMetaStore metaStore ) throws KettleXMLException { readData( stepnode, databases ); } public final Object clone() { PaloCellOutputMeta retval = (PaloCellOutputMeta) super.clone(); return retval; } private void readData( final Node stepnode, final List<? extends SharedObjectInterface> databases ) throws KettleXMLException { try { databaseMeta = DatabaseMeta.findDatabase( databases, XMLHandler.getTagValue( stepnode, "connection" ) ); this.cube = XMLHandler.getTagValue( stepnode, "cube" ); measureType = XMLHandler.getTagValue( stepnode, "measuretype" ); updateMode = XMLHandler.getTagValue( stepnode, "updateMode" ); splashMode = XMLHandler.getTagValue( stepnode, "splashMode" ); clearCube = XMLHandler.getTagValue( stepnode, "clearcube" ).equals( "Y" ) ? true : false; /* For backwards compatibility */ try { enableDimensionCache = XMLHandler.getTagValue( stepnode, "enableDimensionCache" ).equals( "Y" ) ? true : false; preloadDimensionCache = XMLHandler.getTagValue( stepnode, "preloadDimensionCache" ).equals( "Y" ) ? true : false; commitSize = Integer.parseInt( XMLHandler.getTagValue( stepnode, "commitSize" ) ); } catch ( Exception e ) { enableDimensionCache = false; preloadDimensionCache = false; commitSize = 1000; } this.fields = new ArrayList<DimensionField>(); Node fields = XMLHandler.getSubNode( stepnode, "fields" ); int nrFields = XMLHandler.countNodes( fields, "field" ); for ( int i = 0; i < nrFields; i++ ) { Node fnode = XMLHandler.getSubNodeByNr( fields, "field", i ); String dimensionName = XMLHandler.getTagValue( fnode, "dimensionname" ); String fieldName = XMLHandler.getTagValue( fnode, "fieldname" ); String fieldType = XMLHandler.getTagValue( fnode, "fieldtype" ); this.fields.add( new DimensionField( dimensionName, fieldName, fieldType ) ); } Node measures = XMLHandler.getSubNode( stepnode, "measures" ); int nrMeasures = XMLHandler.countNodes( measures, "measure" ); // v4 code review (matt): // -------------------------- // It's a bit weird to break in the for loop. // I think this was done for compatibility reasons. // for ( int i = 0; i < nrMeasures; ) { Node fnode = XMLHandler.getSubNodeByNr( measures, "measure", i ); String measureName = XMLHandler.getTagValue( fnode, "measurename" ); String fieldName = XMLHandler.getTagValue( fnode, "measurefieldname" ); String fieldType = XMLHandler.getTagValue( fnode, "measurefieldtype" ); this.measureField = new DimensionField( measureName, fieldName, fieldType ); break; } setDefault(); } catch ( Exception e ) { throw new KettleXMLException( "Unable to load step info from XML", e ); } } public final String getXML() { StringBuffer retval = new StringBuffer(); retval.append( " " ).append( XMLHandler.addTagValue( "connection", databaseMeta == null ? "" : databaseMeta.getName() ) ); retval.append( " " ).append( XMLHandler.addTagValue( "cube", this.cube ) ); retval.append( " " ).append( XMLHandler.addTagValue( "measuretype", measureType ) ); retval.append( " " ).append( XMLHandler.addTagValue( "updateMode", updateMode ) ); retval.append( " " ).append( XMLHandler.addTagValue( "splashMode", splashMode ) ); retval.append( " " ).append( XMLHandler.addTagValue( "clearcube", clearCube ) ); retval.append( " " ).append( XMLHandler.addTagValue( "enableDimensionCache", enableDimensionCache ) ); retval.append( " " ).append( XMLHandler.addTagValue( "preloadDimensionCache", preloadDimensionCache ) ); retval.append( " " ).append( XMLHandler.addTagValue( "commitSize", commitSize ) ); retval.append( " <fields>" ).append( Const.CR ); for ( DimensionField field : this.fields ) { retval.append( " <field>" ).append( Const.CR ); retval.append( " " ).append( XMLHandler.addTagValue( "dimensionname", field.getDimensionName() ) ); retval.append( " " ).append( XMLHandler.addTagValue( "fieldname", field.getFieldName() ) ); retval.append( " " ).append( XMLHandler.addTagValue( "fieldtype", field.getFieldType() ) ); retval.append( " </field>" ).append( Const.CR ); } retval.append( " </fields>" ).append( Const.CR ); retval.append( " <measures>" ).append( Const.CR ); // The two condition checks for the Measure is a workaround for a bug. // If you opened a transformation where the measure was not completely defined // then Kettle displayed an error saying the argument can not be null. if ( measureField.getDimensionName() != "" ) { retval.append( " <measure>" ).append( Const.CR ); retval.append( " " ).append( XMLHandler.addTagValue( "measurename", measureField.getDimensionName() ) ); if ( measureField.getFieldName() == "" ) { retval.append( " " ).append( XMLHandler.addTagValue( "measurefieldname", "CHOOSE FIELD" ) ); } else { retval.append( " " ).append( XMLHandler.addTagValue( "measurefieldname", measureField.getFieldName() ) ); } retval.append( " " ).append( XMLHandler.addTagValue( "measurefieldtype", measureField.getFieldType() ) ); retval.append( " </measure>" ).append( Const.CR ); } retval.append( " </measures>" ).append( Const.CR ); return retval.toString(); } public void readRep( Repository rep, IMetaStore metaStore, ObjectId idStep, List<DatabaseMeta> databases ) throws KettleException { try { this.databaseMeta = rep.loadDatabaseMetaFromStepAttribute( idStep, "connection", databases ); this.cube = rep.getStepAttributeString( idStep, "cube" ); this.measureType = rep.getStepAttributeString( idStep, "measuretype" ); this.updateMode = rep.getStepAttributeString( idStep, "updateMode" ); this.splashMode = rep.getStepAttributeString( idStep, "splashMode" ); this.clearCube = rep.getStepAttributeBoolean( idStep, "clearcube" ); /* For backwards compatibility */ try { this.enableDimensionCache = rep.getStepAttributeBoolean( idStep, "enableDimensionCache" ); this.preloadDimensionCache = rep.getStepAttributeBoolean( idStep, "preloadDimensionCache" ); this.commitSize = (int) rep.getStepAttributeInteger( idStep, "commitSize" ); if ( this.commitSize <= 0 ) { this.setCommitSize( 1000 ); } } catch ( Exception e ) { enableDimensionCache = false; preloadDimensionCache = false; commitSize = 1000; } int nrFields = rep.countNrStepAttributes( idStep, "dimensionname" ); for ( int i = 0; i < nrFields; i++ ) { String dimensionName = rep.getStepAttributeString( idStep, i, "dimensionname" ); String fieldName = rep.getStepAttributeString( idStep, i, "fieldname" ); String fieldType = rep.getStepAttributeString( idStep, i, "fieldtype" ); this.fields.add( new DimensionField( dimensionName, fieldName, fieldType ) ); } String measureName = rep.getStepAttributeString( idStep, "measurename" ); String measureFieldName = rep.getStepAttributeString( idStep, "measurefieldname" ); String measureFieldType = rep.getStepAttributeString( idStep, "measurefieldtype" ); this.measureField = new DimensionField( measureName, measureFieldName, measureFieldType ); setDefault(); } catch ( Exception e ) { throw new KettleException( "Unexpected error reading step information from the repository", e ); } } public void saveRep( Repository rep, IMetaStore metaStore, ObjectId idTransformation, ObjectId idStep ) throws KettleException { try { rep.saveDatabaseMetaStepAttribute( idTransformation, idStep, "connection", databaseMeta ); rep.saveStepAttribute( idTransformation, idStep, "cube", this.cube ); rep.saveStepAttribute( idTransformation, idStep, "measuretype", this.measureType ); rep.saveStepAttribute( idTransformation, idStep, "updateMode", this.updateMode ); rep.saveStepAttribute( idTransformation, idStep, "splashMode", this.splashMode ); rep.saveStepAttribute( idTransformation, idStep, "clearcube", this.clearCube ); rep.saveStepAttribute( idTransformation, idStep, "preloadDimensionCache", this.preloadDimensionCache ); rep.saveStepAttribute( idTransformation, idStep, "enableDimensionCache", this.enableDimensionCache ); rep.saveStepAttribute( idTransformation, idStep, "commitSize", this.commitSize ); rep.saveStepAttribute( idTransformation, idStep, "measurename", this.measureField.getDimensionName() ); rep.saveStepAttribute( idTransformation, idStep, "measurefieldname", this.measureField.getFieldName() ); rep.saveStepAttribute( idTransformation, idStep, "measurefieldtype", this.measureField.getFieldType() ); for ( int i = 0; i < this.fields.size(); i++ ) { rep.saveStepAttribute( idTransformation, idStep, i, "dimensionname", this.fields.get( i ).getDimensionName() ); rep.saveStepAttribute( idTransformation, idStep, i, "fieldname", this.fields.get( i ).getFieldName() ); rep.saveStepAttribute( idTransformation, idStep, i, "fieldtype", this.fields.get( i ).getFieldType() ); } } catch ( Exception e ) { throw new KettleException( "Unable to save step information to the repository for idStep=" + idStep, e ); } } public final void check( final List<CheckResultInterface> remarks, final TransMeta transMeta, final StepMeta stepMeta, final RowMetaInterface prev, final String[] input, final String[] output, final RowMetaInterface info, VariableSpace space, Repository repository, IMetaStore metaStore ) { CheckResult cr; if ( databaseMeta != null ) { cr = new CheckResult( CheckResultInterface.TYPE_RESULT_OK, "Connection exists", stepMeta ); remarks.add( cr ); final PaloHelper helper = new PaloHelper( databaseMeta, DefaultLogLevel.getLogLevel() ); try { helper.connect(); cr = new CheckResult( CheckResultInterface.TYPE_RESULT_OK, "Connection to database OK", stepMeta ); remarks.add( cr ); if ( !Utils.isEmpty( this.cube ) ) { cr = new CheckResult( CheckResultInterface.TYPE_RESULT_OK, "The name of the cube is entered", stepMeta ); remarks.add( cr ); } else { cr = new CheckResult( CheckResultInterface.TYPE_RESULT_ERROR, "The name of the cube is missing.", stepMeta ); remarks.add( cr ); } if ( this.measureField == null || Utils.isEmpty( this.measureField.getFieldName() ) ) { cr = new CheckResult( CheckResultInterface.TYPE_RESULT_ERROR, "Measure field is empty.", stepMeta ); remarks.add( cr ); } else { if ( Utils.isEmpty( this.measureField.getFieldType() ) ) { cr = new CheckResult( CheckResultInterface.TYPE_RESULT_ERROR, "Measure field type is empty.", stepMeta ); remarks.add( cr ); } } if ( this.fields == null || this.fields.size() == 0 ) { cr = new CheckResult( CheckResultInterface.TYPE_RESULT_ERROR, "Cell Output Fields are empty.", stepMeta ); remarks.add( cr ); } else { for ( DimensionField field : this.fields ) { if ( Utils.isEmpty( field.getFieldName() ) ) { cr = new CheckResult( CheckResultInterface.TYPE_RESULT_ERROR, "Input field for dimension " + field.getDimensionName() + " is empty.", stepMeta ); remarks.add( cr ); } if ( Utils.isEmpty( field.getFieldType() ) ) { cr = new CheckResult( CheckResultInterface.TYPE_RESULT_ERROR, "Input field type for dimension " + field.getDimensionName() + " is empty.", stepMeta ); remarks.add( cr ); } } } } catch ( KettleException e ) { cr = new CheckResult( CheckResultInterface.TYPE_RESULT_ERROR, "An error occurred: " + e.getMessage(), stepMeta ); remarks.add( cr ); } finally { helper.disconnect(); } } else { cr = new CheckResult( CheckResultInterface.TYPE_RESULT_ERROR, "Please select or create a connection to use", stepMeta ); remarks.add( cr ); } } public final StepInterface getStep( final StepMeta stepMeta, final StepDataInterface stepDataInterface, final int cnr, final TransMeta transMeta, final Trans trans ) { return new PaloCellOutput( stepMeta, stepDataInterface, cnr, transMeta, trans ); } public final StepDataInterface getStepData() { try { return new PaloCellOutputData( this.databaseMeta ); } catch ( Exception e ) { return null; } } public final DatabaseMeta[] getUsedDatabaseConnections() { if ( databaseMeta != null ) { return new DatabaseMeta[]{ databaseMeta }; } else { return super.getUsedDatabaseConnections(); } } public String getCube() { return this.cube; } /** * @param cube the cube name to set */ public void setCube( String cube ) { this.cube = cube; } public final String getMeasureType() { return measureType; } public final void setMeasureType( String measureType ) { this.measureType = measureType; } public List<DimensionField> getFields() { return this.fields; } public void setLevels( List<DimensionField> fields ) { this.fields = fields; } public DimensionField getMeasure() { return this.measureField; } public void setMeasureField( DimensionField measureField ) { this.measureField = measureField; } public String getUpdateMode() { return updateMode; } public void setUpdateMode( String updateMode ) { this.updateMode = updateMode; } public String getSplashMode() { return splashMode; } public void setSplashMode( String splashMode ) { this.splashMode = splashMode; } public void setClearCube( boolean create ) { this.clearCube = create; } public boolean getClearCube() { return this.clearCube; } public void setCommitSize( int commitSize ) { this.commitSize = commitSize; } public int getCommitSize() { return commitSize; } public void setEnableDimensionCache( boolean enableDimensionCache ) { this.enableDimensionCache = enableDimensionCache; } public boolean getEnableDimensionCache() { return enableDimensionCache; } public void setPreloadDimensionCache( boolean preloadDimensionCache ) { this.preloadDimensionCache = preloadDimensionCache; } public boolean getPreloadDimensionCache() { return preloadDimensionCache; } }