com.aionlightning.commons.scripting.impl.javacompiler.ScriptClassLoaderImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.aionlightning.commons.scripting.impl.javacompiler.ScriptClassLoaderImpl.java

Source

/**
 * This file is part of aion-lightning <aion-lightning.org>.
 * 
 * aion-lightning 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.
 * 
 * aion-lightning 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 aion-lightning.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.aionlightning.commons.scripting.impl.javacompiler;

import com.aionlightning.commons.scripting.ScriptClassLoader;
import com.aionlightning.commons.utils.ClassUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.tools.JavaFileObject;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.*;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;

/**
 * This classloader is used to load script classes. <br>
 * <br>
 * Due to JavaCompiler limitations we have to keep list of available classes here.
 * 
 * @author SoulKeeper
 */
public class ScriptClassLoaderImpl extends ScriptClassLoader {

    private static final Logger log = LoggerFactory.getLogger(ScriptClassLoaderImpl.class);

    /**
     * ClassFileManager that is related to this ClassLoader
     */
    private final ClassFileManager classFileManager;

    /**
     * Creates new ScriptClassLoader with given ClassFileManger. <br>
     * Parent ClassLoader is ClassLoader of current class: <pre>ScriptClassLoaderImpl.class.getClassLoader()</pre>
     *
     * @param classFileManager
     *          classFileManager of this classLoader
     */
    ScriptClassLoaderImpl(ClassFileManager classFileManager) {
        super(new URL[] {}, ScriptClassLoaderImpl.class.getClassLoader());
        this.classFileManager = classFileManager;
    }

    /**
     * Creates new ScriptClassLoader with given ClassFileManger and another classLoader as parent
     * 
     * @param classFileManager
     *          classFileManager of this classLoader
     * @param parent
     *          parent classLoader
     */
    ScriptClassLoaderImpl(ClassFileManager classFileManager, ClassLoader parent) {
        super(new URL[] {}, parent);
        this.classFileManager = classFileManager;
    }

    /**
     * Returns ClassFileManager that is related to this ClassLoader
     * 
     * @return classFileManager of this classLoader
     */
    public ClassFileManager getClassFileManager() {
        return classFileManager;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Set<String> getCompiledClasses() {
        Set<String> compiledClasses = classFileManager.getCompiledClasses().keySet();
        return Collections.unmodifiableSet(compiledClasses);
    }

    /**
     * Returns list of classes that are members of a package
     * 
     * @param packageName
     *          package to search for classes
     * @return list of classes that are package members
     * @throws IOException
     *           if was unable to load class
     */
    public Set<JavaFileObject> getClassesForPackage(String packageName) throws IOException {
        Set<JavaFileObject> result = new HashSet<JavaFileObject>();

        // load parent
        ClassLoader parent = getParent();
        if (parent instanceof ScriptClassLoaderImpl) {
            ScriptClassLoaderImpl pscl = (ScriptClassLoaderImpl) parent;
            result.addAll(pscl.getClassesForPackage(packageName));
        }

        // load current classloader compiled classes
        for (String cn : classFileManager.getCompiledClasses().keySet()) {
            if (ClassUtils.isPackageMember(cn, packageName)) {
                BinaryClass bc = classFileManager.getCompiledClasses().get(cn);
                result.add(bc);
            }
        }

        // initialize set with class names, will be used to resolve classes
        Set<String> classNames = new HashSet<String>();

        // load package members from this classloader
        Enumeration<URL> urls = getResources(packageName.replace('.', '/'));
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            String path = URLDecoder.decode(url.getPath());
            if (new File(path).isDirectory()) {
                Set<String> packageClasses = ClassUtils.getClassNamesFromPackage(new File(path), packageName,
                        false);
                classNames.addAll(packageClasses);
            } else if (path.toLowerCase().contains(".jar!")) {
                File file = new File(path);
                while (!file.getName().toLowerCase().endsWith(".jar!")) {
                    file = file.getParentFile();
                }
                path = file.getPath().substring(0, file.getPath().length() - 1);
                path = path.replace('\\', '/');
                path = path.substring(path.indexOf(":") + 1);
                file = new File(path);
                // add jar file as library. Actually it's doesn't matter if we have it as library
                // or as file in class path
                addJarFile(file);
            }
        }

        // add library class names from this classloader to available classes
        classNames.addAll(getLibraryClassNames());

        // load classes for class names from this classloader
        for (String cn : classNames) {
            if (ClassUtils.isPackageMember(cn, packageName)) {
                BinaryClass bc = new BinaryClass(cn);
                try {
                    byte[] data = getRawClassByName(cn);
                    OutputStream os = bc.openOutputStream();
                    os.write(data);
                } catch (IOException e) {
                    log.error("Error while loading class from package " + packageName, e);
                    throw e;
                }
                result.add(bc);
            }
        }

        return result;
    }

    /**
     * Finds class with the specified name from the URL search path. Any URLs referring to JAR files are loaded and opened
     * as needed until the class is found.
     * 
     * @param name
     *          the name of the class
     * @return the resulting class data
     * @throws IOException
     *           if failed to load class
     * @throws IllegalArgumentException if failed to open input stream for class
     */
    protected byte[] getRawClassByName(String name) throws IOException {
        String resourceName = name.replace('.', '/').concat(".class");
        URL resource = getResource(resourceName);
        InputStream is = null;
        byte[] clazz = null;

        try {
            is = resource.openStream();
            clazz = IOUtils.toByteArray(is);
        } catch (IOException e) {
            log.error("Error while loading class data: " + name, e);
            throw e;
        } catch (NullPointerException e) {
            log.error("Can't open input stream for resource: " + name);
            throw new IllegalArgumentException("Failed to open input stream for resource: " + name);
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    log.error("Error while closing stream", e);
                }
            }
        }
        return clazz;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public byte[] getByteCode(String className) {
        BinaryClass bc = getClassFileManager().getCompiledClasses().get(className);
        byte[] b = new byte[bc.getBytes().length];
        System.arraycopy(bc.getBytes(), 0, b, 0, b.length);
        return b;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Class<?> getDefinedClass(String name) {
        BinaryClass bc = classFileManager.getCompiledClasses().get(name);
        if (bc == null) {
            return null;
        }

        return bc.getDefinedClass();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setDefinedClass(String name, Class<?> clazz) {
        BinaryClass bc = classFileManager.getCompiledClasses().get(name);

        if (bc == null) {
            throw new IllegalArgumentException("Attempt to set defined class for class that was not compiled?");
        }

        bc.setDefinedClass(clazz);
    }
}