com.pentaho.big.data.bundles.impl.shim.common.ShimBridgingClassloaderTest.java Source code

Java tutorial

Introduction

Here is the source code for com.pentaho.big.data.bundles.impl.shim.common.ShimBridgingClassloaderTest.java

Source

/*******************************************************************************
 *
 * Pentaho Big Data
 *
 * Copyright (C) 2002-2017 by Hitachi Vantara : 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 com.pentaho.big.data.bundles.impl.shim.common;

import org.apache.commons.io.IOUtils;
import org.apache.commons.vfs2.FileObject;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.wiring.BundleWiring;
import org.pentaho.di.core.exception.KettleFileException;
import org.pentaho.di.core.exception.KettlePluginException;
import org.pentaho.di.core.plugins.LifecyclePluginType;
import org.pentaho.di.core.plugins.PluginRegistry;
import org.pentaho.di.core.row.ValueMetaInterface;
import org.pentaho.di.core.row.value.ValueMetaInteger;
import org.pentaho.di.core.vfs.KettleVFS;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

/**
 * Created by bryan on 10/5/15.
 */
public class ShimBridgingClassloaderTest {
    private PluginClassloaderGetter originalPluginClassloaderGetter;
    private PluginClassloaderGetter pluginClassloaderGetter;
    private ShimBridgingClassloader.PublicLoadResolveClassLoader parentClassLoader;
    private BundleContext bundleContext;
    private ShimBridgingClassloader shimBridgingClassloader;
    private BundleWiring bundleWiring;
    private Bundle bundle;
    private ShimBridgingClassloader.PublicLoadResolveClassLoader bundleWiringClassloader;

    @Before
    public void setup() {
        parentClassLoader = mock(ShimBridgingClassloader.PublicLoadResolveClassLoader.class);
        bundleContext = mock(BundleContext.class);
        bundleWiring = mock(BundleWiring.class);
        bundle = mock(Bundle.class);
        bundleWiringClassloader = mock(ShimBridgingClassloader.PublicLoadResolveClassLoader.class);
        when(bundleWiring.getClassLoader()).thenReturn(bundleWiringClassloader);
        when(bundleContext.getBundle()).thenReturn(bundle);
        when(bundle.adapt(BundleWiring.class)).thenReturn(bundleWiring);
        shimBridgingClassloader = new ShimBridgingClassloader(parentClassLoader, bundleContext);
        originalPluginClassloaderGetter = ShimBridgingClassloader.getPluginClassloaderGetter();
        pluginClassloaderGetter = mock(PluginClassloaderGetter.class);
        ShimBridgingClassloader.setPluginClassloaderGetter(pluginClassloaderGetter);
    }

    @After
    public void teardown() {
        ShimBridgingClassloader.setPluginClassloaderGetter(originalPluginClassloaderGetter);
    }

