/*! ****************************************************************************** * * 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; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import com.google.common.util.concurrent.SettableFuture; import org.junit.Test; import org.pentaho.di.core.annotations.KettleLifecyclePlugin; import org.pentaho.di.core.exception.KettleException; import org.pentaho.di.core.lifecycle.KettleLifecycleListener; import org.pentaho.di.core.lifecycle.LifecycleException; 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.variables.Variables; import org.pentaho.di.core.vfs.KettleVFS; /** * Tests for the Kettle Environment * */ public class KettleEnvironmentTest { private static final AtomicBoolean environmentInitCalled = new AtomicBoolean( false ); private static final String pluginId = "MockLifecycleListener"; @KettleLifecyclePlugin( id = pluginId ) public static class MockLifecycleListener implements KettleLifecycleListener { @Override public void onEnvironmentInit() throws LifecycleException { environmentInitCalled.set( true ); } @Override public void onEnvironmentShutdown() { } } @KettleLifecyclePlugin( id = pluginId ) public static class FailingMockLifecycleListener extends MockLifecycleListener { @Override public void onEnvironmentInit() throws LifecycleException { throw new LifecycleException( false ); } } @KettleLifecyclePlugin( id = pluginId ) public static class SevereFailingMockLifecycleListener extends MockLifecycleListener { @Override public void onEnvironmentInit() throws LifecycleException { throw new LifecycleException( true ); } } @KettleLifecyclePlugin( id = pluginId ) public static class ThrowableFailingMockLifecycleListener extends MockLifecycleListener { @Override public void onEnvironmentInit() throws LifecycleException { // Simulate a LifecycleListener that wasn't updated to the latest API throw new AbstractMethodError(); } } private void resetKettleEnvironmentInitializationFlag() throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException { Field f = KettleEnvironment.class.getDeclaredField( "initialized" ); f.setAccessible( true ); f.set( null, new AtomicReference<SettableFuture<Boolean>>( null ) ); Constructor<KettleVFS> constructor; constructor = KettleVFS.class.getDeclaredConstructor(); constructor.setAccessible( true ); KettleVFS KVFS = constructor.newInstance(); f = KVFS.getClass().getDeclaredField( "kettleVFS" ); f.setAccessible( true ); Field modifiersField = Field.class.getDeclaredField( "modifiers" ); modifiersField.setAccessible( true ); modifiersField.setInt( f, f.getModifiers() & ~Modifier.FINAL | Modifier.VOLATILE ); f.set( null, KVFS ); f = KVFS.getClass().getDeclaredField( "defaultVariableSpace" ); f.setAccessible( true ); modifiersField.setInt( f, f.getModifiers() & ~Modifier.FINAL ); Variables var = new Variables(); var.initializeVariablesFrom( null ); f.set( null, var ); } /** * Validate that a LifecycleListener's environment init callback is called when the Kettle Environment is initialized. */ @Test public void lifecycleListenerEnvironmentInitCallback() throws Exception { resetKettleEnvironmentInitializationFlag(); assertFalse( "This test only works if the Kettle Environment is not yet initialized", KettleEnvironment .isInitialized() ); System.setProperty( Const.KETTLE_PLUGIN_CLASSES, MockLifecycleListener.class.getName() ); KettleEnvironment.init(); PluginInterface pi = PluginRegistry.getInstance().findPluginWithId( KettleLifecyclePluginType.class, pluginId ); MockLifecycleListener l = (MockLifecycleListener) PluginRegistry.getInstance().loadClass( pi, KettleLifecycleListener.class ); assertNotNull( "Test plugin not registered properly", l ); assertTrue( environmentInitCalled.get() ); } /** * Validate that a LifecycleListener's environment init callback is called when the Kettle Environment is initialized. */ @Test public void lifecycleListenerEnvironmentInitCallback_exception_thrown() throws Exception { resetKettleEnvironmentInitializationFlag(); assertFalse( "This test only works if the Kettle Environment is not yet initialized", KettleEnvironment .isInitialized() ); System.setProperty( Const.KETTLE_PLUGIN_CLASSES, FailingMockLifecycleListener.class.getName() ); KettleEnvironment.init(); PluginInterface pi = PluginRegistry.getInstance().findPluginWithId( KettleLifecyclePluginType.class, pluginId ); MockLifecycleListener l = (MockLifecycleListener) PluginRegistry.getInstance().loadClass( pi, KettleLifecycleListener.class ); assertNotNull( "Test plugin not registered properly", l ); assertTrue( KettleEnvironment.isInitialized() ); } /** * Validate that a LifecycleListener's environment init callback is called when the Kettle Environment is initialized. */ @Test public void lifecycleListenerEnvironmentInitCallback_exception_thrown_severe() throws Exception { resetKettleEnvironmentInitializationFlag(); assertFalse( "This test only works if the Kettle Environment is not yet initialized", KettleEnvironment .isInitialized() ); System.setProperty( Const.KETTLE_PLUGIN_CLASSES, SevereFailingMockLifecycleListener.class.getName() ); try { KettleEnvironment.init(); fail( "Expected exception" ); } catch ( KettleException ex ) { assertEquals( LifecycleException.class, ex.getCause().getClass() ); ex.printStackTrace(); } assertFalse( KettleEnvironment.isInitialized() ); } @Test public void lifecycleListenerEnvironmentInitCallback_throwable_thrown() throws Exception { resetKettleEnvironmentInitializationFlag(); assertFalse( "This test only works if the Kettle Environment is not yet initialized", KettleEnvironment .isInitialized() ); System.setProperty( Const.KETTLE_PLUGIN_CLASSES, ThrowableFailingMockLifecycleListener.class.getName() ); try { KettleEnvironment.init(); fail( "Expected exception" ); } catch ( KettleException ex ) { assertEquals( AbstractMethodError.class, ex.getCause().getClass() ); ex.printStackTrace(); } assertFalse( KettleEnvironment.isInitialized() ); } }