package org.atomnuke.container.packaging.classloader;
import org.atomnuke.util.collections.EmptyEnumeration;
import org.atomnuke.util.collections.SingleValueEnumeration;
import com.rackspace.papi.commons.util.io.RawInputStreamReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;
import org.atomnuke.container.packaging.resource.Resource;
import org.atomnuke.container.packaging.resource.ResourceManagerImpl;
import org.atomnuke.container.packaging.resource.ResourceManager;
import org.atomnuke.container.packaging.resource.ResourceUtil;
public class IdentityClassLoader extends ClassLoader {
private static final Logger LOG = LoggerFactory.getLogger(IdentityClassLoader.class);
private final ResourceManager resourceManager;
private final ClassLoader parent;
public IdentityClassLoader() {
this(getSystemClassLoader(), new ResourceManagerImpl());
}
public IdentityClassLoader(ResourceManager resourceManager) {
this(getSystemClassLoader(), resourceManager);
}
public IdentityClassLoader(ClassLoader parent, ResourceManager resourceManager) {
super(parent);
this.resourceManager = resourceManager;
this.parent = parent;
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
@Override
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name);
} else {
c = findSystemClass(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
c = findClass(name);
}
if (c == null) {
// If still not found throw an exception
throw new ClassNotFoundException(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
@Override
protected Class<?> findClass(String classPath) throws ClassNotFoundException {
final Resource descriptor = resourceManager.lookup(classPath);
if (descriptor != null) {
try {
return defineClass(descriptor);
} catch (IOException ioe) {
LOG.error("Failed to resolve registered class: " + classPath, ioe);
}
}
return null;
}
//findResource("/META-INF/stuff")
@Override
protected URL findResource(String resourcePath) {
// Look it up locally first
final Resource descriptor = resourceManager.lookup(resourcePath);
URL resourceUrl = descriptor != null ? descriptor.url() : null;
// Look it up from the parent next if null
if (resourceUrl == null && parent != null) {
resourceUrl = parent.getResource(resourcePath);
}
// Lastly, call into our super definition if still null
if (resourceUrl == null) {
resourceUrl = super.findResource(resourcePath);
}
return resourceUrl;
}
@Override
protected Enumeration<URL> findResources(String name) throws IOException {
final URL resourceUrl = findResource(name);
return resourceUrl != null ? new SingleValueEnumeration<URL>(resourceUrl) : (Enumeration<URL>) EmptyEnumeration.instance();
}
@Override
public URL getResource(String name) {
return findResource(name);
}
@Override
public Enumeration<URL> getResources(String name) throws IOException {
return findResources(name);
}
@Override
public InputStream getResourceAsStream(String name) {
final URL resourceUrl = getResource(name);
if (resourceUrl != null) {
try {
return resourceUrl.openStream();
} catch (IOException ioe) {
LOG.error("Failed to open resource with URL: " + resourceUrl.toString());
}
}
return null;
}
final Class<?> defineClass(Resource descriptor) throws IOException {
final URL resourceUrl = descriptor.url();
if (resourceUrl == null) {
return null;
}
final byte[] classBytes = readResource(resourceUrl);
final String packageName = ResourceUtil.instance().relativePathToJavaPackage(descriptor.relativePath());
if (getPackage(packageName) == null) {
definePackage(packageName, null, null, null, null, null, null, null);
}
final String classPath = ResourceUtil.instance().relativePathToClassPath(descriptor.relativePath());
return defineClass(classPath, classBytes, 0, classBytes.length);
}
private byte[] readResource(URL resourceUrl) throws IOException {
final InputStream resourceInputStream = resourceUrl.openStream();
return readResource(resourceInputStream).toByteArray();
}
public static ByteArrayOutputStream readResource(InputStream resourceInputStream) throws IOException {
final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
RawInputStreamReader.instance().copyTo(resourceInputStream, byteArrayOutputStream);
resourceInputStream.close();
return byteArrayOutputStream;
}
}