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

Java tutorial

Introduction

Here is the source code for com.pentaho.big.data.bundles.impl.shim.common.ShimBridgingClassloader.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 com.google.common.annotations.VisibleForTesting;
import org.apache.commons.io.IOUtils;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleReference;
import org.osgi.framework.wiring.BundleWiring;
import org.pentaho.di.core.exception.KettlePluginException;
import org.pentaho.di.core.plugins.LifecyclePluginType;
import org.pentaho.di.core.plugins.PluginRegistry;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.List;

/**
 * Created by bryan on 6/4/15.
 */
public class ShimBridgingClassloader extends ClassLoader implements BundleReference {
    public static final String HADOOP_SPOON_PLUGIN = "HadoopSpoonPlugin";
    private static PluginClassloaderGetter pluginClassloaderGetter = new PluginClassloaderGetter();
    private final BundleWiring bundleWiring;
    private final PublicLoadResolveClassLoader bundleWiringClassloader;

    public ShimBridgingClassloader(ClassLoader parentClassLoader, BundleContext bundleContext) {
        super(parentClassLoader);
        this.bundleWiring = (BundleWiring) bundleContext.getBundle().adapt(BundleWiring.class);
        this.bundleWiringClassloader = new PublicLoadResolveClassLoader(bundleWiring.getClassLoader());
    }

    public static Object create(BundleContext bundleContext, String className, List<Object> arguments)
            throws KettlePluginException, ClassNotFoundException, IllegalAccessException, InstantiationException,
            InvocationTargetException {
        ShimBridgingClassloader shimBridgingClassloader = new ShimBridgingClassloader(pluginClassloaderGetter
                .getPluginClassloader(LifecyclePluginType.class.getCanonicalName(), HADOOP_SPOON_PLUGIN),
                bundleContext);
        Class<?> clazz = Class.forName(className, true, shimBridgingClassloader);
        if (arguments == null || arguments.size() == 0) {
            return clazz.newInstance();
        }
        for (Constructor<?> constructor : clazz.getConstructors()) {
            Class<?>[] parameterTypes = constructor.getParameterTypes();
            if (parameterTypes.length == arguments.size()) {
                boolean match = true;
                for (int i = 0; i < parameterTypes.length; i++) {
                    Object o = arguments.get(i);
                    if (o != null && !parameterTypes[i].isInstance(o)) {
                        match = false;
                        break;
                    }
                }
                if (match) {
                    return constructor.newInstance(arguments.toArray());
                }
            }
        }
        throw new InstantiationException(
                "Unable to find constructor for class " + className + " with arguments " + arguments);
    }

    @VisibleForTesting
    static PluginClassloaderGetter getPluginClassloaderGetter() {
        return pluginClassloaderGetter;
    }

    @VisibleForTesting
    static void setPluginClassloaderGetter(PluginClassloaderGetter pluginClassloaderGetter) {
        ShimBridgingClassloader.pluginClassloaderGetter = pluginClassloaderGetter;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        int lastIndexOfDot = name.lastIndexOf('.');
        final String packageName;
        final String translatedPath;
        final String translatedName;
        if (lastIndexOfDot >= 0) {
            packageName = name.substring(0, lastIndexOfDot);
            if (getPackage(packageName) == null) {
                definePackage(packageName, null, null, null, null, null, null, null);
            }
            translatedPath = "/" + packageName.replace('.', '/');
            translatedName = name.substring(lastIndexOfDot + 1) + ".class";
        } else {
            packageName = "";
            translatedPath = "/";
            translatedName = name;
        }
        if (getPackage(packageName) == null) {
            definePackage(packageName, null, null, null, null, null, null, null);
        }
        List<URL> entries = bundleWiring.findEntries(translatedPath, translatedName, 0);
        if (entries.size() == 1) {
            byte[] bytes;
            try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
                IOUtils.copy(entries.get(0).openStream(), byteArrayOutputStream);
                bytes = byteArrayOutputStream.toByteArray();
            } catch (IOException e) {
                throw new ClassNotFoundException("Unable to define class", e);
            }
            return defineClass(name, bytes, 0, bytes.length);
        }
        throw new ClassNotFoundException();
    }

    @Override
    public URL getResource(String name) {
        int lastIndexOf = name.lastIndexOf('/');

        List<URL> entries;
        if (lastIndexOf > 0) {
            entries = bundleWiring.findEntries(name.substring(0, lastIndexOf), name.substring(lastIndexOf + 1), 0);
        } else {
            entries = bundleWiring.findEntries("/", name, 0);
        }
        if (entries.size() == 1) {
            return entries.get(0);
        }
        URL resource = bundleWiringClassloader.getResource(name);
        if (resource == null) {
            resource = super.getResource(name);
        }
        return resource;
    }

    @Override
    public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        Class<?> result = null;
        synchronized (this) {
            result = findLoadedClass(name);
        }
        if (result == null) {
            try {
                result = findClass(name);
            } catch (Exception e) {
                // Ignore
            }
        }
        if (result == null) {
            try {
                Class<?> osgiProvidedClass = bundleWiringClassloader.loadClass(name, resolve);
                if (osgiProvidedClass.getClassLoader() == PluginRegistry.class.getClassLoader()) {
                    // Give parent a chance to supercede the system classloader (workaround for boot delegation of packages we
                    // should have loaded from the parent)
                    try {
                        return super.loadClass(name, resolve);
                    } catch (Exception e) {
                        // Ignore
                    }
                }
                return osgiProvidedClass;
            } catch (Exception e) {
                // Ignore
            }
        }
        if (result == null) {
            return super.loadClass(name, resolve);
        }
        if (resolve) {
            resolveClass(result);
        }
        return result;
    }

    @Override
    public Bundle getBundle() {
        return this.bundleWiring.getBundle();
    }

    /**
     * Trivial classloader subclass that lets us call loadClass with a resolve parameter
     */
    @VisibleForTesting
    static class PublicLoadResolveClassLoader extends ClassLoader {
        public PublicLoadResolveClassLoader(ClassLoader parent) {
            super(parent);
        }

        @Override
        public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
            return super.loadClass(name, resolve);
        }
    }
}