diff options
Diffstat (limited to 'bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/ContextFinder.java')
-rw-r--r-- | bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/ContextFinder.java | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/ContextFinder.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/ContextFinder.java new file mode 100644 index 000000000..9ecc15b45 --- /dev/null +++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/ContextFinder.java @@ -0,0 +1,175 @@ +/******************************************************************************* + * Copyright (c) 2005, 2010 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.core.runtime.internal.adaptor; + +import java.io.IOException; +import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.*; +import org.eclipse.osgi.framework.adaptor.BundleClassLoader; + +public class ContextFinder extends ClassLoader implements PrivilegedAction<List<ClassLoader>> { + static final class Finder extends SecurityManager { + public Class<?>[] getClassContext() { + return super.getClassContext(); + } + } + + //This is used to detect cycle that could be caused while delegating the loading to other classloaders + //It keeps track on a thread basis of the set of requested classes and resources + private static ThreadLocal<Set<String>> cycleDetector = new ThreadLocal<Set<String>>(); + static ClassLoader finderClassLoader; + static Finder contextFinder; + static { + AccessController.doPrivileged(new PrivilegedAction<Object>() { + public Object run() { + finderClassLoader = ContextFinder.class.getClassLoader(); + contextFinder = new Finder(); + return null; + } + }); + } + + private static Class<ContextFinder> THIS = ContextFinder.class; + + private final ClassLoader parentContextClassLoader; + + public ContextFinder(ClassLoader contextClassLoader) { + super(contextClassLoader); + this.parentContextClassLoader = contextClassLoader != null ? contextClassLoader : new ClassLoader(Object.class.getClassLoader()) {/*boot classloader*/}; + } + + // Return a list of all classloaders on the stack that are neither the + // ContextFinder classloader nor the boot classloader. The last classloader + // in the list is either a bundle classloader or the framework's classloader + // We assume that the bootclassloader never uses the context classloader to find classes in itself. + List<ClassLoader> basicFindClassLoaders() { + Class<?>[] stack = contextFinder.getClassContext(); + List<ClassLoader> result = new ArrayList<ClassLoader>(1); + ClassLoader previousLoader = null; + for (int i = 1; i < stack.length; i++) { + ClassLoader tmp = stack[i].getClassLoader(); + if (stack[i] != THIS && tmp != null && tmp != this) { + if (checkClassLoader(tmp)) { + if (previousLoader != tmp) { + result.add(tmp); + previousLoader = tmp; + } + } + // stop at the framework classloader or the first bundle classloader + if (tmp == finderClassLoader || tmp instanceof BundleClassLoader) + break; + } + } + return result; + } + + // ensures that a classloader does not have the ContextFinder as part of the + // parent hierachy. A classloader which has the ContextFinder as a parent must + // not be used as a delegate, otherwise we endup in endless recursion. + private boolean checkClassLoader(ClassLoader classloader) { + if (classloader == null || classloader == getParent()) + return false; + for (ClassLoader parent = classloader.getParent(); parent != null; parent = parent.getParent()) + if (parent == this) + return false; + return true; + } + + private List<ClassLoader> findClassLoaders() { + if (System.getSecurityManager() == null) + return basicFindClassLoaders(); + return AccessController.doPrivileged(this); + } + + public List<ClassLoader> run() { + return basicFindClassLoaders(); + } + + //Return whether the request for loading "name" should proceed. + //False is returned when a cycle is being detected + private boolean startLoading(String name) { + Set<String> classesAndResources = cycleDetector.get(); + if (classesAndResources != null && classesAndResources.contains(name)) + return false; + + if (classesAndResources == null) { + classesAndResources = new HashSet<String>(3); + cycleDetector.set(classesAndResources); + } + classesAndResources.add(name); + return true; + } + + private void stopLoading(String name) { + cycleDetector.get().remove(name); + } + + protected Class<?> loadClass(String arg0, boolean arg1) throws ClassNotFoundException { + //Shortcut cycle + if (startLoading(arg0) == false) + throw new ClassNotFoundException(arg0); + + try { + List<ClassLoader> toConsult = findClassLoaders(); + for (Iterator<ClassLoader> loaders = toConsult.iterator(); loaders.hasNext();) + try { + return loaders.next().loadClass(arg0); + } catch (ClassNotFoundException e) { + // go to the next class loader + } + // avoid calling super.loadClass here because it checks the local cache (bug 127963) + return parentContextClassLoader.loadClass(arg0); + } finally { + stopLoading(arg0); + } + } + + public URL getResource(String arg0) { + //Shortcut cycle + if (startLoading(arg0) == false) + return null; + try { + List<ClassLoader> toConsult = findClassLoaders(); + for (Iterator<ClassLoader> loaders = toConsult.iterator(); loaders.hasNext();) { + URL result = loaders.next().getResource(arg0); + if (result != null) + return result; + // go to the next class loader + } + return super.getResource(arg0); + } finally { + stopLoading(arg0); + } + } + + protected Enumeration<URL> findResources(String arg0) throws IOException { + //Shortcut cycle + if (startLoading(arg0) == false) { + @SuppressWarnings("unchecked") + Enumeration<URL> result = Collections.enumeration(Collections.EMPTY_LIST); + return result; + } + try { + List<ClassLoader> toConsult = findClassLoaders(); + for (Iterator<ClassLoader> loaders = toConsult.iterator(); loaders.hasNext();) { + Enumeration<URL> result = loaders.next().getResources(arg0); + if (result != null && result.hasMoreElements()) + return result; + // go to the next class loader + } + return super.findResources(arg0); + } finally { + stopLoading(arg0); + } + } +} |