package org.openflexo.foundation.dm; import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.logging.Level; import java.util.zip.ZipException; import org.apache.commons.io.FileUtils; public class JarClassLoader extends ClassLoader { private static final java.util.logging.Logger logger = org.openflexo.logging.FlexoLogger.getLogger(JarClassLoader.class.getPackage() .getName()); private Map<String, Class<?>> classForClassName; private List<File> jarDirectories; private Set<JarFile> jarFiles; private Set<File> bannedZip; public JarClassLoader(List<File> jarDirectories) { super(); this.jarDirectories = jarDirectories; jarFiles = new HashSet<JarFile>(); classForClassName = Collections.synchronizedMap(new HashMap<String, Class<?>>()); } @Override protected void finalize() throws Throwable { System.err.println("Finalizing Jar Class Loader"); } private boolean isClassFile(String jarentryname) { return !jarentryname.startsWith("WebServerResources/"); } private String parseClassName(String jarentryname) { String classname; int index = jarentryname.lastIndexOf(".class"); if (index - 1 > 0) { classname = jarentryname.substring(0, index); } else { classname = jarentryname; } classname = classname.replace('/', '.'); return classname; } @Override public Class<?> findClass(String className) { className = parseClassName(className); if (!isClassFile(className)) { if (logger.isLoggable(Level.WARNING)) { logger.warning("Class " + className + " is not a valid class"); } return null; } if (className != null && className.indexOf("WORPCProvider") > -1) { if (logger.isLoggable(Level.INFO)) { logger.info("Skipping load of class " + className + " because it can lead to System.exit(1) if invoked within a WOApplication"); } // We don't try to load this class because it contains a static block that tries to load an additional class which is not // automatically on the classpath. // If the load of that second class fails, then, if we are within a wo_application, the static block will perform // System.exit(1) causing the application to stop return null; } Class<?> tryToLookup = lookupClass(className, null, null); if (tryToLookup != null) { return tryToLookup; } return null; } private Class<?> lookupClass(String className, JarFile jarFile, JarEntry entry) { return lookupClass(className, jarFile, entry, false); } private Class<?> lookupClass(String className, JarFile jarFile, JarEntry entry, boolean useClassForNameOnly) { boolean addedJarToList = jarFile != null && jarFiles.add(jarFile); try { // Put the class name in right format className = parseClassName(className); // Look in this class loader Class<?> class1 = getClassForClassName().get(className); if (class1 != null) { return class1; } // Look in the application class loader try { Class<?> forName = Class.forName(className); if (forName != null) { classForClassName.put(className, forName); } return forName; } catch (ClassNotFoundException e) { } catch (NoClassDefFoundError e) { } catch (ExceptionInInitializerError e) { } catch (LinkageError e) { } if (useClassForNameOnly) { return null; } if (jarFile != null && entry != null) { return loadClass(jarFile, entry, className); } class1 = findInJars(className); if (class1 != null) { return class1; } if (className.indexOf('.') == -1) { class1 = lookupClass("java.lang." + className, null, null, true); } classForClassName.put(className, class1); if (class1 == null) { if (logger.isLoggable(Level.WARNING)) { logger.warning("Looked everywhere but I could not find " + className); } } return null; } finally { if (addedJarToList) { jarFiles.remove(jarFile); } } } private Class<?> findInJars(String className) { String jarEntryName = className.replace('.', '/') + ".class"; for (JarFile jarFile : this.jarFiles) { Class<?> klass = lookupClassInJarFile(jarFile, jarEntryName, className); if (klass != null) { return klass; } } List<File> jarFiles = new ArrayList<File>(); for (File jarDir : jarDirectories) { jarFiles.addAll(FileUtils.listFiles(jarDir, new String[] { "jar" }, false)); } if (bannedZip != null) { jarFiles.removeAll(bannedZip); } for (File file : jarFiles) { try { JarFile jarFile = new JarFile(file); Class<?> klass = lookupClassInJarFile(jarFile, jarEntryName, className); if (klass != null) { return klass; } } catch (IOException e) { e.printStackTrace(); if (e instanceof ZipException) { if (bannedZip == null) { bannedZip = new HashSet<File>(); } bannedZip.add(file); } } } return null; } public Class<?> lookupClassInJarFile(JarFile jarFile, String jarEntryName, String className) { JarEntry e = jarFile.getJarEntry(jarEntryName); if (e != null) { Class<?> klass = loadClass(jarFile, e, className); if (klass != null) { return klass; } } return null; } private Class<?> loadClass(JarFile jarFile, JarEntry entry, String className) { try { InputStream is = jarFile.getInputStream(entry); BufferedInputStream bis = new BufferedInputStream(is); byte[] content = new byte[(int) entry.getSize()]; bis.read(content, 0, (int) entry.getSize()); Class<?> returned = defineClass(className, content, 0, content.length); classForClassName.put(className, returned); return returned; } catch (ClassFormatError err) { if (logger.isLoggable(Level.WARNING)) { logger.warning("Error: " + err + " : class not loaded: " + className); } } catch (NoClassDefFoundError err) { if (logger.isLoggable(Level.WARNING)) { logger.warning("Error: " + err + " : class not loaded: " + className); } } catch (IllegalAccessError err) { if (logger.isLoggable(Level.WARNING)) { logger.warning("Error: " + err + " : class not loaded: " + className); } } catch (IOException err) { // Warns about the exception if (logger.isLoggable(Level.WARNING)) { logger.warning("Exception raised: " + err.getClass().getName() + ". See console for details."); } } catch (LinkageError err) { if (logger.isLoggable(Level.WARNING)) { logger.warning("Error: " + err + " : class not loaded: " + className); } } return null; } public Map<String, Class<?>> getClassForClassName() { return new HashMap<String, Class<?>>(classForClassName); } public void unloadClasses(List<String> classNames) { for (String className : classNames) { classForClassName.remove(className); } } public void unloadClass(Class<?> klass) { Class<?> class1 = classForClassName.get(klass.getName()); if (class1 != null) { if (class1 == klass) { classForClassName.remove(klass.getName()); } else { if (logger.isLoggable(Level.SEVERE)) { logger.severe("Class " + klass.getName() + " has been loaded twice in project."); } } } else { if (logger.isLoggable(Level.WARNING)) { logger.warning("Could not find class " + klass.getName()); } } } public Class<?> findClass(JarFile jarFile, JarEntry entry) { return lookupClass(parseClassName(entry.getName()), jarFile, entry); } }