/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later, * or the Apache License Version 2.0. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package scouter.javassist.scopedpool; import java.lang.ref.WeakReference; import java.security.ProtectionDomain; import java.util.Iterator; import java.util.Map; import scouter.javassist.CannotCompileException; import scouter.javassist.ClassPool; import scouter.javassist.CtClass; import scouter.javassist.LoaderClassPath; import scouter.javassist.NotFoundException; import scouter.javassist.scopedpool.ScopedClassPoolRepository; import scouter.javassist.scopedpool.SoftValueHashMap; /** * A scoped class pool. * * @author <a href="mailto:bill@jboss.org">Bill Burke</a> * @author <a href="adrian@jboss.com">Adrian Brock</a> * @author <a href="kabir.khan@jboss.com">Kabir Khan</a> * @version $Revision: 1.8 $ */ public class ScopedClassPool extends ClassPool { protected ScopedClassPoolRepository repository; protected WeakReference classLoader; protected LoaderClassPath classPath; protected SoftValueHashMap softcache = new SoftValueHashMap(); boolean isBootstrapCl = true; static { ClassPool.doPruning = false; ClassPool.releaseUnmodifiedClassFile = false; } /** * Create a new ScopedClassPool. * * @param cl * the classloader * @param src * the original class pool * @param repository * the repository *@deprecated */ protected ScopedClassPool(ClassLoader cl, ClassPool src, ScopedClassPoolRepository repository) { this(cl, src, repository, false); } /** * Create a new ScopedClassPool. * * @param cl * the classloader * @param src * the original class pool * @param repository * the repository * @param isTemp * Whether this is a temporary pool used to resolve references */ protected ScopedClassPool(ClassLoader cl, ClassPool src, ScopedClassPoolRepository repository, boolean isTemp) { super(src); this.repository = repository; this.classLoader = new WeakReference(cl); if (cl != null) { classPath = new LoaderClassPath(cl); this.insertClassPath(classPath); } childFirstLookup = true; if (!isTemp && cl == null) { isBootstrapCl = true; } } /** * Get the class loader * * @return the class loader */ public ClassLoader getClassLoader() { ClassLoader cl = getClassLoader0(); if (cl == null && !isBootstrapCl) { throw new IllegalStateException( "ClassLoader has been garbage collected"); } return cl; } protected ClassLoader getClassLoader0() { return (ClassLoader)classLoader.get(); } /** * Close the class pool */ public void close() { this.removeClassPath(classPath); classPath.close(); classes.clear(); softcache.clear(); } /** * Flush a class * * @param classname * the class to flush */ public synchronized void flushClass(String classname) { classes.remove(classname); softcache.remove(classname); } /** * Soften a class * * @param clazz * the class */ public synchronized void soften(CtClass clazz) { if (repository.isPrune()) clazz.prune(); classes.remove(clazz.getName()); softcache.put(clazz.getName(), clazz); } /** * Whether the classloader is loader * * @return false always */ public boolean isUnloadedClassLoader() { return false; } /** * Get the cached class * * @param classname * the class name * @return the class */ protected CtClass getCached(String classname) { CtClass clazz = getCachedLocally(classname); if (clazz == null) { boolean isLocal = false; ClassLoader dcl = getClassLoader0(); if (dcl != null) { final int lastIndex = classname.lastIndexOf('$'); String classResourceName = null; if (lastIndex < 0) { classResourceName = classname.replaceAll("[\\.]", "/") + ".class"; } else { classResourceName = classname.substring(0, lastIndex) .replaceAll("[\\.]", "/") + classname.substring(lastIndex) + ".class"; } isLocal = dcl.getResource(classResourceName) != null; } if (!isLocal) { Map registeredCLs = repository.getRegisteredCLs(); synchronized (registeredCLs) { Iterator it = registeredCLs.values().iterator(); while (it.hasNext()) { ScopedClassPool pool = (ScopedClassPool)it.next(); if (pool.isUnloadedClassLoader()) { repository.unregisterClassLoader(pool .getClassLoader()); continue; } clazz = pool.getCachedLocally(classname); if (clazz != null) { return clazz; } } } } } // *NOTE* NEED TO TEST WHEN SUPERCLASS IS IN ANOTHER UCL!!!!!! return clazz; } /** * Cache a class * * @param classname * the class name * @param c * the ctClass * @param dynamic * whether the class is dynamically generated */ protected void cacheCtClass(String classname, CtClass c, boolean dynamic) { if (dynamic) { super.cacheCtClass(classname, c, dynamic); } else { if (repository.isPrune()) c.prune(); softcache.put(classname, c); } } /** * Lock a class into the cache * * @param c * the class */ public void lockInCache(CtClass c) { super.cacheCtClass(c.getName(), c, false); } /** * Whether the class is cached in this pooled * * @param classname * the class name * @return the cached class */ protected CtClass getCachedLocally(String classname) { CtClass cached = (CtClass)classes.get(classname); if (cached != null) return cached; synchronized (softcache) { return (CtClass)softcache.get(classname); } } /** * Get any local copy of the class * * @param classname * the class name * @return the class * @throws NotFoundException * when the class is not found */ public synchronized CtClass getLocally(String classname) throws NotFoundException { softcache.remove(classname); CtClass clazz = (CtClass)classes.get(classname); if (clazz == null) { clazz = createCtClass(classname, true); if (clazz == null) throw new NotFoundException(classname); super.cacheCtClass(classname, clazz, false); } return clazz; } /** * Convert a javassist class to a java class * * @param ct * the javassist class * @param loader * the loader * @throws CannotCompileException * for any error */ public Class toClass(CtClass ct, ClassLoader loader, ProtectionDomain domain) throws CannotCompileException { // We need to pass up the classloader stored in this pool, as the // default implementation uses the Thread context cl. // In the case of JSP's in Tomcat, // org.apache.jasper.servlet.JasperLoader will be stored here, while // it's parent // org.jboss.web.tomcat.tc5.WebCtxLoader$ENCLoader is used as the Thread // context cl. The invocation class needs to // be generated in the JasperLoader classloader since in the case of // method invocations, the package name will be // the same as for the class generated from the jsp, i.e. // org.apache.jsp. For classes belonging to org.apache.jsp, // JasperLoader does NOT delegate to its parent if it cannot find them. lockInCache(ct); return super.toClass(ct, getClassLoader0(), domain); } }