org.pepstock.jem.util.ClassLoaderUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.pepstock.jem.util.ClassLoaderUtil.java

Source

/**
JEM, the BEE - Job Entry Manager, the Batch Execution Environment
Copyright (C) 2012-2015   Andrea "Stock" Stocchero
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.pepstock.jem.util;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;

import org.apache.commons.io.FileUtils;
import org.pepstock.jem.node.Main;
import org.pepstock.jem.node.configuration.AbstractPluginDefinition;
import org.pepstock.jem.node.configuration.ClassPath;

/**
 * This utility needs to load Abstract plugins from a reverse URL classloader.<br>
 * To allow loading a plugin by custom classloader, it's mandatory to set the
 * CLASSPATH element in the plugin definition.<br>
 * The classpath could be set as following:<br>
 * <ul>
 * <li>[string]: could be a file (only JARs are considered) or a directory
 * <li>[string]/*: indicates the content of a folder will be part of classpath
 * (only jars)
 * <li>[string]/**: indicates the content of a folder and subfolders (in
 * cascade) will be part of classpath (only jars)
 * <ul>
 * 
 * @author Andrea "Stock" Stocchero
 * @version 2.2
 */
public final class ClassLoaderUtil {

    /**
     * Extensions files loaded for classpath
     */
    public static final List<String> EXTENSIONS = Collections.unmodifiableList(Arrays.asList("jar"));

    /**
     * Loads all files of a folder, without any cascading
     */
    public static final String ALL_FOLDER = "*";

    /**
     * Loads all files of a folder, with cascading on all subfolders
     */
    public static final String ALL_FOLDER_IN_CASCADE = "**";

    /**
     * Empty to avoid instantiation
     */
    private ClassLoaderUtil() {

    }

    /**
     * Loads all classpath information from plugin configuration and creates a
     * custom classloader to load the plugin.
     * 
     * @param pluginDef plugin defintion
     * @param props list of properties to used to substitute if necessary
     * @return containers with object instantiated and class path based on URLs
     * @throws InstantiationException if any error occurs
     * @throws IllegalAccessException if any error occurs
     * @throws ClassNotFoundException if any error occurs
     * @throws IOException if any error occurs
     */
    public static ObjectAndClassPathContainer loadAbstractPlugin(AbstractPluginDefinition pluginDef,
            Properties props)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException, IOException {
        return loadAbstractPlugin(pluginDef, props, null);
    }

    /**
     * Loads all classpath information from plugin configuration and creates a
     * custom classloader to load the plugin.
     * 
     * @param pluginDef plugin defintion
     * @param props list of properties to used to substitute if necessary
     * @param knownLoader ClassLoader already created previously
     * @return containers with object instantiated and class path based on URLs
     * @throws InstantiationException if any error occurs
     * @throws IllegalAccessException if any error occurs
     * @throws ClassNotFoundException if any error occurs
     * @throws IOException if any error occurs
     */
    public static ObjectAndClassPathContainer loadAbstractPlugin(AbstractPluginDefinition pluginDef,
            Properties props, ClassLoader knownLoader)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException, IOException {
        // creates the result to return
        ObjectAndClassPathContainer result = new ObjectAndClassPathContainer();

        if (knownLoader != null) {
            // there already a classloader
            // loads the plugin from classloader
            Class<?> clazz = knownLoader.loadClass(pluginDef.getClassName());
            // sets the object
            result.setObject(clazz.newInstance());
            return result;
        } else if (pluginDef.getClasspath() == null || pluginDef.getClasspath().isEmpty()) {
            // if plugin defintion doesn't have the classpath, that means that the
            // plugin is already placed in JEM classpath
            // therefore it's enough to call it
            // load by Class.forName of factory
            result.setObject(Class.forName(pluginDef.getClassName()).newInstance());
        } else {
            // CLASSPATH has been set therefore it an try to load the plugin by
            // a custom classloader
            // collection of all file of classpath
            Collection<File> files = new LinkedList<File>();
            // scans all strings of classpath
            for (ClassPath classPath : pluginDef.getClasspath()) {
                // substitute variables if there are
                String path = VariableSubstituter.substitute(classPath.getContent(), props);
                // creates the file
                File file = new File(path);
                // if file ends with * could be only this folder or all folders
                // in cascade
                if (path.endsWith(ALL_FOLDER)) {
                    // checks if is all folders in cascade
                    boolean cascade = path.endsWith(ALL_FOLDER_IN_CASCADE);
                    // gets the parent and asks for all JAR files
                    File parent = file.getParentFile();
                    Collection<File> newFiles = FileUtils.listFiles(parent, EXTENSIONS.toArray(new String[0]),
                            cascade);
                    // loads to the collection
                    files.addAll(newFiles);
                    if (cascade) {
                        // scan all files to extract folder to add classpath
                        // with *, standard JAVA
                        for (File newFile : newFiles) {
                            String parentNormalized = newFile.getParentFile().getAbsolutePath() + File.separator
                                    + ALL_FOLDER;
                            // if the path is not already in the result, load it
                            if (!result.getClassPath().contains(parentNormalized)) {
                                result.getClassPath().add(parentNormalized);
                            }
                        }
                    } else {
                        // loads all files
                        if (!result.getClassPath().contains(file.getAbsolutePath())) {
                            result.getClassPath().add(file.getAbsolutePath());
                        }
                    }
                } else if (file.isDirectory() && file.exists()) {
                    // if here, we have a directory
                    // adds the directory to collection
                    files.add(file);
                    if (!result.getClassPath().contains(file.getAbsolutePath())) {
                        result.getClassPath().add(file.getAbsolutePath());
                    }
                } else if (file.isFile() && file.exists()) {
                    // if here, a file has been indicated
                    // adds the directory to collection
                    files.add(file);
                    if (!result.getClassPath().contains(file.getAbsolutePath())) {
                        result.getClassPath().add(file.getAbsolutePath());
                    }
                }
            }
            // checks if the collection is empty.
            // if yes, all classpath definiton is wrong and no files have been
            // loaded
            if (!files.isEmpty()) {
                // exports files in URLs, for our classloader
                final URL[] urls = FileUtils.toURLs(files.toArray(new File[files.size()]));
                // loads a our classloader by access controller
                ClassLoader loader = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
                    public ClassLoader run() {
                        return new ReverseURLClassLoader(urls, Main.class.getClassLoader());
                    }
                });
                // loads the plugin from classloader
                Class<?> clazz = loader.loadClass(pluginDef.getClassName());
                // sets the object
                result.setObject(clazz.newInstance());
                result.setLoader(loader);
            } else {
                throw new IOException(UtilMessage.JEMB009E.toMessage().getMessage());
            }
        }
        return result;
    }

}