org.ms123.common.libhelper.FileSystemClassLoader.java Source code

Java tutorial

Introduction

Here is the source code for org.ms123.common.libhelper.FileSystemClassLoader.java

Source

/**
 * This file is part of SIMPL4(http://simpl4.org).
 *
 *    Copyright [2014] [Manfred Sattler] <manfred@ms123.org>
 *
 * SIMPL4 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
 * (at your option) any later version.
 *
 * SIMPL4 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 SIMPL4.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.ms123.common.libhelper;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.commons.io.FileUtils.readFileToByteArray;

/**
 */
public class FileSystemClassLoader extends ClassLoader {

    private File[] locations;
    private String[] includePattern;

    private boolean loadCustomClassFirst = true;

    private boolean isValid = true;

    private Map<String, Class<?>> loadedClasses = new ConcurrentHashMap<String, Class<?>>();

    public FileSystemClassLoader(File[] locations) {
        super(FileSystemClassLoader.getDefaultClassLoader());
        this.locations = locations;
    }

    public FileSystemClassLoader(ClassLoader parent, File[] locations) {
        this(parent, locations, null);
    }

    public FileSystemClassLoader(ClassLoader parent, File[] locations, String[] includePattern) {
        super(parent);
        this.locations = locations;
        this.includePattern = includePattern;
        for (File f : locations) {
            debug("location:" + f.toString());
        }
    }

    protected Class<?> loadCustomClass(String className) {
        byte[] classData = null;
        try {
            classData = findClassBytes(className);
            if (classData != null) {
                Class<?> result = defineClass(className, classData, 0, classData.length, null);
                loadedClasses.put(className, result);
                debug(className + " loaded");
                return result;
            }
            return null;
        } catch (IOException e) {
            error("IOException attempting to read class " + className + " from location(s) "
                    + Arrays.toString(locations));
            return null;
        } catch (ClassFormatError e) {
            error("Invalid format for class " + className + " from location(s) " + Arrays.toString(locations));
            return null;
        }
    }

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }

    public synchronized Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
        //System.out.println("\tloadClass:"+className);
        Class<?> toReturn = null;
        if (!isValid) {
            //System.out.println("loadClass:isValid:"+this);
            return toReturn;
        }
        if (toReturn == null) {
            toReturn = getAlreadyLoadedClass(className);
        }
        if (loadCustomClassFirst) {
            if (toReturn == null) {
                toReturn = loadCustomClass(className);
            }
            if (toReturn == null) {
                toReturn = loadParentClass(className);
            }
        } else {
            if (toReturn == null) {
                toReturn = loadParentClass(className);
            }
            if (toReturn == null) {
                toReturn = loadCustomClass(className);
            }
        }
        if (toReturn == null) {
            debug("Class not found: " + className);
            throw new ClassNotFoundException(className);
        } else {
            if (resolve) {
                resolveClass(toReturn);
            }
        }
        return toReturn;
    }

    public void setInvalid() {
        isValid = false;
    }

    protected byte[] findClassBytes(String className) throws IOException {
        byte[] classData = null;
        String relativeClassFile = className.replace('.', '/') + ".class";
        boolean include = includePattern == null;

        if (!include) {
            for (int i = 0; i < includePattern.length; i++) {
                if (className.matches(includePattern[i])) {
                    include = true;
                    break;
                }
            }
        }
        debug("findClassBytes:" + className + "=" + include + "/"
                + (includePattern != null ? includePattern[0] : ""));
        if (!include) {
            return null;
        }
        for (int i = 0; i < locations.length; i++) {
            File file = new File(locations[i], relativeClassFile);
            debug("\tfindClassBytes.file:" + file + "/" + file.exists());
            if (file.exists()) {
                classData = readFileToByteArray(file);
                break;
            }
        }
        if (classData != null) {
        } else {
        }
        return classData;
    }

    /**
     * Returns a cached <code>Class</code> instance, if it has already been
     * loaded using the <code>ClassLoader</code>. Designed to be used by
     * subclasses.
     * @param className the name of the class to return
     * @return returns the cached <code>Class</code> instance, if previously
     * loaded using this <code>ClassLoader</code>, otherwise null.
     */
    protected Class<?> getAlreadyLoadedClass(String className) {
        Class<?> loadedClass = loadedClasses.get(className);
        if (loadedClass != null) {
            debug("Returning already loaded custom class: " + className);
            return loadedClass;
        }
        return loadedClass;
    }

    /**
     * Attempts to load the designated class by delegating to the parent class
     * loader.
     * @param className the name of the class to load
     * @return a <code>Class</code> instance successfully loaded, otherwise
     * null
     */
    protected Class<?> loadParentClass(String className) {
        try {
            Class<?> parentClass = getParent().loadClass(className);
            debug("Returning from parent class loader {}: {}" + getParent() + ": " + parentClass);
            return parentClass;
        } catch (Exception e) {
        }
        return null;
    }

    /**
     * Attempt to load a resoure, first by calling
     * <code>getCustomResource</code>. If the resource is not found
     * <code>super.getResource(name)</code> is called.
     */
    @Override
    public URL getResource(String name) {
        final URL url = getCustomResource(name);
        if (url != null) {
            return url;
        }
        return super.getResource(name);
    }

    /**
     * Attempts to find a resource from one of the file system locations
     * specified in a constructor.
     * @param name the name of the resource to load
     * @return a <code>URL</code> instance, if the resource can be found,
     * otherwise null.
     */
    protected URL getCustomResource(String name) {
        try {
            for (int i = 0; i < locations.length; i++) {
                File file = new File(locations[i], name);
                if (file.exists()) {
                    URL url = file.toURI().toURL();
                    return url;
                }
            }
        } catch (IOException e) {
            error("IOException attempting to load resource " + name + " from location(s) "
                    + Arrays.toString(locations));
        }
        return null;
    }

    /**
     * Returns an immutable map of name to <code>Class</code> instances loaded
     * via this class loader
     * @return
     */
    public Map<String, Class<?>> getLoadedClasses() {
        return Collections.unmodifiableMap(loadedClasses);
    }

    protected File[] getLocations() {
        return locations;
    }

    /*private void debug(String message) {
       logger.debug(this.getClass().getSimpleName() + "[" + System.identityHashCode(this) + "]: " + message);
    }*/

    public static ClassLoader getDefaultClassLoader() {
        ClassLoader cl = null;
        try {
            cl = Thread.currentThread().getContextClassLoader();
        } catch (Throwable ex) {
        }
        if (cl == null) {
            // No thread context class loader -> use class loader of this class.
            cl = FileSystemClassLoader.class.getClassLoader();
        }
        return cl;
    }

    protected void debug(String message) {
        m_logger.debug(message);
    }

    protected void error(String message) {
        m_logger.error(message);
        System.out.println(message);
    }

    protected void info(String message) {
        m_logger.info(message);
        System.out.println(message);
    }

    private static final Logger m_logger = LoggerFactory.getLogger(FileSystemClassLoader.class);
}