    @Test
    public void testCreateSuccessWithArgs()
            throws ClassNotFoundException, InvocationTargetException, InstantiationException, KettlePluginException,
            IllegalAccessException, KettleFileException, IOException {
        String testName = "testName";

        String canonicalName = ValueMetaInteger.class.getCanonicalName();
        FileObject myFile = KettleVFS.getFileObject("ram://testCreateSuccessWithArgs");
        try (FileObject fileObject = myFile) {
            try (OutputStream outputStream = fileObject.getContent().getOutputStream(false)) {
                IOUtils.copy(
                        getClass().getClassLoader().getResourceAsStream(canonicalName.replace(".", "/") + ".class"),
                        outputStream);
            }
            String packageName = ValueMetaInteger.class.getPackage().getName();
            when(bundleWiring.findEntries("/" + packageName.replace(".", "/"),
                    ValueMetaInteger.class.getSimpleName() + ".class", 0))
                            .thenReturn(Arrays.asList(fileObject.getURL()));
            when(parentClassLoader.loadClass(anyString(), anyBoolean())).thenAnswer(new Answer<Class<?>>() {
                @Override
                public Class<?> answer(InvocationOnMock invocation) throws Throwable {
                    Object[] arguments = invocation.getArguments();
                    return new ShimBridgingClassloader.PublicLoadResolveClassLoader(getClass().getClassLoader())
                            .loadClass((String) arguments[0], (boolean) arguments[1]);
                }
            });
            when(pluginClassloaderGetter.getPluginClassloader(LifecyclePluginType.class.getCanonicalName(),
                    ShimBridgingClassloader.HADOOP_SPOON_PLUGIN)).thenReturn(parentClassLoader);
            // With name
            Object o = ShimBridgingClassloader.create(bundleContext, canonicalName,
                    Arrays.<Object>asList(testName));
            // Interface should be same class object
            assertTrue(o instanceof ValueMetaInterface);
            assertEquals(testName, ((ValueMetaInterface) o).getName());
            // Shouldn't be true because it was loaded from diff classloader
            assertFalse(o instanceof ValueMetaInteger);

            // Null argument
            ArrayList<Object> arguments = new ArrayList<>();
            arguments.add(null);
            o = ShimBridgingClassloader.create(bundleContext, canonicalName, arguments);
            // Interface should be same class object
            assertTrue(o instanceof ValueMetaInterface);
            assertNull(((ValueMetaInterface) o).getName());
            // Shouldn't be true because it was loaded from diff classloader
            assertFalse(o instanceof ValueMetaInteger);

            // Null arg list
            o = ShimBridgingClassloader.create(bundleContext, canonicalName, null);
            // Interface should be same class object
            assertTrue(o instanceof ValueMetaInterface);
            assertNull(((ValueMetaInterface) o).getName());
            // Shouldn't be true because it was loaded from diff classloader
            assertFalse(o instanceof ValueMetaInteger);

            // Empty arg list
            o = ShimBridgingClassloader.create(bundleContext, canonicalName, new ArrayList<Object>());
            // Interface should be same class object
            assertTrue(o instanceof ValueMetaInterface);
            assertNull(((ValueMetaInterface) o).getName());
            // Shouldn't be true because it was loaded from diff classloader
            assertFalse(o instanceof ValueMetaInteger);
        } finally {
            myFile.delete();
        }
    }

    @Test(expected = InstantiationException.class)
    public void testCreateFalureNoMatching()
            throws ClassNotFoundException, InvocationTargetException, InstantiationException, KettlePluginException,
            IllegalAccessException, KettleFileException, IOException {
        String testName = "testName";

        String canonicalName = ValueMetaInteger.class.getCanonicalName();
        FileObject myFile = KettleVFS.getFileObject("ram://testCreateSuccessWithArgs");
        try (FileObject fileObject = myFile) {
            try (OutputStream outputStream = fileObject.getContent().getOutputStream(false)) {
                IOUtils.copy(
                        getClass().getClassLoader().getResourceAsStream(canonicalName.replace(".", "/") + ".class"),
                        outputStream);
            }
            String packageName = ValueMetaInteger.class.getPackage().getName();
            when(bundleWiring.findEntries("/" + packageName.replace(".", "/"),
                    ValueMetaInteger.class.getSimpleName() + ".class", 0))
                            .thenReturn(Arrays.asList(fileObject.getURL()));
            when(parentClassLoader.loadClass(anyString(), anyBoolean())).thenAnswer(new Answer<Class<?>>() {
                @Override
                public Class<?> answer(InvocationOnMock invocation) throws Throwable {
                    Object[] arguments = invocation.getArguments();
                    return new ShimBridgingClassloader.PublicLoadResolveClassLoader(getClass().getClassLoader())
                            .loadClass((String) arguments[0], (boolean) arguments[1]);
                }
            });
            when(pluginClassloaderGetter.getPluginClassloader(LifecyclePluginType.class.getCanonicalName(),
                    ShimBridgingClassloader.HADOOP_SPOON_PLUGIN)).thenReturn(parentClassLoader);
            Object o = ShimBridgingClassloader.create(bundleContext, canonicalName, Arrays.<Object>asList(1.1));
        } finally {
            myFile.delete();
        }
    }

