/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.mx.loading; import java.net.URL; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.management.AttributeNotFoundException; import javax.management.InstanceNotFoundException; import javax.management.MBeanException; import javax.management.MBeanServer; import javax.management.ObjectName; import javax.management.ReflectionException; import org.jboss.mx.loading.LoadMgr3.PkgClassLoader; import org.jboss.mx.util.ObjectNameFactory; /** A simple extension of UnifiedLoaderRepository3 that adds the notion of a * parent UnifiedLoaderRepository. Classes and resources are loaded from child * first and then the parent depending on the java2ParentDelegation flag. * * @author Scott.Stark@jboss.org * @version $Revision: 81022 $ */ public class HeirarchicalLoaderRepository3 extends UnifiedLoaderRepository3 { private static ObjectName DEFAULT_LOADER_OBJECT_NAME = ObjectNameFactory.create(DEFAULT_LOADER_NAME); /** A ClassLoader override that prevents a child class loader from looking * beyond its URLs for classes. */ static class NoParentClassLoader extends ClassLoader { NoParentClassLoader() { super(HeirarchicalLoaderRepository3.class.getClassLoader()); } /** Override to always return null to force the UCL to only load from * its URLs. * @param name * @return */ public URL getResource(String name) { return null; } /** Override to always throw a CNFE to force the UCL to only load from * its URLs. * @param name * @param resolve * @return nothing * @throws ClassNotFoundException always */ protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { throw new ClassNotFoundException("NoParentClassLoader has no classes"); } /** Override to always throw a CNFE to force the UCL to only load from * its URLs. * @param name * @return * @throws ClassNotFoundException */ protected Class findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException("NoParentClassLoader has no classes"); } } static class CacheClassLoader extends UnifiedClassLoader3 { Class cacheClass; CacheClassLoader(Class cacheClass, LoaderRepository rep) { super(null, null, new NoParentClassLoader(), rep); this.cacheClass = cacheClass; } protected Class findClass(String name) throws ClassNotFoundException { Class c = cacheClass; if( name.equals(cacheClass.getName()) == false ) c = null; return c; } } /** The repository to which we delegate if requested classes or resources are not available from this repository. */ private UnifiedLoaderRepository3 parentRepository; /** A flag indicating if the standard parent delegation loading where the parent repository is used before this repository. */ private boolean java2ParentDelegation; /** The package classloader */ private PkgClassLoader packageClassLoader; /** Create a HeirarchicalLoaderRepository3 with an explicit parent. * * @param parent * @throws AttributeNotFoundException * @throws InstanceNotFoundException * @throws MBeanException * @throws ReflectionException */ public HeirarchicalLoaderRepository3(UnifiedLoaderRepository3 parent) throws AttributeNotFoundException, InstanceNotFoundException, MBeanException, ReflectionException { this.parentRepository = parent; init(); } /** Create a HeirarchicalLoaderRepository3 with a parent obtained by querying * the server for the ServerConstants.DEFAULT_LOADER_NAME mbean. * * @param server * @throws AttributeNotFoundException * @throws InstanceNotFoundException * @throws MBeanException * @throws ReflectionException */ public HeirarchicalLoaderRepository3(MBeanServer server) throws AttributeNotFoundException, InstanceNotFoundException, MBeanException, ReflectionException { this(server, DEFAULT_LOADER_OBJECT_NAME); } /** Create a HeirarchicalLoaderRepository3 with a parent obtained by querying * the server for the parentName mbean. * * @param server * @param parentName * @throws AttributeNotFoundException * @throws InstanceNotFoundException * @throws MBeanException * @throws ReflectionException */ public HeirarchicalLoaderRepository3(MBeanServer server, ObjectName parentName) throws AttributeNotFoundException, InstanceNotFoundException, MBeanException, ReflectionException { this.parentRepository = (UnifiedLoaderRepository3) server.getAttribute(parentName, "Instance"); init(); } /** * Initialisation */ private void init() { // Include a class loader with a parent to the system class loader ClassLoader loader = RepositoryClassLoader.class.getClassLoader(); RepositoryClassLoader ucl = null; if( loader instanceof RepositoryClassLoader ) ucl = (RepositoryClassLoader) loader; else ucl = new UnifiedClassLoader3(null, null, HeirarchicalLoaderRepository3.this); packageClassLoader = new PkgClassLoader(ucl, 3); } // Public -------------------------------------------------------- public RepositoryClassLoader newClassLoader(final URL url, boolean addToRepository) throws Exception { UnifiedClassLoader3 ucl = null; if( java2ParentDelegation == false ) ucl = new UnifiedClassLoader3(url, null, new NoParentClassLoader(), this); else ucl = new UnifiedClassLoader3(url, null, this); if( addToRepository ) { this.addClassLoader(ucl); } return ucl; } public RepositoryClassLoader newClassLoader(final URL url, final URL origURL, boolean addToRepository) throws Exception { UnifiedClassLoader3 ucl = null; if( java2ParentDelegation == false ) ucl = new UnifiedClassLoader3(url, origURL, new NoParentClassLoader(), this); else ucl = new UnifiedClassLoader3(url, origURL, this); if( addToRepository ) { this.addClassLoader(ucl); } return ucl; } /** Get the use parent first flag. This indicates whether the parent * repository is consulted first for resource and class loading or if the * HeirchicalLoaderRepository is consulted first. * * @return true if the parent repository is consulted first, false if the * HeirchicalLoaderRepository is consulted first. */ public boolean getUseParentFirst() { return java2ParentDelegation; } /** Set the use parent first flag. This indicates whether the parent * repository is consulted first for resource and class loading or if the * HeirchicalLoaderRepository is consulted first. * * @param flag true if the parent repository is consulted first, false if the * HeirchicalLoaderRepository is consulted first. */ public void setUseParentFirst(boolean flag) { java2ParentDelegation = flag; } /** Load a class using the repository class loaders. * * @param name The name of the class * @param resolve an obsolete unused parameter from ClassLoader.loadClass * @param scl The asking class loader * @return The loaded class * @throws ClassNotFoundException If the class could not be found. */ public Class loadClass(String name, boolean resolve, ClassLoader scl) throws ClassNotFoundException { Class foundClass = null; if( java2ParentDelegation == true ) { try { // Try the parent repository first foundClass = parentRepository.loadClass(name, resolve, scl); } catch(ClassNotFoundException e) { // Next try our repository if( foundClass == null ) foundClass = super.loadClass(name, resolve, scl); } } else { try { // Try this repository first foundClass = super.loadClass(name, resolve, scl); } catch(ClassNotFoundException e) { // Next try our parent repository if( foundClass == null ) foundClass = parentRepository.loadClass(name, resolve, scl); } } if( foundClass != null ) return foundClass; /* If we reach here, all of the classloaders currently in the VM don't know about the class */ throw new ClassNotFoundException(name); } /** Override getCachedClass to return the parent repository cached class * if java2ParentDelegation=true, followed by this repository's cached * value. Else, if java2ParentDelegation=false, only check this repository's * cache to attempt to load the class from the child repository before * going to the parent cache. * * @param classname * @return the cached class if found, null otherwise */ public Class getCachedClass(String classname) { Class clazz = null; if( java2ParentDelegation == true ) { // Try the parent repository clazz = parentRepository.getCachedClass(classname); // Next try our parent repository if( clazz == null ) clazz = super.getCachedClass(classname); } else { // Try this repository clazz = super.getCachedClass(classname); } return clazz; } /** Find a resource from this repository. This first looks to this * repository and then the parent repository. * @param name The name of the resource * @param scl The asking class loader * @return An URL for reading the resource, or <code>null</code> if the * resource could not be found. */ public URL getResource(String name, ClassLoader scl) { URL resource = null; if( java2ParentDelegation == true ) { /* Try our parent repository. This cannot use the getResource method because we do not want the parent repository to load the resource via our scoped class loader */ resource = getParentResource(name, scl); // Next try this repository if( resource == null ) resource = super.getResource(name, scl); } else { // Try this repository resource = super.getResource(name, scl); // Next try our parent repository if( resource == null ) { /* Try our parent repository. This cannot use the getResource method because we do not want the parent repository to load the resource via our scoped class loader */ resource = getParentResource(name, scl); } } return resource; } /** Find all resource URLs for the given name. This is entails an * exhuastive search of this and the parent repository and is an expensive * operation. * * @param name the resource name * @param cl the requesting class loader * @param urls a list into which the located resource URLs will be placed */ public void getResources(String name, ClassLoader cl, List urls) { if( java2ParentDelegation == true ) { // Get the parent repository resources parentRepository.getResources(name, cl, urls); // Next get this repositories resources super.getResources(name, cl, urls); } else { // Get this repositories resources super.getResources(name, cl, urls); // Next get the parent repository resources parentRepository.getResources(name, cl, urls); } } /** Obtain a listing of the URLs for all UnifiedClassLoaders associated with *the repository */ public URL[] getURLs() { URL[] ourURLs = super.getURLs(); URL[] parentURLs = parentRepository.getURLs(); int size = ourURLs.length + parentURLs.length; URL[] urls = new URL[size]; System.arraycopy(ourURLs, 0, urls, 0, ourURLs.length); System.arraycopy(parentURLs, 0, urls, ourURLs.length, parentURLs.length); return urls; } /** Called by LoadMgr to locate a previously loaded class. This looks * first to this repository and then the parent repository. *@return the cached class if found, null otherwise */ public Class loadClassFromCache(String name) { Class foundClass = null; if( java2ParentDelegation == true ) { // Try this repository foundClass = parentRepository.loadClassFromCache(name); // Next try our parent repository if( foundClass == null ) foundClass = super.loadClassFromCache(name); } else { // Try this repository foundClass = super.loadClassFromCache(name); /* We do not try the parent repository cache as this does not allow the child repository to override classes in the parent */ } return foundClass; } /** Called by LoadMgr to obtain all class loaders. This returns a set of * PkgClassLoader with the HeirarchicalLoaderRepository3 ordered ahead of * the parent repository pkg class loaders *@return Set<PkgClassLoader> */ public Set getPackageClassLoaders(String name) { Set pkgSet = super.getPackageClassLoaders(name); Set parentPkgSet = parentRepository.getPackageClassLoaders(name); GetClassLoadersAction action = new GetClassLoadersAction(name, pkgSet, parentPkgSet); Set theSet = (Set) AccessController.doPrivileged(action); return theSet; } public int compare(LoaderRepository lr) { if (lr == this) return 0; return reverseCompare(lr); } protected int reverseCompare(LoaderRepository lr) { // If it is not our parent we don't care if (lr != parentRepository) return 0; // The order depends upon the delegation model if (java2ParentDelegation) return +1; else return -1; } /** A subset of the functionality found in getResource(String, ClassLoader), * but this version queries the parentRepository and does not use the scl * to avoid leaking class loaders across scoped. * * @param name - the resource name * @param scl - the requesting class loader * @return the resource URL if found, null otherwise */ private URL getParentResource(String name, ClassLoader scl) { // Not found in classloader, ask the global cache URL resource = parentRepository.getResourceFromGlobalCache(name); // The cache has it, we are done if( resource != null ) return resource; // Not visible in global cache, iterate on all classloaders resource = parentRepository.getResourceFromRepository(name, scl); return resource; } private class GetClassLoadersAction implements PrivilegedAction { private String name; Set pkgSet; Set parentPkgSet; GetClassLoadersAction(String name, Set pkgSet, Set parentPkgSet) { this.name = name; this.pkgSet = pkgSet; this.parentPkgSet = parentPkgSet; } public Object run() { // Build a set of PkgClassLoader Set theSet = ClassLoaderUtils.newPackageSet(); if( pkgSet != null ) { Iterator iter = pkgSet.iterator(); while( iter.hasNext() ) { RepositoryClassLoader ucl = (RepositoryClassLoader) iter.next(); PkgClassLoader pkgUcl = new PkgClassLoader(ucl, 0); theSet.add(pkgUcl); } } if( java2ParentDelegation == false ) { Class cacheClass = parentRepository.loadClassFromCache(name); if( cacheClass != null ) { RepositoryClassLoader ucl = new CacheClassLoader(cacheClass, HeirarchicalLoaderRepository3.this); PkgClassLoader pkgUcl = new PkgClassLoader(ucl, 1); theSet.add(pkgUcl); } } if( parentPkgSet != null ) { Iterator iter = parentPkgSet.iterator(); while( iter.hasNext() ) { RepositoryClassLoader ucl = (RepositoryClassLoader) iter.next(); PkgClassLoader pkgUcl = new PkgClassLoader(ucl, 2); theSet.add(pkgUcl); } } if( java2ParentDelegation == false ) { theSet.add(packageClassLoader); } return theSet; } } }