/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2013 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.core.lifecycle;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Throwables;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.exception.KettlePluginException;
import org.pentaho.di.core.logging.LogChannel;
import org.pentaho.di.core.plugins.KettleLifecyclePluginType;
import org.pentaho.di.core.plugins.PluginInterface;
import org.pentaho.di.core.plugins.PluginRegistry;
import org.pentaho.di.core.plugins.PluginTypeListener;
import org.pentaho.di.i18n.BaseMessages;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* A single point of contact for Kettle Lifecycle Plugin instances for invoking lifecycle methods.
*/
public class KettleLifecycleSupport {
private static Class<?> PKG = Const.class; // for i18n purposes, needed by Translator2!!
@VisibleForTesting protected static PluginRegistry registry = PluginRegistry.getInstance();
private ConcurrentMap<KettleLifecycleListener, Boolean> kettleLifecycleListeners;
private AtomicBoolean initialized = new AtomicBoolean( false );
public KettleLifecycleSupport() {
Set<KettleLifecycleListener> listeners =
LifecycleSupport.loadPlugins( KettleLifecyclePluginType.class, KettleLifecycleListener.class );
kettleLifecycleListeners = new ConcurrentHashMap<KettleLifecycleListener, Boolean>();
for ( KettleLifecycleListener kll: listeners ) {
kettleLifecycleListeners.put( kll, false );
}
registry.addPluginListener( KettleLifecyclePluginType.class, new PluginTypeListener() {
@Override
public void pluginAdded( Object serviceObject ) {
KettleLifecycleListener listener = null;
try {
listener = (KettleLifecycleListener) registry.loadClass( (PluginInterface) serviceObject );
} catch ( KettlePluginException e ) {
e.printStackTrace();
return;
}
kettleLifecycleListeners.put( listener, false );
if ( initialized.get() ) {
try {
onEnvironmentInit( listener );
} catch ( Throwable e ) {
// Exception is unexpected and couldn't recover
Throwables.propagate( e );
}
}
}
@Override
public void pluginRemoved( Object serviceObject ) {
kettleLifecycleListeners.remove( serviceObject );
}
@Override
public void pluginChanged( Object serviceObject ) {
}
} );
}
/**
* Execute all known listener's {@link #onEnvironmentInit()} methods. If an invocation throws a
* {@link LifecycleException} is severe this method will re-throw the exception.
*
* @throws LifecycleException
* if any listener throws a severe Lifecycle Exception or any {@link Throwable}.
*/
public void onEnvironmentInit() throws KettleException {
// Execute only once
if ( initialized.compareAndSet( false, true ) ) {
for ( KettleLifecycleListener listener : kettleLifecycleListeners.keySet() ) {
onEnvironmentInit( listener );
}
}
}
private void onEnvironmentInit( KettleLifecycleListener listener ) throws KettleException {
// Run only once per listener
if ( kettleLifecycleListeners.replace( listener, false, true ) ) {
try {
listener.onEnvironmentInit();
} catch ( LifecycleException ex ) {
String message =
BaseMessages.getString( PKG, "LifecycleSupport.ErrorInvokingKettleLifecycleListener", listener );
if ( ex.isSevere() ) {
throw new KettleException( message, ex );
}
// Not a severe error so let's simply log it and continue invoking the others
LogChannel.GENERAL.logError( message, ex );
} catch ( Throwable t ) {
Throwables.propagateIfPossible( t, KettleException.class );
String message = BaseMessages.getString(
PKG, "LifecycleSupport.ErrorInvokingKettleLifecycleListener", listener );
throw new KettleException( message, t );
}
}
}
public void onEnvironmentShutdown() {
for ( KettleLifecycleListener listener : kettleLifecycleListeners.keySet() ) {
try {
listener.onEnvironmentShutdown();
} catch ( Throwable t ) {
// Log the error and continue invoking other listeners
LogChannel.GENERAL.logError( BaseMessages.getString(
PKG, "LifecycleSupport.ErrorInvokingKettleLifecycleListener", listener ), t );
}
}
}
}