package org.jboss.mx.loading;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Enumeration;
import java.util.Map;
import java.util.Set;
import java.io.IOException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanNotificationInfo;
import javax.management.Notification;
import javax.management.NotificationEmitter;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.MBeanRegistration;
import javax.management.ObjectName;
import javax.management.MBeanServer;
import javax.management.loading.MLet;
import org.jboss.logging.Logger;
import org.jboss.mx.util.JBossNotificationBroadcasterSupport;
import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap;
import EDU.oswego.cs.dl.util.concurrent.CopyOnWriteArraySet;
public class UnifiedLoaderRepository3 extends LoaderRepository
implements MBeanRegistration, NotificationEmitter,
UnifiedLoaderRepository3MBean
{
private static final Logger log = Logger.getLogger(UnifiedLoaderRepository3.class);
private static int addedCount;
private CopyOnWriteArraySet classLoaders = new CopyOnWriteArraySet();
private HashSet dynamicClassLoaders = new HashSet();
private HashMap nonUCLClassLoader = new HashMap();
private HashSet classLoaderURLs = new HashSet();
private ConcurrentReaderHashMap classes = new ConcurrentReaderHashMap();
private HashMap loaderToClassesMap = new HashMap();
private HashMap loaderToResourcesMap = new HashMap();
private HashMap globalResources = new HashMap();
private ConcurrentReaderHashMap packagesMap = new ConcurrentReaderHashMap();
private HashMap loaderToPackagesMap = new HashMap();
private long sequenceNumber = 0;
private final JBossNotificationBroadcasterSupport broadcaster = new JBossNotificationBroadcasterSupport();
private MBeanNotificationInfo[] info;
public RepositoryClassLoader newClassLoader(final URL url, boolean addToRepository)
throws Exception
{
UnifiedClassLoader3 ucl = new UnifiedClassLoader3(url, null, this);
if (addToRepository)
this.registerClassLoader(ucl);
return ucl;
}
public RepositoryClassLoader newClassLoader(final URL url, final URL origURL, boolean addToRepository)
throws Exception
{
UnifiedClassLoader3 ucl = new UnifiedClassLoader3(url, origURL, this);
if (addToRepository)
this.registerClassLoader(ucl);
return ucl;
}
public int getCacheSize()
{
return classes.size();
}
public int getClassLoadersSize()
{
return classLoaders.size();
}
public void flush()
{
synchronized (classes)
{
classes.clear();
}
}
public Class getCachedClass(String classname)
{
return (Class) classes.get(classname);
}
public Class loadClass(String name, boolean resolve, ClassLoader cl) throws ClassNotFoundException
{
RepositoryClassLoader rcl = getRepositoryClassLoader(cl, name);
return rcl.loadClass(name, resolve);
}
public Set getPackageClassLoaders(String className)
{
String pkgName = ClassLoaderUtils.getPackageName(className);
Set pkgSet = (Set) packagesMap.get(pkgName);
if (dynamicClassLoaders.size() > 0)
{
if (pkgSet == null)
pkgSet = ClassLoaderUtils.newPackageSet();
pkgSet.addAll(dynamicClassLoaders);
}
return pkgSet;
}
private String getResourcePackageName(String rsrcName)
{
int index = rsrcName.lastIndexOf('/');
String pkgName = rsrcName;
if (index > 0)
pkgName = rsrcName.substring(0, index);
return pkgName.replace('/', '.');
}
public Class loadClassFromCache(String name)
{
Class cls = null;
synchronized (classes)
{
cls = (Class) classes.get(name);
}
return cls;
}
public void cacheLoadedClass(String name, Class cls, ClassLoader cl)
{
synchronized (classes)
{
Object prevClass = classes.put(name, cls);
if (log.isTraceEnabled())
{
log.trace("cacheLoadedClass, classname: " + name + ", class: " + cls
+ ", ucl: " + cl + ", prevClass: " + prevClass);
}
HashSet loadedClasses = (HashSet) loaderToClassesMap.get(cl);
if (loadedClasses == null)
{
loadedClasses = new HashSet();
loaderToClassesMap.put(cl, loadedClasses);
}
loadedClasses.add(name);
}
}
Class loadClassFromClassLoader(String name, boolean resolve, RepositoryClassLoader cl)
{
try
{
Class cls = cl.loadClassLocally(name, resolve);
cacheLoadedClass(name, cls, cl);
return cls;
}
catch (ClassNotFoundException x)
{
}
return null;
}
public URL getResource(String name, ClassLoader cl)
{
URL resource = getResourceFromCache(name, cl);
if (resource != null)
return resource;
resource = getResourceFromClassLoader(name, cl);
if (resource != null)
return resource;
resource = getResourceFromGlobalCache(name);
if (resource != null)
return resource;
resource = getResourceFromRepository(name, cl);
if (resource != null)
return resource;
return null;
}
public void getResources(String name, ClassLoader cl, List urls)
{
Iterator iter = classLoaders.iterator();
while (iter.hasNext() == true)
{
ClassLoader nextCL = (ClassLoader) iter.next();
if (nextCL instanceof RepositoryClassLoader)
{
RepositoryClassLoader ucl = (RepositoryClassLoader) nextCL;
try
{
Enumeration resURLs = ucl.findResourcesLocally(name);
while (resURLs.hasMoreElements())
{
Object res = resURLs.nextElement();
urls.add(res);
}
}
catch (IOException ignore)
{
}
}
}
}
private URL getResourceFromCache(String name, ClassLoader cl)
{
URL resource = null;
synchronized (loaderToResourcesMap)
{
if (loaderToResourcesMap.containsKey(cl))
{
HashMap resources = (HashMap) loaderToResourcesMap.get(cl);
resource = (URL) resources.get(name);
}
}
return resource;
}
private URL getResourceFromClassLoader(String name, ClassLoader cl)
{
URL resource = null;
if (cl instanceof RepositoryClassLoader)
{
RepositoryClassLoader ucl = (RepositoryClassLoader) cl;
resource = ucl.getResourceLocally(name);
cacheLoadedResource(name, resource, cl);
}
return resource;
}
protected URL getResourceFromGlobalCache(String name)
{
ResourceInfo ri = null;
synchronized (loaderToResourcesMap)
{
ri = (ResourceInfo) globalResources.get(name);
}
URL resource = null;
if (ri != null)
resource = ri.url;
return resource;
}
protected URL getResourceFromRepository(String name, ClassLoader cl)
{
String pkgName = getResourcePackageName(name);
Iterator i = null;
Set pkgSet = (Set) this.packagesMap.get(pkgName);
if (pkgSet != null)
{
i = pkgSet.iterator();
}
if (i == null)
{
i = classLoaders.iterator();
}
URL url = null;
while (i.hasNext() == true)
{
ClassLoader classloader = (ClassLoader) i.next();
if (classloader.equals(cl))
{
continue;
}
if (classloader instanceof RepositoryClassLoader)
{
url = ((RepositoryClassLoader) classloader).getResourceLocally(name);
if (url != null)
{
cacheLoadedResource(name, url, classloader);
cacheGlobalResource(name, url, classloader);
break;
}
else
{
}
}
}
return url;
}
private void cacheLoadedResource(String name, URL url, ClassLoader cl)
{
synchronized (loaderToResourcesMap)
{
HashMap resources = (HashMap) loaderToResourcesMap.get(cl);
if (resources == null)
{
resources = new HashMap();
loaderToResourcesMap.put(cl, resources);
}
resources.put(name, url);
}
}
private void cacheGlobalResource(String name, URL url, ClassLoader cl)
{
synchronized (loaderToResourcesMap)
{
globalResources.put(name, new ResourceInfo(url, cl));
}
}
public URL[] getURLs()
{
HashSet classpath = new HashSet();
Set tmp = classLoaders;
for (Iterator iter = tmp.iterator(); iter.hasNext();)
{
Object obj = iter.next();
if (obj instanceof RepositoryClassLoader)
{
RepositoryClassLoader cl = (RepositoryClassLoader) obj;
URL[] urls = cl.getClasspath();
int length = urls != null ? urls.length : 0;
for (int u = 0; u < length; u++)
{
URL path = urls[u];
classpath.add(path);
}
}
}
URL[] cp = new URL[classpath.size()];
classpath.toArray(cp);
return cp;
}
public String displayClassInfo(String className)
{
String classRsrcName = className.replace('.', '/') + ".class";
int count = 0;
Class loadedClass = this.loadClassFromCache(className);
StringBuffer results = new StringBuffer(className + " Information\n");
if (loadedClass != null)
{
results.append("Repository cache version:");
ClassLoaderUtils.displayClassInfo(loadedClass, results);
}
else
{
results.append("Not loaded in repository cache\n");
}
Set tmp = classLoaders;
for (Iterator iter = tmp.iterator(); iter.hasNext();)
{
URLClassLoader cl = (URLClassLoader) iter.next();
URL classURL = cl.findResource(classRsrcName);
if (classURL != null)
{
results.append("\n\n### Instance" + count + " found in UCL: " + cl + "\n");
count++;
}
}
ClassLoader tcl = Thread.currentThread().getContextClassLoader();
URLClassLoader[] stack = ClassLoaderUtils.getClassLoaderStack(tcl);
for (int s = 0; s < stack.length; s++)
{
URLClassLoader cl = stack[s];
URL classURL = cl.findResource(classRsrcName);
if (classURL != null)
{
results.append("\n\n### Instance" + count + " via UCL: " + cl + "\n");
count++;
}
}
return results.toString();
}
public Class loadClass(String className) throws ClassNotFoundException
{
ClassLoader scl = Thread.currentThread().getContextClassLoader();
ClassLoader ucl = null;
if (classLoaders.size() > 0)
ucl = (ClassLoader) this.classLoaders.iterator().next();
try
{
if (ucl != null)
return loadClass(className, false, ucl);
}
catch (ClassNotFoundException ignore)
{
}
try
{
return scl.loadClass(className);
}
catch (ClassNotFoundException e)
{
}
Class clazz = getNativeClassForName(className);
if (clazz != null) return clazz;
throw new ClassNotFoundException(className);
}
public Class loadClassWithout(ClassLoader loader, String className)
throws ClassNotFoundException
{
throw new ClassNotFoundException("NYI");
}
public Class loadClassBefore(ClassLoader stop, String className) throws ClassNotFoundException
{
RepositoryClassLoader stopAt = getRepositoryClassLoader(stop, className);
return stopAt.loadClassBefore(className);
}
public RepositoryClassLoader getWrappingClassLoader(ClassLoader cl)
{
synchronized (classLoaders)
{
return (RepositoryClassLoader) nonUCLClassLoader.get(cl);
}
}
public void addClassLoader(ClassLoader loader)
{
if (loader instanceof RepositoryClassLoader)
addRepositoryClassLoader((RepositoryClassLoader) loader);
else if (loader instanceof MLet)
{
addMLetClassLoader((MLet) loader);
}
else if (loader instanceof URLClassLoader)
{
addURLClassLoader((URLClassLoader) loader);
}
else
{
log.warn("Tried to add non-URLClassLoader. Ignored");
} }
public boolean addClassLoaderURL(ClassLoader cl, URL url)
{
RepositoryClassLoader ucl = (RepositoryClassLoader) cl;
boolean added = false;
synchronized (classLoaders)
{
String query = url.getQuery();
if (query != null)
{
String ext = url.toExternalForm();
String ext2 = ext.substring(0, ext.length() - query.length() - 1);
try
{
url = new URL(ext2);
}
catch (MalformedURLException e)
{
log.warn("Failed to strip query from: " + url, e);
}
}
if (classLoaderURLs.contains(url) == false)
{
updatePackageMap(ucl, url);
classLoaderURLs.add(url);
added = true;
if (query != null && query.indexOf("dynamic=true") >= 0)
dynamicClassLoaders.add(ucl);
}
}
return added;
}
private void addRepositoryClassLoader(RepositoryClassLoader cl)
{
cl.setRepository(this);
URL url = cl.getURL();
boolean added = false;
synchronized (classLoaders)
{
boolean exists = false;
if (cl instanceof UnifiedClassLoader)
exists = classLoaderURLs.contains(url);
if (!exists)
{
if (url != null)
classLoaderURLs.add(url);
added = classLoaders.add(cl);
}
if (added)
{
log.debug("Adding " + cl);
addedCount++;
cl.setAddedOrder(addedCount);
updatePackageMap(cl);
}
else
{
log.debug("Skipping duplicate " + cl);
}
}
}
private void addMLetClassLoader(MLet loader)
{
MLetRepositoryClassLoader rcl = new MLetRepositoryClassLoader(loader);
synchronized (classLoaders)
{
nonUCLClassLoader.put(loader, rcl);
}
addRepositoryClassLoader(rcl);
}
private void addURLClassLoader(URLClassLoader loader)
{
URL[] urls = loader.getURLs();
int count = urls != null && urls.length > 0 ? urls.length : 0;
URL origURL = count > 0 ? urls[0] : null;
UnifiedClassLoader3 ucl3 = new UnifiedClassLoader3(origURL, origURL, this);
addRepositoryClassLoader(ucl3);
synchronized (classLoaders)
{
nonUCLClassLoader.put(loader, ucl3);
}
for (int i = 1; i < count; i++)
{
this.addClassLoaderURL(ucl3, urls[i]);
}
}
private void updatePackageMap(RepositoryClassLoader cl)
{
try
{
String[] pkgNames = ClassLoaderUtils.updatePackageMap(cl, packagesMap);
loaderToPackagesMap.put(cl, pkgNames);
}
catch (Exception e)
{
if (log.isTraceEnabled())
log.trace("Failed to update pkgs for cl=" + cl, e);
else
log.debug("Failed to update pkgs for cl=" + cl, e);
}
}
private void updatePackageMap(RepositoryClassLoader cl, URL url)
{
try
{
String[] prevPkgNames = (String[]) loaderToPackagesMap.get(cl);
String[] pkgNames = ClassLoaderUtils.updatePackageMap(cl, packagesMap, url, prevPkgNames);
loaderToPackagesMap.put(cl, pkgNames);
}
catch (Exception e)
{
if (log.isTraceEnabled())
log.trace("Failed to update pkgs for cl=" + cl, e);
else
log.debug("Failed to update pkgs for cl=" + cl, e);
}
}
public void removeClassLoader(ClassLoader loader)
{
ArrayList removeNotifications = new ArrayList();
ClassLoader cl = loader;
synchronized (classLoaders)
{
if ((loader instanceof RepositoryClassLoader) == false)
{
cl = (ClassLoader) nonUCLClassLoader.remove(loader);
}
if (cl instanceof RepositoryClassLoader)
{
RepositoryClassLoader ucl = (RepositoryClassLoader) cl;
if (getTranslator() != null)
getTranslator().unregisterClassLoader(ucl);
URL[] urls = ucl.getClasspath();
for (int u = 0; u < urls.length; u++)
classLoaderURLs.remove(urls[u]);
}
boolean dynamic = dynamicClassLoaders.remove(cl);
boolean removed = classLoaders.remove(cl);
log.debug("UnifiedLoaderRepository removed(" + removed + ") " + cl);
HashSet loadedClasses = null;
boolean hasLoadedClasses = false;
synchronized (classes)
{
hasLoadedClasses = loaderToClassesMap.containsKey(cl);
if (hasLoadedClasses)
loadedClasses = (HashSet) loaderToClassesMap.remove(cl);
if (loadedClasses != null)
{
for (Iterator iter = loadedClasses.iterator(); iter.hasNext();)
{
String className = (String) iter.next();
Notification n = new Notification(CLASS_REMOVED, this,
getNextSequenceNumber(), className);
removeNotifications.add(n);
}
for (Iterator i = loadedClasses.iterator(); i.hasNext();)
{
String cls = (String) i.next();
this.classes.remove(cls);
}
}
}
synchronized (loaderToResourcesMap)
{
if (loaderToResourcesMap.containsKey(cl))
{
HashMap resources = (HashMap) loaderToResourcesMap.remove(cl);
if (resources != null)
{
for (Iterator i = resources.keySet().iterator(); i.hasNext();)
{
String name = (String) i.next();
ResourceInfo ri = (ResourceInfo) globalResources.get(name);
if (ri != null && ri.cl == cl)
globalResources.remove(name);
}
}
}
}
if (dynamic == false)
{
String[] pkgNames = (String[]) loaderToPackagesMap.remove(cl);
int length = pkgNames != null ? pkgNames.length : 0;
for (int p = 0; p < length; p++)
{
String pkgName = pkgNames[p];
Set pkgSet = (Set) packagesMap.get(pkgName);
if (pkgSet != null)
{
pkgSet.remove(cl);
if (pkgSet.isEmpty())
packagesMap.remove(pkgName);
}
}
}
else
{
loaderToPackagesMap.remove(cl);
for (Iterator i = packagesMap.entrySet().iterator(); i.hasNext();)
{
Map.Entry entry = (Map.Entry) i.next();
Set pkgSet = (Set) entry.getValue();
pkgSet.remove(cl);
if (pkgSet.isEmpty())
i.remove();
}
}
}
for (int n = 0; n < removeNotifications.size(); n++)
{
Notification msg = (Notification) removeNotifications.get(n);
broadcaster.sendNotification(msg);
}
Notification msg = new Notification(CLASSLOADER_REMOVED, this, getNextSequenceNumber());
msg.setUserData(cl);
broadcaster.sendNotification(msg);
}
public LoaderRepository registerClassLoader(RepositoryClassLoader ucl)
{
addClassLoader(ucl);
Notification msg = new Notification(CLASSLOADER_ADDED, this, getNextSequenceNumber());
msg.setUserData(ucl);
broadcaster.sendNotification(msg);
return this;
}
public LoaderRepository getInstance()
{
return this;
}
public void addNotificationListener(NotificationListener listener,
NotificationFilter filter, Object handback) throws IllegalArgumentException
{
broadcaster.addNotificationListener(listener, filter, handback);
}
public MBeanNotificationInfo[] getNotificationInfo()
{
if (info == null)
{
info = new MBeanNotificationInfo[]{
new MBeanNotificationInfo(new String[]{"CLASSLOADER_ADDED"},
"javax.management.Notification",
"Notification that a classloader has been added to the extensible classloader"),
new MBeanNotificationInfo(new String[]{"CLASS_REMOVED"},
"javax.management.Notification",
"Notification that a class has been removed from the extensible classloader")
};
}
return info;
}
public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException
{
broadcaster.removeNotificationListener(listener);
}
public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback)
throws ListenerNotFoundException
{
broadcaster.removeNotificationListener(listener, filter, handback);
}
public ObjectName preRegister(MBeanServer server, ObjectName name)
throws Exception
{
return name;
}
public void postRegister(Boolean registrationDone)
{
}
public void preDeregister()
throws Exception
{
}
public void postDeregister()
{
log.debug("postDeregister, clearing all references");
classLoaders.clear();
dynamicClassLoaders.clear();
nonUCLClassLoader.clear();
classLoaderURLs.clear();
classes.clear();
loaderToClassesMap.clear();
loaderToResourcesMap.clear();
globalResources.clear();
packagesMap.clear();
loaderToPackagesMap.clear();
}
private synchronized long getNextSequenceNumber()
{
return sequenceNumber++;
}
private RepositoryClassLoader getRepositoryClassLoader(ClassLoader cl, String name) throws ClassNotFoundException
{
if (cl instanceof RepositoryClassLoader)
return (RepositoryClassLoader) cl;
else
{
RepositoryClassLoader rcl = getWrappingClassLoader(cl);
if (rcl == null)
throw new ClassNotFoundException("Class not found " + name + " (Unknown classloader " + cl + ")");
return rcl;
}
}
}