package lucee.runtime.osgi;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import lucee.commons.io.SystemUtil;
import lucee.commons.io.SystemUtil.Caller;
import lucee.commons.lang.ExceptionUtil;
import lucee.commons.lang.PhysicalClassLoader;
import lucee.loader.engine.CFMLEngineFactory;
import lucee.runtime.config.ConfigImpl;
import lucee.runtime.config.ConfigWebUtil;
import org.osgi.framework.Bundle;
public class EnvClassLoader extends URLClassLoader {
private ConfigImpl config;
//private final ClassLoader[] parents;
//private ClassLoader loaderCL;
private static final short CLASS=1;
private static final short URL=2;
private static final short STREAM=3;
public EnvClassLoader(ConfigImpl config) {
super(new URL[0],config.getClassLoaderCore());
this.config=config;
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
@Override
public URL getResource(String name) {
return (java.net.URL) load(name, URL,true);
}
@Override
public InputStream getResourceAsStream(String name) {
InputStream is = (InputStream) load(name, STREAM,true);
if(is!=null) return is;
// PATCH
if(name.equalsIgnoreCase("META-INF/services/org.apache.xerces.xni.parser.XMLParserConfiguration")) {
String value="org.apache.xerces.parsers.XIncludeAwareParserConfiguration";
System.setProperty("org.apache.xerces.xni.parser.XMLParserConfiguration", value);
return new ByteArrayInputStream(value.getBytes());
}
// return (InputStream) load("org/apache/xerces/parsers/org.apache.xerces.xni.parser.XMLParserConfiguration", STREAM,true);
return null;
}
@Override
public Enumeration<URL> getResources(String name) throws IOException {
List<URL> list=new ArrayList<URL>();
URL url = (URL)load(name,URL,false);
if(url!=null) list.add(url);
return new E<URL>(list.iterator());
}
@Override
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if(c==null)c = (Class<?>) load(name, CLASS,true);
if(c==null)c = findClass(name);
if (resolve)resolveClass(c);
return c;
}
private synchronized Object load(String name, short type, boolean doLog) {
Object obj=null;
// first we check the callers classpath
Caller caller = SystemUtil.getCallerClass();
if(!caller.isEmpty()) {
// if the request comes from classpath
Class clazz=caller.fromClasspath();
if(clazz!=null) {
if(clazz.getClassLoader()!=null) {
obj=_load(clazz.getClassLoader(),name,type);
if(obj==null && clazz.getClassLoader() instanceof PhysicalClassLoader && clazz!=caller.fromBootDelegation && caller.fromBootDelegation!=null & caller.fromBootDelegation.getClassLoader()!=null) {
obj=_load(caller.fromBootDelegation.getClassLoader(),name,type);
}
}
}
if(obj==null && caller.fromBundle!=null) {
if(caller.fromBundle.getClassLoader()!=null)
obj=_load(caller.fromBundle.getClassLoader(),name,type);
}
if(obj!=null) return obj;
}
// now we check in the core for the class (this includes all jars loaded by the core)
if((caller.isEmpty() || caller.fromBundle!=null) && caller.fromBundle.getClassLoader()!=getParent()) {
//print.e("check core:"+name+"->");
obj=_load(getParent(), name, type);
if(obj!=null) {
//print.e("found in core:"+name+"->");
return obj;
}
}
// now we check extension bundles
if(caller.isEmpty() || caller.fromBundle!=null) {
//print.e("check extension:"+name+"->");
Bundle[] bundles = ConfigWebUtil.getEngine(config).getBundleContext().getBundles();
Bundle b=null;
for(int i=0;i<bundles.length;i++) {
b=bundles[i];
if(b!=null && !OSGiUtil.isFrameworkBundle(b)) {
try {
if(type==CLASS)obj = b.loadClass(name);
else if(type==URL)obj = b.getResource(name);
else {
java.net.URL url = b.getResource(name);
if(url!=null) obj=url.openStream();
}
if(obj!=null)break;
}
catch(Throwable t) {
ExceptionUtil.rethrowIfNecessary(t);
obj=null;
}
}
}
if(obj!=null){
//print.e("found in extensions:"+name+"->");
return obj;
}
}
if(caller.fromClasspath()!=null) {
ClassLoader loader = CFMLEngineFactory.class.getClassLoader();
obj=_load(loader, name, type);
if(obj!=null) {
//print.e("found in classpath:"+name+"->");
return obj;
}
}
if(obj==null) {
ClassLoader loader = CFMLEngineFactory.class.getClassLoader();
Object obj2 = _load(loader, name, type);
if(obj2!=null) {
//print.e("found in classpath but not used:"+name+"->");
}
}
return obj;
}
private Object _load(ClassLoader cl, String name, short type) {
Object obj=null;
if(cl!=null) {
try {
if(type==CLASS)obj = cl.loadClass(name);
else if(type==URL)obj = cl.getResource(name);
else obj = cl.getResourceAsStream(name);
}
catch(Throwable t) {ExceptionUtil.rethrowIfNecessary(t);}
}
return obj;
}
/*private void test(Bundle[] bundles, short type, String name) {
Bundle b=null;
List<Bundle> list=new ArrayList<Bundle>();
Object obj;
for(int i=0;i<bundles.length;i++) {
b=bundles[i];
if(b.getSymbolicName().equalsIgnoreCase("hibernate.extension") && "org.lucee.extension.orm.hibernate.jdbc.ConnectionProviderImpl".equals(name)) {
print.e("=>"+b+":"+b.hashCode());
try {
Class<?> clazz = b.loadClass(name);
ClassLoader cl = clazz.getClassLoader();
print.e("-=>"+cl+":"+cl.hashCode());
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
if(type==CLASS)obj = b.loadClass(name);
else if(type==URL)obj = b.getResource(name);
else obj = ((ClassLoader)b).getResourceAsStream(name);
if(obj!=null) {
list.add(b);
}
}
catch(Throwable t) {ExceptionUtil.rethrowIfNecessary(t);
b=null;
}
}
if(list.size()>1) {
print.e("nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn");
print.e("found in more than one bundle!");
Iterator<Bundle> it = list.iterator();
while(it.hasNext()){
b=it.next();
print.e("- "+b);
}
}
if(list.size()==0) {
print.e("000000000000000000000000000000000000000000000000000000");
print.e("not found:"+name);
}
}*/
private String toType(short type) {
if(CLASS==type) return "class";
if(STREAM==type) return "stream";
return "url";
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {//if(name.indexOf("sub")!=-1)print.ds(name);
throw new ClassNotFoundException("class "+name+" not found in the core, the loader and all the extension bundles");
}
private static class E<T> implements Enumeration<T> {
private Iterator<T> it;
private E(Iterator<T> it){
this.it=it;
}
@Override
public boolean hasMoreElements() {
return it.hasNext();
}
@Override
public T nextElement() {
return it.next();
}
}
//////////////////////////////////////////////////
// URLClassloader methods, need to be supressed //
//////////////////////////////////////////////////
@Override
public URL findResource(String name) {
return getResource(name);
}
@Override
public Enumeration<URL> findResources(String name) throws IOException {
return getResources(name);
}
}