    @Test
    public void testFindClassSuccess() throws ClassNotFoundException, IOException, KettleFileException {
        String canonicalName = ShimBridgingClassloader.class.getCanonicalName();
        FileObject myFile = KettleVFS.getFileObject("ram://testFindClassSuccess");
        try (FileObject fileObject = myFile) {
            try (OutputStream outputStream = fileObject.getContent().getOutputStream(false)) {
                IOUtils.copy(
                        getClass().getClassLoader().getResourceAsStream(canonicalName.replace(".", "/") + ".class"),
                        outputStream);
            }
            String packageName = ShimBridgingClassloader.class.getPackage().getName();
            when(bundleWiring.findEntries("/" + packageName.replace(".", "/"),
                    ShimBridgingClassloader.class.getSimpleName() + ".class", 0))
                            .thenReturn(Arrays.asList(fileObject.getURL()));
            when(parentClassLoader.loadClass(anyString(), anyBoolean())).thenAnswer(new Answer<Class<?>>() {
                @Override
                public Class<?> answer(InvocationOnMock invocation) throws Throwable {
                    Object[] arguments = invocation.getArguments();
                    return new ShimBridgingClassloader.PublicLoadResolveClassLoader(getClass().getClassLoader())
                            .loadClass((String) arguments[0], (boolean) arguments[1]);
                }
            });
            Class<?> shimBridgingClassloaderClass = shimBridgingClassloader.findClass(canonicalName);
            assertEquals(canonicalName, shimBridgingClassloaderClass.getCanonicalName());
            assertEquals(shimBridgingClassloader, shimBridgingClassloaderClass.getClassLoader());
            assertEquals(packageName, shimBridgingClassloaderClass.getPackage().getName());
        } finally {
            myFile.delete();
        }
    }

    @Test(expected = ClassNotFoundException.class)
    public void testFindFailReading() throws ClassNotFoundException, IOException, KettleFileException {
        FileObject myFile = KettleVFS.getFileObject("ram://testFindFailReading");
        try (FileObject fileObject = myFile) {
            when(bundleWiring.findEntries(
                    "/" + ShimBridgingClassloader.class.getPackage().getName().replace(".", "/"),
                    ShimBridgingClassloader.class.getSimpleName() + ".class", 0))
                            .thenReturn(Arrays.asList(fileObject.getURL()));
            shimBridgingClassloader.findClass(ShimBridgingClassloader.class.getCanonicalName());
        } finally {
            myFile.delete();
        }
    }

    @Test(expected = ClassNotFoundException.class)
    public void testFindNotFound() throws ClassNotFoundException {
        when(bundleWiring.findEntries("/" + ShimBridgingClassloader.class.getPackage().getName().replace(".", "/"),
                ShimBridgingClassloader.class.getSimpleName() + ".class", 0)).thenReturn(Arrays.<URL>asList());
        shimBridgingClassloader.findClass(ShimBridgingClassloader.class.getCanonicalName());
    }

    @Test
    public void testGetResourceDefaultPackage() throws MalformedURLException {
        String testName = "testName";
        URL url = new URL("file://testGetResourceDefaultPackage");
        when(bundleWiring.findEntries("/", testName, 0)).thenReturn(Arrays.asList(url));
        assertEquals(url, shimBridgingClassloader.getResource(testName));
    }

    @Test
    public void testGetResourceInPackage() throws MalformedURLException {
        String path = "testPath";
        String testName = "testName";
        URL url = new URL("file://path/testGetResourceDefaultPackage");
        when(bundleWiring.findEntries("/testPath", testName, 0)).thenReturn(Arrays.asList(url));
        assertEquals(url, shimBridgingClassloader.getResource("/" + path + "/" + testName));
    }

    @Test
    public void testGetResourceBundleWiringClassloader() throws MalformedURLException {
        String testName = "testName";
        URL url = new URL("file://testGetResourceBundleWiringClassloader");
        when(bundleWiringClassloader.getResource(testName)).thenReturn(url);
        assertEquals(url, shimBridgingClassloader.getResource(testName));
    }

    @Test
    public void testGetResourceParentClassloader() throws MalformedURLException {
        String testName = "testName";
        URL url = new URL("file://testGetResourceParentClassloader");
        when(parentClassLoader.getResource(testName)).thenReturn(url);
        assertEquals(url, shimBridgingClassloader.getResource(testName));
    }

    @Test
    public void testLoadClassFromBothOSGiAndParent() throws ClassNotFoundException {
        String testName = "testName";
        when(parentClassLoader.loadClass(testName, false)).thenReturn((Class) Object.class);
        when(bundleWiringClassloader.loadClass(testName, false)).thenReturn((Class) PluginRegistry.class);
        assertEquals(Object.class, shimBridgingClassloader.loadClass(testName));
    }

    @Test
    public void testLoadClassFromBothOSGiAndParentParentException() throws ClassNotFoundException {
        String testName = "testName";
        when(parentClassLoader.loadClass(testName, false)).thenThrow(new RuntimeException());
        when(bundleWiringClassloader.loadClass(testName, false)).thenReturn((Class) PluginRegistry.class);
        assertEquals(PluginRegistry.class, shimBridgingClassloader.loadClass(testName));
    }

