Java tutorial
/* * This file is part of l2jserver <l2jserver.com>. * * l2jserver 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. * * l2jserver 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 l2jserver. If not, see <http://www.gnu.org/licenses/>. */ package com.l2jserver.service.game.scripting.impl.ecj; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.Collections; import java.util.Enumeration; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; import javax.tools.JavaFileObject; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.l2jserver.service.game.scripting.ScriptClassLoader; import com.l2jserver.service.game.scripting.impl.BinaryClass; import com.l2jserver.util.ClassUtils; import com.l2jserver.util.factory.CollectionFactory; /** * This classloader is used to load script classes. <br> * <br> * Due to JavaCompiler limitations we have to keep list of available classes * here. * * @author <a href="http://www.rogiel.com">Rogiel</a> */ public class EclipseCompilerScriptClassLoader extends ScriptClassLoader { /** * Logger */ private static final Logger log = LoggerFactory.getLogger(EclipseCompilerScriptClassLoader.class); /** * URL Stream handler to allow valid url generation by * {@link #getResource(String)} */ private final EclipseCompilerVirtualClassURLStreamHandler urlStreamHandler = new EclipseCompilerVirtualClassURLStreamHandler( this); /** * ClassFileManager that is related to this ClassLoader */ private final EclipseCompilerClassFileManager classFileManager; /** * Classes that were loaded from libraries. They are no parsed for any * annotations, but they are needed by JavaCompiler to perform valid * compilation */ private Set<String> libraryClasses = CollectionFactory.newSet(); /** * Creates new ScriptClassLoader with given ClassFileManger * * @param classFileManager * classFileManager of this classLoader */ EclipseCompilerScriptClassLoader(EclipseCompilerClassFileManager classFileManager) { super(new URL[] {}); this.classFileManager = classFileManager; } /** * Creates new ScriptClassLoader with given ClassFileManger and another * classLoader as parent * * @param classFileManager * classFileManager of this classLoader * @param parent * parent classLoader */ EclipseCompilerScriptClassLoader(EclipseCompilerClassFileManager classFileManager, ClassLoader parent) { super(new URL[] {}, parent); this.classFileManager = classFileManager; } /** * Returns ClassFileManager that is related to this ClassLoader * * @return classFileManager of this classLoader */ public EclipseCompilerClassFileManager getClassFileManager() { return classFileManager; } /** * AddsLibrary jar * * @param file * jar file to add * @throws IOException * if any I/O error occur */ @Override public void addLibrary(File file) throws IOException { URL fileURL = file.toURI().toURL(); addURL(fileURL); JarFile jarFile = new JarFile(file); Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); String name = entry.getName(); if (name.endsWith(".class")) { name = name.substring(0, name.length() - 6); name = name.replace('/', '.'); libraryClasses.add(name); } } jarFile.close(); } /** * Loads class from library, parent or compiled * * @param name * class to load * @return loaded class * @throws ClassNotFoundException * if class not found */ @Override public Class<?> loadClass(String name) throws ClassNotFoundException { BinaryClass bc = classFileManager.getCompiledClasses().get(name); if (bc == null) { return super.loadClass(name, true); } Class<?> c = bc.getDefinedClass(); if (c == null) { byte[] b = bc.getBytes(); c = super.defineClass(name.replace('/', '.'), b, 0, b.length); bc.setDefinedClass(c); } return c; } /** * {@inheritDoc} */ @Override public URL getResource(String name) { if (!name.endsWith(".class")) { return super.getResource(name); } else { String newName = name.substring(0, name.length() - 6); newName = newName.replace('/', '.'); if (classFileManager.getCompiledClasses().containsKey(newName)) { try { return new URL(null, EclipseCompilerVirtualClassURLStreamHandler.HANDLER_PROTOCOL + newName, urlStreamHandler); } catch (MalformedURLException e) { log.error("Can't create url for compiled class", e); } } } return super.getResource(name); } /** * {@inheritDoc} */ @Override public Set<String> getLibraryClasses() { return Collections.unmodifiableSet(libraryClasses); } /** * {@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 = CollectionFactory.newSet(); // load parent ClassLoader parent = getParent(); if (parent instanceof EclipseCompilerScriptClassLoader) { EclipseCompilerScriptClassLoader pscl = (EclipseCompilerScriptClassLoader) 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); } } // load libraries for (String cn : libraryClasses) { 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 the class could not be found */ protected byte[] getRawClassByName(String name) throws IOException { URL resource = findResource(name.replace('.', '/').concat(".class")); InputStream is = null; byte[] clazz = null; try { is = resource.openStream(); clazz = IOUtils.toByteArray(is); } catch (IOException e) { log.error("Error while loading class data", e); throw e; } finally { if (is != null) { try { is.close(); } catch (IOException e) { log.error("Error while closing stream", e); } } } return clazz; } }