Java tutorial
/** * 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); } }