package krasa.formatter.eclipse; import java.net.URL; import java.net.URLClassLoader; import java.util.HashSet; import java.util.Set; /** * A parent-last classloader that will try the child classloader first and then the parent. This takes a fair bit of * doing because java really prefers parent-first. * <p/> * For those not familiar with class loading trickery, be wary */ public class ParentLastURLClassLoader extends ClassLoader { final static Set<String> loadFromParent = new HashSet<String>(); { { loadFromParent.add("org.eclipse.wst.jsdt.core.formatter.CodeFormatter"); } } final static Set<String> neverLoadFromParentWithPrefix = new HashSet<String>(); { { neverLoadFromParentWithPrefix.add("org.eclipse."); neverLoadFromParentWithPrefix.add("krasa.formatter.adapter."); } } private ChildURLClassLoader childClassLoader; /** * This class allows me to call findClass on a classloader */ private static class FindClassClassLoader extends ClassLoader { public FindClassClassLoader(ClassLoader parent) { super(parent); } @Override public Class<?> findClass(String name) throws ClassNotFoundException { return super.findClass(name); } } /** * This class delegates (child then parent) for the findClass method for a URLClassLoader. We need this because * findClass is protected in URLClassLoader */ private static class ChildURLClassLoader extends URLClassLoader { private FindClassClassLoader realParent; public ChildURLClassLoader(URL[] urls, FindClassClassLoader realParent) { super(urls, null); this.realParent = realParent; } @Override public Class<?> findClass(String name) throws ClassNotFoundException { try { if (loadFromParent.contains(name)) { return realParent.loadClass(name); } //calling twic #findClass with the same classname, you will get a LinkageError, this fixes it Class<?> loaded = super.findLoadedClass(name); if (loaded != null) return loaded; // first try to use the URLClassLoader findClass return super.findClass(name); } catch (ClassNotFoundException e) { for (String forbiddenParentPrefixes : neverLoadFromParentWithPrefix) { if (name.startsWith(forbiddenParentPrefixes)) { throw new RuntimeException( name + " not found in child classloader, and cannot be loaded from parent", e); } } // if that fails, we ask our real parent classloader to load the class (we give up) return realParent.loadClass(name); } } } public ParentLastURLClassLoader(ClassLoader parent, URL... urls) { super(parent); childClassLoader = new ChildURLClassLoader(urls, new ParentLastURLClassLoader.FindClassClassLoader(this.getParent())); childClassLoader.setDefaultAssertionStatus(false); } @Override protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { try { // first we try to find a class inside the child classloader return childClassLoader.findClass(name); } catch (ClassNotFoundException e) { // didn't find it, try the parent return super.loadClass(name, resolve); } } }