package org.eclipse.osgi.internal.framework;
import java.io.IOException;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.osgi.internal.loader.BundleLoader;
import org.eclipse.osgi.internal.loader.ModuleClassLoader;
public class ContextFinder extends ClassLoader implements PrivilegedAction<List<ClassLoader>> {
static final class Finder extends SecurityManager {
@Override
public Class<?>[] getClassContext() {
return super.getClassContext();
}
}
private static ThreadLocal<Set<String>> cycleDetector = new ThreadLocal<>();
static ClassLoader finderClassLoader;
static Finder contextFinder;
static {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void 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, ClassLoader bootLoader) {
super(contextClassLoader);
this.parentContextClassLoader = contextClassLoader != null ? contextClassLoader : bootLoader;
}
List<ClassLoader> basicFindClassLoaders() {
Class<?>[] stack = contextFinder.getClassContext();
List<ClassLoader> result = new ArrayList<>(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;
}
}
if (tmp == finderClassLoader || tmp instanceof ModuleClassLoader)
break;
}
}
return result;
}
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);
}
@Override
public List<ClassLoader> run() {
return basicFindClassLoaders();
}
private boolean startLoading(String name) {
Set<String> classesAndResources = cycleDetector.get();
if (classesAndResources != null && classesAndResources.contains(name))
return false;
if (classesAndResources == null) {
classesAndResources = new HashSet<>(3);
cycleDetector.set(classesAndResources);
}
classesAndResources.add(name);
return true;
}
private void stopLoading(String name) {
cycleDetector.get().remove(name);
}
@Override
protected Class<?> loadClass(String arg0, boolean arg1) throws ClassNotFoundException {
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) {
}
return parentContextClassLoader.loadClass(arg0);
} finally {
stopLoading(arg0);
}
}
@Override
public URL getResource(String arg0) {
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;
}
return super.getResource(arg0);
} finally {
stopLoading(arg0);
}
}
@Override
public Enumeration<URL> getResources(String arg0) throws IOException {
if (startLoading(arg0) == false) {
return Collections.enumeration(Collections.<URL> emptyList());
}
try {
List<ClassLoader> toConsult = findClassLoaders();
Enumeration<URL> result = null;
for (Iterator<ClassLoader> loaders = toConsult.iterator(); loaders.hasNext();) {
result = loaders.next().getResources(arg0);
if (result != null && result.hasMoreElements()) {
break;
}
}
return BundleLoader.compoundEnumerations(result, super.getResources(arg0));
} finally {
stopLoading(arg0);
}
}
}