View Javadoc

1   package org.apache.velocity.util.introspection;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.    
20   */
21  
22  import java.util.HashMap;
23  import java.util.HashSet;
24  import java.util.Iterator;
25  import java.util.Map;
26  import java.util.Set;
27  
28  import org.apache.velocity.runtime.log.Log;
29  
30  /**
31   * This is the internal introspector cache implementation.
32   *
33   * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
34   * @version $Id: IntrospectorCacheImpl.java 477003 2006-11-20 01:14:22Z henning $
35   */
36  public final class IntrospectorCacheImpl
37          implements IntrospectorCache
38  {
39      /** Class logger */
40      private final Log log;
41      
42      /**
43       * Holds the method maps for the classes we know about. Map: Class --&gt; ClassMap object.
44       */
45      private final Map classMapCache = new HashMap();
46  
47      /**
48       * Keep the names of the classes in another map. This is needed for a multi-classloader environment where it is possible
49       * to have Class 'Foo' loaded by a classloader and then get asked to introspect on 'Foo' from another class loader. While these
50       * two Class objects have the same name, a <code>classMethodMaps.get(Foo.class)</code> will return null. For that case, we
51       * keep a set of class names to recognize this case.  
52       */
53      private final Set classNameCache = new HashSet();
54  
55      /**
56       * Set of IntrospectorCache Listeners.
57       */
58      private final Set listeners = new HashSet();
59  
60      /**
61       * C'tor
62       */
63      public IntrospectorCacheImpl(final Log log)
64      {
65  	this.log = log;
66      }
67  
68      /**
69       * Clears the internal cache.
70       */
71      public synchronized void clear()
72      {
73          classMapCache.clear();
74          classNameCache.clear();
75          for (Iterator it = listeners.iterator(); it.hasNext(); )
76          {
77              ((IntrospectorCacheListener) it.next()).triggerClear();
78          }
79      }
80  
81      /**
82       * Lookup a given Class object in the cache. If it does not exist, 
83       * check whether this is due to a class change and purge the caches
84       * eventually.
85       *
86       * @param c The class to look up.
87       * @return A ClassMap object or null if it does not exist in the cache.
88       */
89      public synchronized ClassMap get(final Class c)
90      {
91          if (c == null)
92          {
93              throw new IllegalArgumentException("class is null!");
94          }
95  
96          ClassMap classMap = (ClassMap) classMapCache.get(c);
97  
98          /*
99           * If we don't have this, check to see if we have it
100          * by name.  if so, then we have an object with the same
101          * name but loaded through a different class loader.
102          * In that case, we will just dump the cache to be sure.
103          */
104         
105         if (classMap == null)
106         {
107             if (classNameCache.contains(c.getName()))
108             {
109                 clear();
110             }
111         }
112 
113         for (Iterator it = listeners.iterator(); it.hasNext(); )
114         {
115             ((IntrospectorCacheListener) it.next()).triggerGet(c, classMap);
116         }
117 
118         return classMap;
119     }
120 
121     /**
122      * Creates a class map for specific class and registers it in the
123      * cache.  Also adds the qualified name to the name-&gt;class map
124      * for later Classloader change detection.
125      *
126      * @param c The class for which the class map gets generated.
127      * @return A ClassMap object.
128      */
129     public synchronized ClassMap put(final Class c)
130     {
131         ClassMap classMap = new ClassMap(c, log);
132         classMapCache.put(c, classMap);
133         classNameCache.add(c.getName());
134         
135         for (Iterator it = listeners.iterator(); it.hasNext(); )
136         {
137             ((IntrospectorCacheListener) it.next()).triggerPut(c, classMap);
138         }
139 
140         return classMap;
141     }
142 
143     /**
144      * Register a Cache listener.
145      *
146      * @param listener A Cache listener object.
147      */
148     public void addListener(final IntrospectorCacheListener listener)
149     {
150         listeners.add(listener);
151     }
152 
153     /**
154      * Remove a Cache listener.
155      *
156      * @param listener A Cache listener object.
157      */
158     public void removeListener(final IntrospectorCacheListener listener)
159     {
160         listeners.remove(listener);
161     }
162 }