001: /*
002: * Javassist, a Java-bytecode translator toolkit.
003: * Copyright (C) 2006 JBoss Inc. All Rights Reserved.
004: *
005: * The contents of this file are subject to the Mozilla Public License Version
006: * 1.1 (the "License"); you may not use this file except in compliance with
007: * the License. Alternatively, the contents of this file may be used under
008: * the terms of the GNU Lesser General Public License Version 2.1 or later.
009: *
010: * Software distributed under the License is distributed on an "AS IS" basis,
011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
012: * for the specific language governing rights and limitations under the
013: * License.
014: */
015:
016: package javassist.scopedpool;
017:
018: import java.lang.ref.WeakReference;
019: import java.security.ProtectionDomain;
020: import java.util.Iterator;
021: import java.util.Map;
022: import javassist.CannotCompileException;
023: import javassist.ClassPool;
024: import javassist.CtClass;
025: import javassist.LoaderClassPath;
026: import javassist.NotFoundException;
027:
028: /**
029: * A scoped class pool.
030: *
031: * @author <a href="mailto:bill@jboss.org">Bill Burke</a>
032: * @author <a href="adrian@jboss.com">Adrian Brock</a>
033: * @author <a href="kabir.khan@jboss.com">Kabir Khan</a>
034: * @version $Revision: 1.6 $
035: */
036: public class ScopedClassPool extends ClassPool {
037: protected ScopedClassPoolRepository repository;
038:
039: protected WeakReference classLoader;
040:
041: protected LoaderClassPath classPath;
042:
043: protected SoftValueHashMap softcache = new SoftValueHashMap();
044:
045: static {
046: ClassPool.doPruning = false;
047: ClassPool.releaseUnmodifiedClassFile = false;
048: }
049:
050: /**
051: * Create a new ScopedClassPool.
052: *
053: * @param cl
054: * the classloader
055: * @param src
056: * the original class pool
057: * @param repository
058: * the repository
059: */
060: protected ScopedClassPool(ClassLoader cl, ClassPool src,
061: ScopedClassPoolRepository repository) {
062: super (src);
063: this .repository = repository;
064: this .classLoader = new WeakReference(cl);
065: if (cl != null) {
066: classPath = new LoaderClassPath(cl);
067: this .insertClassPath(classPath);
068: }
069: childFirstLookup = true;
070: }
071:
072: /**
073: * Get the class loader
074: *
075: * @return the class loader
076: */
077: public ClassLoader getClassLoader() {
078: ClassLoader cl = getClassLoader0();
079: if (cl == null)
080: throw new IllegalStateException(
081: "ClassLoader has been garbage collected");
082: return cl;
083: }
084:
085: protected ClassLoader getClassLoader0() {
086: return (ClassLoader) classLoader.get();
087: }
088:
089: /**
090: * Close the class pool
091: */
092: public void close() {
093: this .removeClassPath(classPath);
094: classPath.close();
095: classes.clear();
096: softcache.clear();
097: }
098:
099: /**
100: * Flush a class
101: *
102: * @param classname
103: * the class to flush
104: */
105: public synchronized void flushClass(String classname) {
106: classes.remove(classname);
107: softcache.remove(classname);
108: }
109:
110: /**
111: * Soften a class
112: *
113: * @param clazz
114: * the class
115: */
116: public synchronized void soften(CtClass clazz) {
117: if (repository.isPrune())
118: clazz.prune();
119: classes.remove(clazz.getName());
120: softcache.put(clazz.getName(), clazz);
121: }
122:
123: /**
124: * Whether the classloader is loader
125: *
126: * @return false always
127: */
128: public boolean isUnloadedClassLoader() {
129: return false;
130: }
131:
132: /**
133: * Get the cached class
134: *
135: * @param classname
136: * the class name
137: * @return the class
138: */
139: protected CtClass getCached(String classname) {
140: CtClass clazz = getCachedLocally(classname);
141: if (clazz == null) {
142: boolean isLocal = false;
143:
144: ClassLoader dcl = getClassLoader0();
145: if (dcl != null) {
146: final int lastIndex = classname.lastIndexOf('$');
147: String classResourceName = null;
148: if (lastIndex < 0) {
149: classResourceName = classname.replaceAll("[\\.]",
150: "/")
151: + ".class";
152: } else {
153: classResourceName = classname.substring(0,
154: lastIndex).replaceAll("[\\.]", "/")
155: + classname.substring(lastIndex) + ".class";
156: }
157:
158: isLocal = dcl.getResource(classResourceName) != null;
159: }
160:
161: if (!isLocal) {
162: Map registeredCLs = repository.getRegisteredCLs();
163: synchronized (registeredCLs) {
164: Iterator it = registeredCLs.values().iterator();
165: while (it.hasNext()) {
166: ScopedClassPool pool = (ScopedClassPool) it
167: .next();
168: if (pool.isUnloadedClassLoader()) {
169: repository.unregisterClassLoader(pool
170: .getClassLoader());
171: continue;
172: }
173:
174: clazz = pool.getCachedLocally(classname);
175: if (clazz != null) {
176: return clazz;
177: }
178: }
179: }
180: }
181: }
182: // *NOTE* NEED TO TEST WHEN SUPERCLASS IS IN ANOTHER UCL!!!!!!
183: return clazz;
184: }
185:
186: /**
187: * Cache a class
188: *
189: * @param classname
190: * the class name
191: * @param c
192: * the ctClass
193: * @param dynamic
194: * whether the class is dynamically generated
195: */
196: protected void cacheCtClass(String classname, CtClass c,
197: boolean dynamic) {
198: if (dynamic) {
199: super .cacheCtClass(classname, c, dynamic);
200: } else {
201: if (repository.isPrune())
202: c.prune();
203: softcache.put(classname, c);
204: }
205: }
206:
207: /**
208: * Lock a class into the cache
209: *
210: * @param c
211: * the class
212: */
213: public void lockInCache(CtClass c) {
214: super .cacheCtClass(c.getName(), c, false);
215: }
216:
217: /**
218: * Whether the class is cached in this pooled
219: *
220: * @param classname
221: * the class name
222: * @return the cached class
223: */
224: protected CtClass getCachedLocally(String classname) {
225: CtClass cached = (CtClass) classes.get(classname);
226: if (cached != null)
227: return cached;
228: synchronized (softcache) {
229: return (CtClass) softcache.get(classname);
230: }
231: }
232:
233: /**
234: * Get any local copy of the class
235: *
236: * @param classname
237: * the class name
238: * @return the class
239: * @throws NotFoundException
240: * when the class is not found
241: */
242: public synchronized CtClass getLocally(String classname)
243: throws NotFoundException {
244: softcache.remove(classname);
245: CtClass clazz = (CtClass) classes.get(classname);
246: if (clazz == null) {
247: clazz = createCtClass(classname, true);
248: if (clazz == null)
249: throw new NotFoundException(classname);
250: super .cacheCtClass(classname, clazz, false);
251: }
252:
253: return clazz;
254: }
255:
256: /**
257: * Convert a javassist class to a java class
258: *
259: * @param ct
260: * the javassist class
261: * @param loader
262: * the loader
263: * @throws CannotCompileException
264: * for any error
265: */
266: public Class toClass(CtClass ct, ClassLoader loader,
267: ProtectionDomain domain) throws CannotCompileException {
268: // We need to pass up the classloader stored in this pool, as the
269: // default implementation uses the Thread context cl.
270: // In the case of JSP's in Tomcat,
271: // org.apache.jasper.servlet.JasperLoader will be stored here, while
272: // it's parent
273: // org.jboss.web.tomcat.tc5.WebCtxLoader$ENCLoader is used as the Thread
274: // context cl. The invocation class needs to
275: // be generated in the JasperLoader classloader since in the case of
276: // method invocations, the package name will be
277: // the same as for the class generated from the jsp, i.e.
278: // org.apache.jsp. For classes belonging to org.apache.jsp,
279: // JasperLoader does NOT delegate to its parent if it cannot find them.
280: lockInCache(ct);
281: return super.toClass(ct, getClassLoader0(), domain);
282: }
283: }
|