    @Test
    public void testLoadClassFromOSGi() throws ClassNotFoundException {
        String testName = "testName";
        when(bundleWiringClassloader.loadClass(testName, false)).thenReturn((Class) Object.class);
        assertEquals(Object.class, shimBridgingClassloader.loadClass(testName));
    }

    @Test
    public void testLoadClassFromParent() throws ClassNotFoundException {
        String testName = "testName";
        when(parentClassLoader.loadClass(testName, false)).thenReturn((Class) Object.class);
        assertEquals(Object.class, shimBridgingClassloader.loadClass(testName));
    }

    @Test
    public void testLoadClassFindClass() throws ClassNotFoundException, IOException, KettleFileException {
        String canonicalName = ShimBridgingClassloader.class.getCanonicalName();
        FileObject myFile = KettleVFS.getFileObject("ram://testLoadClassFindClass");
        try (FileObject fileObject = myFile) {
            try (OutputStream outputStream = fileObject.getContent().getOutputStream(false)) {
                IOUtils.copy(
                        getClass().getClassLoader().getResourceAsStream(canonicalName.replace(".", "/") + ".class"),
                        outputStream);
            }
            String packageName = ShimBridgingClassloader.class.getPackage().getName();
            when(bundleWiring.findEntries("/" + packageName.replace(".", "/"),
                    ShimBridgingClassloader.class.getSimpleName() + ".class", 0))
                            .thenReturn(Arrays.asList(fileObject.getURL()));
            when(parentClassLoader.loadClass(anyString(), anyBoolean())).thenAnswer(new Answer<Class<?>>() {
                @Override
                public Class<?> answer(InvocationOnMock invocation) throws Throwable {
                    Object[] arguments = invocation.getArguments();
                    return new ShimBridgingClassloader.PublicLoadResolveClassLoader(getClass().getClassLoader())
                            .loadClass((String) arguments[0], (boolean) arguments[1]);
                }
            });
            Class<?> shimBridgingClassloaderClass = shimBridgingClassloader.loadClass(canonicalName, true);
            assertEquals(canonicalName, shimBridgingClassloaderClass.getCanonicalName());
            assertEquals(shimBridgingClassloader, shimBridgingClassloaderClass.getClassLoader());
            assertEquals(packageName, shimBridgingClassloaderClass.getPackage().getName());
            Class<?> shimBridgingClassloaderClass2 = shimBridgingClassloader.loadClass(canonicalName, false);
            assertEquals(shimBridgingClassloaderClass, shimBridgingClassloaderClass2);
        } finally {
            myFile.delete();
        }
    }

    /*
    BACKLOG-19039 - Yarn step for secure cluster stop working after license verifier check
    Bug reason - license verifier tries to load bundle for com.pentaho.yarn.impl.shim.YarnServiceImpl.class using ShimBridgingClassloader
    for getting bundle header Implementation-Version from bundle info and can't do it because ShimBridgingClassloader doesn't implement interface BundleReference
    As a solution added implementation of interface BundleReference to ShimBridgingClassloader
    Test possibility of ShimBridgingClassloader to return bundle, when it invokes from FrameworkUtil.getBundle(Class<?>)
     */
    @Test
    public void testGetBundleWhenRequestingBundleShouldReturnBundle()
            throws ClassNotFoundException, IOException, KettleFileException {
        String canonicalName = ShimBridgingClassloader.class.getCanonicalName();
        String packageName = ShimBridgingClassloader.class.getPackage().getName();
        URL url = getClass().getClassLoader().getResource(canonicalName.replace(".", "/") + ".class");
        when(bundleWiring.findEntries("/" + packageName.replace(".", "/"),
                ShimBridgingClassloader.class.getSimpleName() + ".class", 0)).thenReturn(Arrays.asList(url));
        when(parentClassLoader.loadClass(anyString(), anyBoolean())).thenAnswer(new Answer<Class<?>>() {
            @Override
            public Class<?> answer(InvocationOnMock invocation) throws Throwable {
                Object[] arguments = invocation.getArguments();
                return new ShimBridgingClassloader.PublicLoadResolveClassLoader(getClass().getClassLoader())
                        .loadClass((String) arguments[0], (boolean) arguments[1]);
            }
        });
        Class<?> shimBridgingClassloaderClass = shimBridgingClassloader.loadClass(canonicalName, true);
        when(shimBridgingClassloader.getBundle()).thenReturn(bundle);

        Bundle actualBundle = FrameworkUtil.getBundle(shimBridgingClassloaderClass);

        assertEquals(actualBundle, bundle);
    }
}