/*! ****************************************************************************** * * 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.trans.steps.synchronizeaftermerge; import static org.junit.Assert.*; import static org.mockito.Mockito.*; import java.lang.reflect.Field; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.pentaho.di.core.KettleEnvironment; import org.pentaho.di.core.database.DatabaseMeta; import org.pentaho.di.core.exception.KettleDatabaseException; import org.pentaho.di.core.plugins.PluginRegistry; import org.pentaho.di.core.plugins.StepPluginType; import org.pentaho.di.core.row.RowMeta; import org.pentaho.di.core.row.RowMetaInterface; import org.pentaho.di.core.row.value.ValueMetaInteger; import org.pentaho.di.core.row.value.ValueMetaString; import org.pentaho.di.trans.RowProducer; import org.pentaho.di.trans.Trans; import org.pentaho.di.trans.TransHopMeta; import org.pentaho.di.trans.TransMeta; import org.pentaho.di.trans.step.StepErrorMeta; import org.pentaho.di.trans.step.StepInterface; import org.pentaho.di.trans.step.StepMeta; import org.pentaho.di.trans.step.BaseStepData.StepExecutionStatus; import org.pentaho.di.trans.steps.dummytrans.DummyTransMeta; import org.pentaho.di.trans.steps.injector.InjectorMeta; public class SynchronizeAfterMergeIT { private static final int COMMIT_SIZE = 10; /** * is used for check PDI-14413 * * set the commit size more than row size and then pass data betwen {@link #ROW_SIZE} and {@link #COMMIT_SIZE} */ private static final int ROW_SIZE = 5; private static final int ROW_FOR_UPDATE = 18; private static final int ROW_FOR_DELETE = 18; private static final int ROW_FOR_INSERT = 18; private static final String DELETE_FLAG = "deleted"; private static final String INSERT_FLAG = "insert"; private static final String UPDATE_FLAG = "update"; private String injectorStepname = "injectorStepname"; private String synchronizeAfterMergeStepname = "SynchronizeAfterMerge"; private TransMeta transMeta; private Connection connection; @BeforeClass public static void beforeClass() throws Exception { KettleEnvironment.init(); DriverManager.registerDriver( new org.h2.Driver() ); } @AfterClass public static void afterClass() throws Exception { KettleEnvironment.shutdown(); } @After public void tearDown() throws SQLException { if ( connection != null ) { connection.close(); connection = null; } } @Before public void setUp() throws KettleDatabaseException, SQLException { connection = DriverManager.getConnection( "jdbc:h2:mem:PERSON;" ); connection.setAutoCommit( false ); PreparedStatement stmt = connection.prepareStatement( "CREATE TABLE PERSON (ID INT PRIMARY KEY, personName VARCHAR(64) )" ); stmt.execute(); stmt.close(); stmt = connection.prepareStatement( "INSERT INTO PERSON (ID, personName) VALUES (?, ?)" ); for ( int i = 0; i < ROW_FOR_UPDATE + ROW_FOR_DELETE; i++ ) { stmt.setInt( 1, i ); stmt.setString( 2, "personName" + i ); stmt.addBatch(); } stmt.executeBatch(); stmt.close(); connection.commit(); PluginRegistry pluginRegistry = PluginRegistry.getInstance(); transMeta = new TransMeta(); transMeta.setName( "SynchronizeAfterMerge" ); InjectorMeta injectorMeta = new InjectorMeta(); String injectorPid = pluginRegistry.getPluginId( StepPluginType.class, injectorMeta ); StepMeta injectorStep = new StepMeta( injectorPid, injectorStepname, injectorMeta ); transMeta.addStep( injectorStep ); DatabaseMeta dbMeta = spy( new DatabaseMeta() ); dbMeta.setDatabaseType( "H2" ); when( dbMeta.getURL() ).thenReturn( "jdbc:h2:mem:PERSON;" ); when( dbMeta.supportsErrorHandlingOnBatchUpdates() ).thenReturn( false ); SynchronizeAfterMergeMeta synchronizeAfterMergeMeta = new SynchronizeAfterMergeMeta(); //set commit size synchronizeAfterMergeMeta.setCommitSize( COMMIT_SIZE ); synchronizeAfterMergeMeta.setDatabaseMeta( dbMeta ); synchronizeAfterMergeMeta.setKeyCondition( new String[] { "=" } ); synchronizeAfterMergeMeta.setKeyLookup( new String[] { "ID" } ); synchronizeAfterMergeMeta.setKeyStream( new String[] { "personName" } ); synchronizeAfterMergeMeta.setKeyStream2( new String[] { null } ); synchronizeAfterMergeMeta.setUpdate( new Boolean[] { Boolean.TRUE } ); synchronizeAfterMergeMeta.setOperationOrderField( "flag" ); synchronizeAfterMergeMeta.setOrderDelete( DELETE_FLAG ); synchronizeAfterMergeMeta.setOrderInsert( INSERT_FLAG ); synchronizeAfterMergeMeta.setOrderUpdate( UPDATE_FLAG ); synchronizeAfterMergeMeta.setPerformLookup( true ); synchronizeAfterMergeMeta.setTableName( "PERSON" ); synchronizeAfterMergeMeta.settablenameInField( false ); synchronizeAfterMergeMeta.settablenameField( null ); synchronizeAfterMergeMeta.setUseBatchUpdate( true ); synchronizeAfterMergeMeta.setUpdateLookup( new String[] { "ID" } ); synchronizeAfterMergeMeta.setUpdateStream( new String[] { "personName" } ); String synchronizeAfterMergePid = pluginRegistry.getPluginId( StepPluginType.class, synchronizeAfterMergeStepname ); StepMeta synchronizeAfterMerge = new StepMeta( synchronizeAfterMergePid, synchronizeAfterMergeStepname, synchronizeAfterMergeMeta ); transMeta.addStep( synchronizeAfterMerge ); String dummyResultStepName = "dummyResultStepName"; DummyTransMeta dummyResultTransMeta = new DummyTransMeta(); String dummyResultPid = pluginRegistry.getPluginId( StepPluginType.class, dummyResultTransMeta ); StepMeta dummyResultStep = new StepMeta( dummyResultPid, dummyResultStepName, dummyResultTransMeta ); transMeta.addStep( dummyResultStep ); String dummyErrorStepName = "dummyErrorStepName"; DummyTransMeta dummyErrorTransMeta = new DummyTransMeta(); String dummyErrorPid = pluginRegistry.getPluginId( StepPluginType.class, dummyErrorTransMeta ); StepMeta dummyErrorStep = new StepMeta( dummyErrorPid, dummyErrorStepName, dummyErrorTransMeta ); transMeta.addStep( dummyErrorStep ); StepErrorMeta stepErrorMeta = new StepErrorMeta( transMeta, synchronizeAfterMerge, dummyErrorStep ); stepErrorMeta.setEnabled( true ); synchronizeAfterMerge.setStepErrorMeta( stepErrorMeta ); TransHopMeta injSynch = new TransHopMeta( injectorStep, synchronizeAfterMerge ); transMeta.addTransHop( injSynch ); TransHopMeta synchDummyResult = new TransHopMeta( synchronizeAfterMerge, dummyResultStep ); transMeta.addTransHop( synchDummyResult ); TransHopMeta synchDummyError = new TransHopMeta( synchronizeAfterMerge, dummyErrorStep ); transMeta.addTransHop( synchDummyError ); } @Test public void testProcessRow_Itterupted() throws Exception { processRow( TransProcessControl.ITTERUPT ); } @Test public void testProcessRow_RowSizeEqualsCommitSize() throws Exception { transMeta.setSizeRowset( COMMIT_SIZE ); processRow( TransProcessControl.WAIT ); } @Test public void testProcessRow_RowSizeLesserThanCommitSize() throws Exception { transMeta.setSizeRowset( ROW_SIZE ); processRow( TransProcessControl.WAIT ); } private void processRow( TransProcessControl control ) throws Exception { Trans trans = new Trans( transMeta ); trans.prepareExecution( null ); RowProducer rp = trans.addRowProducer( injectorStepname, 0 ); trans.startThreads(); generateData( rp ); rp.finished(); StepInterface si = trans.getStepInterface( synchronizeAfterMergeStepname, 0 ); switch ( control ) { case ITTERUPT: trans.stopAll(); while ( !si.getStatus().equals( StepExecutionStatus.STATUS_STOPPED ) ) { //wait until transformation does not stopped }; break; case WAIT: default: trans.waitUntilFinished(); assertEquals( "Step still started", StepExecutionStatus.STATUS_FINISHED, si.getStatus() ); break; } assertEquals( "Unexpected error occurred", 0, si.getErrors() ); Field field = SynchronizeAfterMerge.class.getDeclaredField( "data" ); field.setAccessible( true ); SynchronizeAfterMergeData data = (SynchronizeAfterMergeData) field.get( si ); //should be closed and set null after finish transformation assertNull( data.db.getConnection() ); } private void generateData( RowProducer rp ) { RowMetaInterface rm = new RowMeta(); rm.addValueMeta( new ValueMetaInteger( "ID" ) ); rm.addValueMeta( new ValueMetaString( "personName" ) ); rm.addValueMeta( new ValueMetaString( "flag" ) ); for ( int i = 0; i < ROW_FOR_UPDATE; i++ ) { rp.putRow( rm.clone(), new Object[] { "personNameUpdated" + i, i, UPDATE_FLAG } ); } for ( int i = ROW_FOR_UPDATE; i < ROW_FOR_UPDATE + ROW_FOR_DELETE; i++ ) { rp.putRow( rm.clone(), new Object[] { "personName" + i, i, DELETE_FLAG } ); } for ( int i = ROW_FOR_UPDATE + ROW_FOR_DELETE; i < ROW_FOR_UPDATE + ROW_FOR_DELETE + ROW_FOR_INSERT; i++ ) { rp.putRow( rm.clone(), new Object[] { "personNameInserted" + i, i, INSERT_FLAG } ); } } private enum TransProcessControl { WAIT, ITTERUPT } }