package org.eclipse.vjet.eclipse.javatojs.ui.commands; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.security.CodeSigner; import java.security.CodeSource; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.jar.Attributes; import java.util.jar.Attributes.Name; import java.util.jar.Manifest; import org.eclipse.vjet.eclipse.javatojs.ui.commands.jar.JarClassData; import org.eclipse.vjet.eclipse.javatojs.ui.commands.jar.JarDictionary; import org.eclipse.vjet.eclipse.javatojs.ui.commands.jar.JarDictionaryUtil; /** * The EclipseWorkspaceClassLoader is used as a way to give the plugin access to * the code written within the runtime eclipse workspace. In other words, if * this plugin is installed onto the eclipse development enviroment, then this * class loader gives us the ability to load classes within the eclipse IDE * dynamically. * <p> * The current loading order is to attempt to load all things locally first with * the existing runtime class loader being the parent. If unfound, it will * attempt to locate from within the list of URLs added to this classloader. */ public class VJETPluginClassloader extends URLClassLoader { Set<JarDictionary> m_jarFiles = new HashSet<JarDictionary>(); private static VJETPluginClassloader classLoader = null; private long m_creationTime; public VJETPluginClassloader(URL[] urls, ClassLoader parent) throws IOException { super(new UrlProcessor(null, new ClassloaderUrlFilter()) .getFilteredUrls(urls), parent); m_creationTime = System.currentTimeMillis(); URL[] jarUrls = null; jarUrls = new UrlProcessor(new ClassloaderUrlFilter(), null) .getFilteredUrls(urls); for (URL url : jarUrls) { JarDictionary jarDictionary = new JarDictionary(url); JarDictionaryUtil.fillJarDictionary(url, jarDictionary); m_jarFiles.add(jarDictionary); } } /** * * @return Returns the time when this object was created. */ public long getCreationTime() { return m_creationTime; } public URL[] getURLs() { URL[] baseUrls = super.getURLs(); List<URL> urls = new ArrayList<URL>(baseUrls.length + m_jarFiles.size()); // Add the base urls for (URL url : baseUrls) { urls.add(url); } // Add our managed urls for (JarDictionary jarDictionary : m_jarFiles) { urls.add(jarDictionary.getUrl()); } return urls.toArray(new URL[urls.size()]); } @Override public URL findResource(final String name) { // Look for the class with the parents class loader URL systemURL = getParent().getResource(name); if (systemURL != null) return systemURL; // Look for the resource with the super's class loader URL jarURL = super.findResource(name); if (jarURL != null) return jarURL; // Now try and search our selves. JarClassData jarClassData = getJarClassData(name); if (jarClassData != null) { // Resources in jars are in the format of: // jar:file:/D:/ccviews/ddodd_v4_v2/BuildOutput/modules50/jetty-6.0.0.jar!/org/mortbay/jetty/mime.properties String url = jarClassData.getUrl().toExternalForm(); jarURL = jarClassData.getUrl(); } return jarURL; } @Override public Enumeration<URL> findResources(String name) throws IOException { // // Look for the class with the parents class loader // Enumeration<URL> systemURL = getParent().getResources(name); // if (systemURL != null && systemURL.hasMoreElements()) // return systemURL; // // // Look for the resource with the super's class loader // Enumeration<URL> jarURL = super.findResources(name); // if (jarURL != null && jarURL.hasMoreElements()) // return jarURL; // Now try and search our selves. final List<JarClassData> jarClassData = getJarResources(name); final List<URL> urls = new ArrayList<URL>(); for (JarClassData jar : jarClassData) { urls.add(jar.getUrl()); } final Iterator<URL> it = urls.iterator(); return new Enumeration<URL>() { public boolean hasMoreElements() { return it.hasNext(); } public URL nextElement() { return it.next(); } }; } @Override public Class<?> findClass(final String name) throws ClassNotFoundException { Class<?> loadedClass = findLoadedClass(name); if (loadedClass != null) { return loadedClass; } // System.out.println("Find Class" + getParent().getClass().getName()); // Look for the class with the parents class loader try { Class systemClass = getParent().loadClass(name); if (systemClass != null) return systemClass; } catch (ClassNotFoundException cnf) { // Class not found, keep looking } // Look for the class with the super's class loader try { Class jarClass = super.findClass(name); if (jarClass != null) return jarClass; } catch (ClassNotFoundException cnf) { // Class not found, keep looking } // Now try and search our selves. String path = name.replace('.', '/').concat(".class"); JarClassData jarClassData = getJarClassData(path); if (jarClassData != null) { try { return defineClass(name, jarClassData); } catch (IOException e) { throw new ClassNotFoundException(name, e); } } else { throw new ClassNotFoundException(name); } } JarClassData getJarClassData(String className) { for (JarDictionary jarDictionary : m_jarFiles) { if (jarDictionary.jarContainsClass(className)) return jarDictionary.getJarClassData(className); } return null; } List<JarClassData> getJarResources(String resName) { List<JarClassData> resources = new ArrayList<JarClassData>(); for (JarDictionary jarDictionary : m_jarFiles) { if (jarDictionary.jarContainsClass(resName)) { resources.add(jarDictionary.getJarClassData(resName)); } } return resources; } private Class defineClass(String name, JarClassData jarClassData) throws IOException, ClassNotFoundException { // System.out.println("Define Class" + name); int i = name.lastIndexOf('.'); URL url = jarClassData.getCodeSourceURL(); if (i != -1) { String pkgname = name.substring(0, i); // Check if package already loaded. Package pkg = getPackage(pkgname); Manifest man = null; if (pkg != null) { // Package found, so check package sealing. if (pkg.isSealed()) { // Verify that code source URL is the same. if (!pkg.isSealed(url)) { throw new SecurityException( "sealing violation: package " + pkgname + " is sealed"); } } else { // Make sure we are not attempting to seal the package // at this code source URL. if ((man != null) && isSealed(pkgname, man)) { throw new SecurityException( "sealing violation: can't seal package " + pkgname + ": already loaded"); } } } else { if (man != null) { definePackage(pkgname, man, url); } else { definePackage(pkgname, null, null, null, null, null, null, null); } } } // Now read the class bytes and define the class byte[] bb = jarClassData.getZipEntryData(); if (bb != null) { // Use (direct) ByteBuffer: CodeSigner[] signers = jarClassData.getCodeSigners(); CodeSource cs = new CodeSource(url, signers); return defineClass(name, bb, 0, bb.length, cs); } else { byte[] b = jarClassData.getBytes(); // must read certificates AFTER reading bytes. CodeSigner[] signers = jarClassData.getCodeSigners(); CodeSource cs = new CodeSource(url, signers); return defineClass(name, b, 0, b.length, cs); } } /* * Returns true if the specified package name is sealed according to the * given manifest. */ private boolean isSealed(String name, Manifest man) { String path = name.replace('.', '/').concat("/"); Attributes attr = man.getAttributes(path); String sealed = null; if (attr != null) { sealed = attr.getValue(Name.SEALED); } if (sealed == null) { if ((attr = man.getMainAttributes()) != null) { sealed = attr.getValue(Name.SEALED); } } return "true".equalsIgnoreCase(sealed); } public void addWorkspaceUrl(URL url) throws IOException { ClassloaderUrlFilter urlFilter = new ClassloaderUrlFilter(); if (urlFilter.filter(url) == true) { JarDictionary jarDictionary = new JarDictionary(url); if (m_jarFiles.contains(jarDictionary) == false) { JarDictionaryUtil.fillJarDictionary(url, jarDictionary); m_jarFiles.add(jarDictionary); } else { System.out.println("Skipping"); } } else { addURL(url); } } /** * Overrides the super class method to see if workspace first checking is * enabled. If enabled, try to load the class from the known list of jars. */ @Override public Class<?> loadClass(String className) throws ClassNotFoundException { if (m_packageFragment != null && className.startsWith(m_packageFragment)) { // System.out.println("Load Class" + className); // Now try and search our selves. String path = className.replace('.', '/').concat(".class"); JarClassData jarClassData = getJarClassData(path); if (jarClassData != null && jarClassData.getUrl().getFile().contains( m_packageMustLiveInThisJar)) { try { return defineClass(className, jarClassData); } catch (IOException e) { throw new ClassNotFoundException(className, e); } } } return super.loadClass(className); } /** * Overrides the super class method to get resource bundles. If workspace * first loading is enabled, then try to load the .properties file from our * known list of jars. */ @Override public URL getResource(String resName) { if (m_packageFragment != null && resName.startsWith(m_packageFragment.replace('.', '/'))) { // Now try and search our selves. JarClassData jarClassData = getJarClassData(resName); if (jarClassData != null && jarClassData.getUrl().getFile().contains( m_packageMustLiveInThisJar)) { // Resources in jars are in the format of: // jar:file:/D:/ccviews/ddodd_v4_v2/BuildOutput/modules50/jetty-6.0.0.jar!/org/mortbay/jetty/mime.properties String url = jarClassData.getUrl().toExternalForm(); URL jarURL = jarClassData.getUrl(); return jarURL; } } return super.getResource(resName); } private String m_packageFragment = null; private String m_packageMustLiveInThisJar = null; /** * This method instructs the V4 plugin class loader to check if it can load * the class before delegrating to a super class. The whole reason for this * functionality is so ESF can be invoked from the V4 plugin while still * loading the version of JDT it was compiled against (3.1) as opposed to * loading classes from whatever version of JDT is visible in the current * Eclipse environment. * * @param packageFragment * package fragment to check, e.g. all packages at this level and * below will be checked to see if this class loader can load * them from it's known workspace jar list. * * @param jarFileName * specifies that if a package is found, it must live in this * jar. The idea here is to not load a class in the specified * package hierarchy if it lives in any other jar. Not likely, * but just being extra careful (read paranoid). * */ public void enableWorkspaceFirstLoading(String packageFragment, String jarFileName) { m_packageFragment = packageFragment; m_packageMustLiveInThisJar = jarFileName; } /** * This method disables workspace first classloadinig previously enabled by * a call to <code>enableWorkspaceFirstLoading</code> above. * */ public void disableWorkspaceFirstLoading() { m_packageFragment = null; m_packageMustLiveInThisJar = null; } }