package org.jboss.remoting.loading;
import org.jboss.logging.Logger;
import org.jboss.remoting.Client;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class ClassByteClassLoader extends ClassLoader
{
private static final Logger log = Logger.getLogger(ClassByteClassLoader.class);
private final Map loadedClasses = Collections.synchronizedMap(new java.util.HashMap());
private final Map loadedResources = Collections.synchronizedMap(new java.util.HashMap());
private final ReferenceQueue queue = new ReferenceQueue();
private Client loaderClient = null;
public ClassByteClassLoader()
{
super();
}
public ClassByteClassLoader(ClassLoader parent)
{
super(parent);
}
public void setClientInvoker(Client loaderClient)
{
this.loaderClient = loaderClient;
}
public void destroy()
{
if(loaderClient != null && loaderClient.isConnected())
{
loaderClient.disconnect();
}
}
private final class MyRef extends WeakReference
{
private final String key;
MyRef(String key, Object obj)
{
super(obj);
this.key = key;
}
}
private void clean(MyRef myref)
{
loadedClasses.remove(myref.key);
File f = (File) loadedResources.remove(myref.key);
if(f != null)
{
f.delete();
}
myref.clear();
myref = null;
}
private void performMaintenance()
{
int count = 0;
Reference ref = null;
while((ref = queue.poll()) != null)
{
count++;
MyRef myref = (MyRef) ref;
clean(myref);
}
if(count > 0 && log.isTraceEnabled())
{
log.trace("ClassByteClassLoader reclaimed " + count + " objects");
}
}
public String toString()
{
return "ClassByteClassLoader [" + loadedClasses + "]";
}
protected void finalize() throws Throwable
{
performMaintenance();
java.util.Iterator iter = loadedResources.values().iterator();
while(iter.hasNext())
{
((java.io.File) iter.next()).delete();
}
loadedResources.clear();
loadedClasses.clear();
super.finalize();
}
public Class loadClass(String className, ClassBytes bytes[])
throws ClassNotFoundException, java.io.IOException
{
performMaintenance();
if(log.isTraceEnabled())
{
log.trace("loadClass: " + className + ", bytes: " + bytes);
}
if(bytes != null)
{
for(int c = 0; c < bytes.length; c++)
{
addClass(bytes[c]);
}
}
Class cl = lookupCachedClass(className);
if(cl != null)
{
return cl;
}
cl = findLoadedClass(className);
if(cl != null)
{
return cl;
}
cl = ClassLoader.getSystemClassLoader().loadClass(className);
if(cl != null)
{
return cl;
}
cl = super.loadClass(className, true);
if(cl != null)
{
return cl;
}
cl = loadFromNetwork(className);
if(cl != null)
{
return cl;
}
throw new ClassNotFoundException("Could not load class " + className);
}
private void addClassResource(String name, byte buf[])
throws IOException
{
performMaintenance();
OutputStream out = null;
File file = null;
try
{
file = File.createTempFile("cbc", ".class");
file.deleteOnExit();
if(log.isTraceEnabled())
{
log.trace("adding resource at: " + name + " to file: " + file);
}
out = new FileOutputStream(file);
out.write(buf);
out.flush();
}
catch(java.io.IOException ex)
{
file = null;
throw ex;
}
finally
{
if(out != null)
{
try
{
out.close();
}
catch(Exception ig)
{
}
out = null;
}
if(file != null)
{
loadedResources.put(name, file);
}
}
}
public java.io.InputStream getResourceAsStream(String name)
{
performMaintenance();
String denormalized = name.replace('/', '.').substring(0, name.length() - 6);
java.io.File file = (java.io.File) loadedResources.get(denormalized);
if(log.isTraceEnabled())
{
log.trace("getResourceAsStream =>" + denormalized + " = " + file);
}
if(file != null && file.exists())
{
try
{
return new java.io.BufferedInputStream(new java.io.FileInputStream(file));
}
catch(Exception ex)
{
}
}
return super.getResourceAsStream(name);
}
public Class addClass(ClassBytes classBytes)
throws java.io.IOException
{
performMaintenance();
Class cl = null;
String name = classBytes.getClassName();
if(loadedClasses.containsKey(name) == false)
{
byte buf[] = classBytes.getClassBytes();
boolean array = ClassUtil.isArrayClass(name);
String cn = (array) ? ClassUtil.getArrayClassPart(name) : name;
if(log.isTraceEnabled())
{
log.trace(" add class: " + name + ", array?" + array + ", using as: " + cn);
}
cl = defineClass(cn, buf, 0, buf.length);
resolveClass(cl);
addClassResource(cn, buf);
loadedClasses.put(cn, new MyRef(cn, cl));
}
return cl;
}
private Class lookupCachedClass(String cn)
{
Class cl = null;
MyRef ref = (MyRef) loadedClasses.get(cn);
if(ref != null)
{
cl = (Class) ref.get();
if(cl == null)
{
clean(ref);
}
}
return cl;
}
protected Class findClass(String name) throws ClassNotFoundException
{
performMaintenance();
boolean array = ClassUtil.isArrayClass(name);
String cn = (array) ? ClassUtil.getArrayClassPart(name) : name;
if(log.isTraceEnabled())
{
log.trace("++ loadClass: " + name + ", array?" + array + ", normalized: [" + cn + "]");
}
Class cl = lookupCachedClass(cn);
if(cl == null)
{
cl = findLoadedClass(cn);
}
if(cl != null)
{
if(array)
{
Object obj = java.lang.reflect.Array.newInstance(cl, 1);
return obj.getClass();
}
return cl;
}
cl = loadFromNetwork(cn);
if(cl != null)
{
if(log.isTraceEnabled())
{
log.trace("Loaded " + cn + " can class is " + cl);
}
return cl;
}
if(log.isTraceEnabled())
{
log.trace("++ findClass: " + name + " not found, throwing ClassNotFoundException");
}
throw new ClassNotFoundException(name);
}
private Class loadFromNetwork(String className)
{
Class loadedClass = null;
if(loaderClient != null)
{
String marshallerMethodName = "load_class";
Map metadata = new HashMap();
metadata.put("classname", className);
try
{
Object obj = loaderClient.invoke(marshallerMethodName, metadata);
if(obj != null)
{
if(obj instanceof ClassBytes)
{
ClassBytes classBytes = (ClassBytes) obj;
String name = classBytes.getClassName();
loadedClass = addClass(classBytes);
}
else
{
log.error("Can not load remote class bytes. Returned object (" + obj + ") is not ClassBytes.");
}
}
else
{
log.error("Can not load remote class bytes.");
}
}
catch(Throwable throwable)
{
log.error("Error loading remote class.", throwable);
}
}
else
{
log.trace("Remoting Client for ClassByteClassLoader is null. Can not load class remotely.");
}
return loadedClass;
}
}