package org.embulk.plugin;
import java.util.List;
import java.util.Collection;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.Enumeration;
import java.io.IOException;
import java.nio.file.Path;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.MalformedURLException;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
public class PluginClassLoader
extends URLClassLoader
{
private final List<String> parentFirstPackagePrefixes;
private final List<String> parentFirstResourcePrefixes;
public PluginClassLoader(Collection<URL> urls, ClassLoader parent,
Collection<String> parentFirstPackages,
Collection<String> parentFirstResources)
{
super(urls.toArray(new URL[urls.size()]), parent);
this.parentFirstPackagePrefixes = ImmutableList.copyOf(
Iterables.transform(parentFirstPackages, new Function<String, String>() {
public String apply(String pkg)
{
return pkg + ".";
}
}));
this.parentFirstResourcePrefixes = ImmutableList.copyOf(
Iterables.transform(parentFirstResources, new Function<String, String>() {
public String apply(String pkg)
{
return pkg + "/";
}
}));
}
public void addPath(Path path)
{
try {
addUrl(path.toUri().toURL());
} catch (MalformedURLException ex) {
throw new IllegalArgumentException(ex);
}
}
public void addUrl(URL url)
{
super.addURL(url);
}
@Override
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
Class<?> loadedClass = findLoadedClass(name);
if (loadedClass != null) {
return resolveClass(loadedClass, resolve);
}
boolean parentFirst = isParentFirstPackage(name);
if (!parentFirst) {
try {
return resolveClass(findClass(name), resolve);
} catch (ClassNotFoundException ignored) {
}
}
try {
return resolveClass(getParent().loadClass(name), resolve);
} catch (ClassNotFoundException ignored) {
}
if (parentFirst) {
return resolveClass(findClass(name), resolve);
}
throw new ClassNotFoundException(name);
}
}
private Class<?> resolveClass(Class<?> clazz, boolean resolve)
{
if (resolve) {
resolveClass(clazz);
}
return clazz;
}
@Override
public URL getResource(String name)
{
boolean childFirst = isParentFirstPath(name);
if (childFirst) {
URL childUrl = findResource(name);
if (childUrl != null) {
return childUrl;
}
}
URL parentUrl = getParent().getResource(name);
if (parentUrl != null) {
return parentUrl;
}
if (!childFirst) {
URL childUrl = findResource(name);
if (childUrl != null) {
return childUrl;
}
}
return null;
}
@Override
public Enumeration<URL> getResources(String name)
throws IOException
{
List<Iterator<URL>> resources = new ArrayList<>();
boolean parentFirst = isParentFirstPath(name);
if (!parentFirst) {
Iterator<URL> childResources = Iterators.forEnumeration(findResources(name));
resources.add(childResources);
}
Iterator<URL> parentResources = Iterators.forEnumeration(getParent().getResources(name));
resources.add(parentResources);
if (parentFirst) {
Iterator<URL> childResources = Iterators.forEnumeration(findResources(name));
resources.add(childResources);
}
return Iterators.asEnumeration(Iterators.concat(resources.iterator()));
}
private boolean isParentFirstPackage(String name)
{
for (String pkg : parentFirstPackagePrefixes) {
if (name.startsWith(pkg)) {
return true;
}
}
return false;
}
private boolean isParentFirstPath(String name)
{
for (String path : parentFirstResourcePrefixes) {
if (name.startsWith(path)) {
return true;
}
}
return false;
}
}