1/*
2 * Javassist, a Java-bytecode translator toolkit.
3 * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License.  Alternatively, the contents of this file may be used under
8 * the terms of the GNU Lesser General Public License Version 2.1 or later.
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 */
15
16package javassist.scopedpool;
17
18import java.lang.ref.WeakReference;
19import java.security.ProtectionDomain;
20import java.util.Iterator;
21import java.util.Map;
22import javassist.CannotCompileException;
23import javassist.ClassPool;
24import javassist.CtClass;
25import javassist.LoaderClassPath;
26import javassist.NotFoundException;
27
28/**
29 * A scoped class pool.
30 *
31 * @author <a href="mailto:bill@jboss.org">Bill Burke</a>
32 * @author <a href="adrian@jboss.com">Adrian Brock</a>
33 * @author <a href="kabir.khan@jboss.com">Kabir Khan</a>
34 * @version $Revision: 1.8 $
35 */
36public class ScopedClassPool extends ClassPool {
37    protected ScopedClassPoolRepository repository;
38
39    protected WeakReference classLoader;
40
41    protected LoaderClassPath classPath;
42
43    protected SoftValueHashMap softcache = new SoftValueHashMap();
44
45    boolean isBootstrapCl = true;
46
47    static {
48        ClassPool.doPruning = false;
49        ClassPool.releaseUnmodifiedClassFile = false;
50    }
51
52    /**
53     * Create a new ScopedClassPool.
54     *
55     * @param cl
56     *            the classloader
57     * @param src
58     *            the original class pool
59     * @param repository
60     *            the repository
61     *@deprecated
62     */
63    protected ScopedClassPool(ClassLoader cl, ClassPool src,
64            ScopedClassPoolRepository repository) {
65       this(cl, src, repository, false);
66    }
67
68    /**
69     * Create a new ScopedClassPool.
70     *
71     * @param cl
72     *            the classloader
73     * @param src
74     *            the original class pool
75     * @param repository
76     *            the repository
77     * @param isTemp
78     *            Whether this is a temporary pool used to resolve references
79     */
80    protected ScopedClassPool(ClassLoader cl, ClassPool src, ScopedClassPoolRepository repository, boolean isTemp)
81    {
82       super(src);
83       this.repository = repository;
84       this.classLoader = new WeakReference(cl);
85       if (cl != null) {
86           classPath = new LoaderClassPath(cl);
87           this.insertClassPath(classPath);
88       }
89       childFirstLookup = true;
90       if (!isTemp && cl == null)
91       {
92          isBootstrapCl = true;
93       }
94    }
95
96    /**
97     * Get the class loader
98     *
99     * @return the class loader
100     */
101    public ClassLoader getClassLoader() {
102       ClassLoader cl = getClassLoader0();
103       if (cl == null && !isBootstrapCl)
104       {
105          throw new IllegalStateException(
106                  "ClassLoader has been garbage collected");
107       }
108       return cl;
109    }
110
111    protected ClassLoader getClassLoader0() {
112       return (ClassLoader)classLoader.get();
113    }
114
115    /**
116     * Close the class pool
117     */
118    public void close() {
119        this.removeClassPath(classPath);
120        classPath.close();
121        classes.clear();
122        softcache.clear();
123    }
124
125    /**
126     * Flush a class
127     *
128     * @param classname
129     *            the class to flush
130     */
131    public synchronized void flushClass(String classname) {
132        classes.remove(classname);
133        softcache.remove(classname);
134    }
135
136    /**
137     * Soften a class
138     *
139     * @param clazz
140     *            the class
141     */
142    public synchronized void soften(CtClass clazz) {
143        if (repository.isPrune())
144            clazz.prune();
145        classes.remove(clazz.getName());
146        softcache.put(clazz.getName(), clazz);
147    }
148
149    /**
150     * Whether the classloader is loader
151     *
152     * @return false always
153     */
154    public boolean isUnloadedClassLoader() {
155        return false;
156    }
157
158    /**
159     * Get the cached class
160     *
161     * @param classname
162     *            the class name
163     * @return the class
164     */
165    protected CtClass getCached(String classname) {
166        CtClass clazz = getCachedLocally(classname);
167        if (clazz == null) {
168            boolean isLocal = false;
169
170            ClassLoader dcl = getClassLoader0();
171            if (dcl != null) {
172                final int lastIndex = classname.lastIndexOf('$');
173                String classResourceName = null;
174                if (lastIndex < 0) {
175                    classResourceName = classname.replaceAll("[\\.]", "/")
176                            + ".class";
177                }
178                else {
179                    classResourceName = classname.substring(0, lastIndex)
180                            .replaceAll("[\\.]", "/")
181                            + classname.substring(lastIndex) + ".class";
182                }
183
184                isLocal = dcl.getResource(classResourceName) != null;
185            }
186
187            if (!isLocal) {
188                Map registeredCLs = repository.getRegisteredCLs();
189                synchronized (registeredCLs) {
190                    Iterator it = registeredCLs.values().iterator();
191                    while (it.hasNext()) {
192                        ScopedClassPool pool = (ScopedClassPool)it.next();
193                        if (pool.isUnloadedClassLoader()) {
194                            repository.unregisterClassLoader(pool
195                                    .getClassLoader());
196                            continue;
197                        }
198
199                        clazz = pool.getCachedLocally(classname);
200                        if (clazz != null) {
201                            return clazz;
202                        }
203                    }
204                }
205            }
206        }
207        // *NOTE* NEED TO TEST WHEN SUPERCLASS IS IN ANOTHER UCL!!!!!!
208        return clazz;
209    }
210
211    /**
212     * Cache a class
213     *
214     * @param classname
215     *            the class name
216     * @param c
217     *            the ctClass
218     * @param dynamic
219     *            whether the class is dynamically generated
220     */
221    protected void cacheCtClass(String classname, CtClass c, boolean dynamic) {
222        if (dynamic) {
223            super.cacheCtClass(classname, c, dynamic);
224        }
225        else {
226            if (repository.isPrune())
227                c.prune();
228            softcache.put(classname, c);
229        }
230    }
231
232    /**
233     * Lock a class into the cache
234     *
235     * @param c
236     *            the class
237     */
238    public void lockInCache(CtClass c) {
239        super.cacheCtClass(c.getName(), c, false);
240    }
241
242    /**
243     * Whether the class is cached in this pooled
244     *
245     * @param classname
246     *            the class name
247     * @return the cached class
248     */
249    protected CtClass getCachedLocally(String classname) {
250        CtClass cached = (CtClass)classes.get(classname);
251        if (cached != null)
252            return cached;
253        synchronized (softcache) {
254            return (CtClass)softcache.get(classname);
255        }
256    }
257
258    /**
259     * Get any local copy of the class
260     *
261     * @param classname
262     *            the class name
263     * @return the class
264     * @throws NotFoundException
265     *             when the class is not found
266     */
267    public synchronized CtClass getLocally(String classname)
268            throws NotFoundException {
269        softcache.remove(classname);
270        CtClass clazz = (CtClass)classes.get(classname);
271        if (clazz == null) {
272            clazz = createCtClass(classname, true);
273            if (clazz == null)
274                throw new NotFoundException(classname);
275            super.cacheCtClass(classname, clazz, false);
276        }
277
278        return clazz;
279    }
280
281    /**
282     * Convert a javassist class to a java class
283     *
284     * @param ct
285     *            the javassist class
286     * @param loader
287     *            the loader
288     * @throws CannotCompileException
289     *             for any error
290     */
291    public Class toClass(CtClass ct, ClassLoader loader, ProtectionDomain domain)
292            throws CannotCompileException {
293        // We need to pass up the classloader stored in this pool, as the
294        // default implementation uses the Thread context cl.
295        // In the case of JSP's in Tomcat,
296        // org.apache.jasper.servlet.JasperLoader will be stored here, while
297        // it's parent
298        // org.jboss.web.tomcat.tc5.WebCtxLoader$ENCLoader is used as the Thread
299        // context cl. The invocation class needs to
300        // be generated in the JasperLoader classloader since in the case of
301        // method invocations, the package name will be
302        // the same as for the class generated from the jsp, i.e.
303        // org.apache.jsp. For classes belonging to org.apache.jsp,
304        // JasperLoader does NOT delegate to its parent if it cannot find them.
305        lockInCache(ct);
306        return super.toClass(ct, getClassLoader0(), domain);
307    }
308}
309