/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2017 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.ui.spoon;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
import org.pentaho.di.base.AbstractMeta;
import org.pentaho.di.cluster.ClusterSchema;
import org.pentaho.di.cluster.SlaveServer;
import org.pentaho.di.core.EngineMetaInterface;
import org.pentaho.di.core.database.DatabaseMeta;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.job.JobMeta;
import org.pentaho.di.partition.PartitionSchema;
import org.pentaho.di.repository.RepositoryElementInterface;
import org.pentaho.di.shared.SharedObjectInterface;
import org.pentaho.di.trans.TransMeta;
import org.pentaho.di.trans.step.StepMeta;
import org.pentaho.di.ui.spoon.delegates.SpoonDelegates;
/**
* This utility provides methods for synchronization of kettle's shared objects.
*
*/
public class SharedObjectSyncUtil {
private final ConnectionSynchronizationHandler connectionSynchronizationHandler =
new ConnectionSynchronizationHandler();
private final SlaveServerSynchronizationHandler slaveServerSynchronizationHandler =
new SlaveServerSynchronizationHandler();
private final ClusterSchemaSynchronizationHandler clusterSchemaSynchronizationHandler =
new ClusterSchemaSynchronizationHandler();
private final PartitionSchemaSynchronizationHandler partitionSchemaSynchronizationHandler =
new PartitionSchemaSynchronizationHandler();
private final StepMetaSynchronizationHandler stepMetaSynchronizationHandler = new StepMetaSynchronizationHandler();
private final SpoonDelegates spoonDelegates;
private final Spoon spoon;
public SharedObjectSyncUtil( Spoon spoon ) {
this.spoonDelegates = spoon.delegates;
this.spoon = spoon;
spoonDelegates.db.setSharedObjectSyncUtil( this );
spoonDelegates.slaves.setSharedObjectSyncUtil( this );
spoonDelegates.clusters.setSharedObjectSyncUtil( this );
spoonDelegates.partitions.setSharedObjectSyncUtil( this );
}
public synchronized void synchronizeConnections( DatabaseMeta database ) {
synchronizeConnections( database, database.getName() );
}
/**
* Synchronizes data from <code>database</code> to shared databases.
*
* @param database
* data to share
*/
public synchronized void synchronizeConnections( DatabaseMeta database, String originalName ) {
if ( !database.isShared() ) {
return;
}
synchronizeJobs( database, connectionSynchronizationHandler, originalName );
synchronizeTransformations( database, connectionSynchronizationHandler, originalName );
saveSharedObjects();
}
private void saveSharedObjects() {
try {
// flush to file for newly opened
spoon.getActiveMeta().saveSharedObjects();
} catch ( KettleException e ) {
spoon.getLog().logError( e.getLocalizedMessage(), e );
}
}
/**
* Synchronizes data from <code>slaveServer</code> to shared slave servers.
*
* @param slaveServer
* data to share
*/
public synchronized void synchronizeSlaveServers( SlaveServer slaveServer ) {
synchronizeSlaveServers( slaveServer, slaveServer.getName() );
}
public synchronized void synchronizeSlaveServers( SlaveServer slaveServer, String originalName ) {
if ( slaveServer.isShared() ) {
synchronizeJobs( slaveServer, slaveServerSynchronizationHandler, originalName );
synchronizeTransformations( slaveServer, slaveServerSynchronizationHandler, originalName );
saveSharedObjects();
}
if ( slaveServer.getObjectId() != null ) {
updateRepositoryObjects( slaveServer, slaveServerSynchronizationHandler );
}
}
public synchronized void deleteSlaveServer( SlaveServer removed ) {
synchronizeAll( true, meta -> meta.getSlaveServers().remove( removed ) );
}
public synchronized void deleteClusterSchema( ClusterSchema removed ) {
synchronizeTransformations( true, transMeta -> transMeta.getClusterSchemas().remove( removed ) );
}
public synchronized void deletePartitionSchema( PartitionSchema removed ) {
synchronizeTransformations( true, transMeta -> transMeta.getPartitionSchemas().remove( removed ) );
}
private <T extends SharedObjectInterface & RepositoryElementInterface>
void updateRepositoryObjects( T updatedObject, SynchronizationHandler<T> handler ) {
synchronizeJobs( true, job -> synchronizeByObjectId( updatedObject, handler.getObjectsForSyncFromJob( job ), handler ) );
synchronizeTransformations( true, trans ->
synchronizeByObjectId( updatedObject, handler.getObjectsForSyncFromTransformation( trans ), handler ) );
}
public synchronized void reloadTransformationRepositoryObjects( boolean includeActive ) {
if ( spoon.rep != null ) {
synchronizeTransformations( includeActive, transMeta -> {
try {
spoon.rep.readTransSharedObjects( transMeta );
} catch ( KettleException e ) {
logError( e );
}
} );
}
}
public synchronized void reloadJobRepositoryObjects( boolean includeActive ) {
if ( spoon.rep != null ) {
synchronizeJobs( includeActive, jobMeta -> {
try {
spoon.rep.readJobMetaSharedObjects( jobMeta );
} catch ( KettleException e ) {
logError( e );
}
} );
}
}
public synchronized void reloadSharedObjects() {
synchronizeAll( false, meta -> {
try {
meta.setSharedObjects( meta.readSharedObjects() );
} catch ( KettleException e ) {
logError( e );
}
} );
}
private synchronized void synchronizeJobs( boolean includeActive, Consumer<JobMeta> synchronizeAction ) {
JobMeta current = spoon.getActiveJob();
for ( JobMeta job : spoonDelegates.jobs.getLoadedJobs() ) {
if ( includeActive || job != current ) {
synchronizeAction.accept( job );
}
}
}
private synchronized void synchronizeTransformations( boolean includeActive, Consumer<TransMeta> synchronizeAction ) {
TransMeta current = spoon.getActiveTransformation();
for ( TransMeta trans : spoonDelegates.trans.getLoadedTransformations() ) {
if ( includeActive || trans != current ) {
synchronizeAction.accept( trans );
}
}
}
private synchronized void synchronizeAll( boolean includeActive, Consumer<AbstractMeta> synchronizeAction ) {
EngineMetaInterface current = spoon.getActiveMeta();
for ( TransMeta trans : spoonDelegates.trans.getLoadedTransformations() ) {
if ( includeActive || trans != current ) {
synchronizeAction.accept( trans );
}
}
for ( JobMeta job : spoonDelegates.jobs.getLoadedJobs() ) {
if ( includeActive || job != current ) {
synchronizeAction.accept( job );
}
}
}
/**
* Synchronizes data from <code>clusterSchema</code> to shared cluster schemas.
*
* @param clusterSchema
* data to share
*/
public synchronized void synchronizeClusterSchemas( ClusterSchema clusterSchema ) {
synchronizeClusterSchemas( clusterSchema, clusterSchema.getName() );
}
public synchronized void synchronizeClusterSchemas( ClusterSchema clusterSchema, String originalName ) {
if ( clusterSchema.isShared() ) {
synchronizeTransformations( clusterSchema, clusterSchemaSynchronizationHandler, originalName );
}
if ( clusterSchema.getObjectId() != null ) {
updateRepositoryObjects( clusterSchema, clusterSchemaSynchronizationHandler );
}
}
/**
* Synchronizes data from <code>clusterSchema</code> to shared partition schemas.
*
* @param partitionSchema
* data to share
*/
public synchronized void synchronizePartitionSchemas( PartitionSchema partitionSchema ) {
synchronizePartitionSchemas( partitionSchema, partitionSchema.getName() );
}
public synchronized void synchronizePartitionSchemas( PartitionSchema partitionSchema, String originalName ) {
if ( partitionSchema.isShared() ) {
synchronizeTransformations( partitionSchema, partitionSchemaSynchronizationHandler, originalName );
}
if ( partitionSchema.getObjectId() != null ) {
updateRepositoryObjects( partitionSchema, partitionSchemaSynchronizationHandler );
}
}
/**
* Synchronizes data from <code>clusterSchema</code> to shared steps.
*
* @param step
* data to shares
*/
public synchronized void synchronizeSteps( StepMeta step ) {
synchronizeSteps( step, step.getName() );
}
public synchronized void synchronizeSteps( StepMeta step, String originalName ) {
if ( !step.isShared() ) {
return;
}
synchronizeTransformations( step, stepMetaSynchronizationHandler, step.getName() );
}
private void logError( KettleException e ) {
if ( spoon.getLog() != null ) {
spoon.getLog().logError( e.getLocalizedMessage(), e );
}
}
private <T extends SharedObjectInterface> void synchronizeJobs( T sourceObject, SynchronizationHandler<T> handler,
String originalName ) {
for ( JobMeta currentJob : spoonDelegates.jobs.getLoadedJobs() ) {
List<T> objectsForSync = handler.getObjectsForSyncFromJob( currentJob );
synchronizeShared( sourceObject, originalName, objectsForSync, handler );
}
}
private <T extends SharedObjectInterface> void synchronizeTransformations( T object,
SynchronizationHandler<T> handler, String originalName ) {
for ( TransMeta currentTransformation : spoonDelegates.trans.getLoadedTransformations() ) {
List<T> objectsForSync =
handler.getObjectsForSyncFromTransformation( currentTransformation );
synchronizeShared( object, originalName, objectsForSync, handler );
}
}
private static <T extends SharedObjectInterface> void synchronizeShared(
T object, String name, List<T> objectsForSync, SynchronizationHandler<T> handler ) {
synchronize( object, toSync -> toSync.isShared() && toSync.getName().equals( name ), objectsForSync, handler );
}
private static <T extends SharedObjectInterface & RepositoryElementInterface>
void synchronizeByObjectId( T object, List<T> objectsForSync, SynchronizationHandler<T> handler ) {
synchronize( object, toSync -> object.getObjectId().equals( toSync.getObjectId() ), objectsForSync, handler );
}
private static <T extends SharedObjectInterface> void synchronize( T object, Predicate<T> pred, List<T> objectsForSync,
SynchronizationHandler<T> handler ) {
for ( T objectToSync : objectsForSync ) {
if ( pred.test( objectToSync ) && object != objectToSync ) {
handler.doSynchronization( object, objectToSync );
}
}
}
protected static interface SynchronizationHandler<T extends SharedObjectInterface> {
List<T> getObjectsForSyncFromJob( JobMeta job );
List<T> getObjectsForSyncFromTransformation( TransMeta transformation );
void doSynchronization( T source, T target );
}
private static class ConnectionSynchronizationHandler implements SynchronizationHandler<DatabaseMeta> {
@Override
public List<DatabaseMeta> getObjectsForSyncFromJob( JobMeta job ) {
return job.getDatabases();
}
@Override
public List<DatabaseMeta> getObjectsForSyncFromTransformation( TransMeta transformation ) {
return transformation.getDatabases();
}
@Override
public void doSynchronization( DatabaseMeta source, DatabaseMeta target ) {
target.replaceMeta( source );
}
}
private static class SlaveServerSynchronizationHandler implements SynchronizationHandler<SlaveServer> {
@Override
public List<SlaveServer> getObjectsForSyncFromJob( JobMeta job ) {
return job.getSlaveServers();
}
@Override
public List<SlaveServer> getObjectsForSyncFromTransformation( TransMeta transformation ) {
return transformation.getSlaveServers();
}
@Override
public void doSynchronization( SlaveServer source, SlaveServer target ) {
target.replaceMeta( source );
}
}
private static class ClusterSchemaSynchronizationHandler implements SynchronizationHandler<ClusterSchema> {
@Override
public List<ClusterSchema> getObjectsForSyncFromJob( JobMeta job ) {
return Collections.emptyList();
}
@Override
public List<ClusterSchema> getObjectsForSyncFromTransformation( TransMeta transformation ) {
return transformation.getClusterSchemas();
}
@Override
public void doSynchronization( ClusterSchema source, ClusterSchema target ) {
target.replaceMeta( source );
}
}
private static class PartitionSchemaSynchronizationHandler implements SynchronizationHandler<PartitionSchema> {
@Override
public List<PartitionSchema> getObjectsForSyncFromJob( JobMeta job ) {
return Collections.emptyList();
}
@Override
public List<PartitionSchema> getObjectsForSyncFromTransformation( TransMeta transformation ) {
return transformation.getPartitionSchemas();
}
@Override
public void doSynchronization( PartitionSchema source, PartitionSchema target ) {
target.replaceMeta( source );
}
}
private static class StepMetaSynchronizationHandler implements SynchronizationHandler<StepMeta> {
@Override
public List<StepMeta> getObjectsForSyncFromJob( JobMeta job ) {
return Collections.emptyList();
}
@Override
public List<StepMeta> getObjectsForSyncFromTransformation( TransMeta transformation ) {
return transformation.getSteps();
}
@Override
public void doSynchronization( StepMeta source, StepMeta target ) {
target.replaceMeta( source );
}
}
}