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.Map;
25  import java.util.Set;
26  
27  import org.apache.velocity.runtime.log.Log;
28  
29  /**
30   * This is the internal introspector cache implementation.
31   *
32   * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
33   * @author <a href="mailto:cdauth@cdauth.eu">Candid Dauth</a>
34   * @version $Id: IntrospectorCacheImpl.java 1329799 2012-04-24 15:41:21Z cbrisson $
35   * @since 1.5
36   */
37  public final class IntrospectorCacheImpl implements IntrospectorCache
38  {
39      /**
40       * define a public string so that it can be looked for if interested
41       */
42      public final static String CACHEDUMP_MSG =
43          "IntrospectorCache detected classloader change. Dumping cache.";
44  
45      /** Class logger */
46      private final Log log;
47      
48      /**
49       * Holds the method maps for the classes we know about. Map: Class --&gt; ClassMap object.
50       */
51      private final Map classMapCache = new HashMap();
52  
53      /**
54       * Holds the field maps for the classes we know about. Map: Class --&gt; ClassFieldMap object.
55       */
56      private final Map classFieldMapCache = new HashMap();
57  
58      /**
59       * Keep the names of the classes in another map. This is needed for a multi-classloader environment where it is possible
60       * to have Class 'Foo' loaded by a classloader and then get asked to introspect on 'Foo' from another class loader. While these
61       * two Class objects have the same name, a <code>classMethodMaps.get(Foo.class)</code> will return null. For that case, we
62       * keep a set of class names to recognize this case.  
63       */
64      private final Set classNameCache = new HashSet();
65  
66      /**
67       * C'tor
68       */
69      public IntrospectorCacheImpl(final Log log)
70      {
71  	    this.log = log;
72      }
73  
74      /**
75       * Clears the internal cache.
76       */
77      public void clear()
78      {
79          synchronized (classMapCache)
80          {
81              classMapCache.clear();
82              classFieldMapCache.clear();
83              classNameCache.clear();
84              log.debug(CACHEDUMP_MSG);
85          }
86      }
87  
88      /**
89       * Lookup a given Class object in the cache. If it does not exist,
90       * check whether this is due to a class change and purge the caches
91       * eventually.
92       *
93       * @param c The class to look up.
94       * @return A ClassMap object or null if it does not exist in the cache.
95       */
96      public ClassMap get(final Class c)
97      {
98          if (c == null)
99          {
100             throw new IllegalArgumentException("class is null!");
101         }
102 
103         ClassMap classMap = (ClassMap)classMapCache.get(c);
104         if (classMap == null)
105         {
106             /*
107              * check to see if we have it by name.
108              * if so, then we have an object with the same
109              * name but loaded through a different class loader.
110              * In that case, we will just dump the cache to be sure.
111              */
112             synchronized (classMapCache)
113             {
114                 if (classNameCache.contains(c.getName()))
115                 {
116                     clear();
117                 }
118             }
119         }
120         return classMap;
121     }
122 
123     /**
124      * Lookup a given Class object in the cache. If it does not exist,
125      * check whether this is due to a class change and purge the caches
126      * eventually.
127      *
128      * @param c The class to look up.
129      * @return A ClassFieldMap object or null if it does not exist in the cache.
130      */
131     public ClassFieldMap getFieldMap(final Class c)
132     {
133         if (c == null)
134         {
135             throw new IllegalArgumentException("class is null!");
136         }
137 
138         ClassFieldMap classFieldMap = (ClassFieldMap)classFieldMapCache.get(c);
139         if (classFieldMap == null)
140         {
141             /*
142              * check to see if we have it by name.
143              * if so, then we have an object with the same
144              * name but loaded through a different class loader.
145              * In that case, we will just dump the cache to be sure.
146              */
147             synchronized (classMapCache)
148             {
149                 if (classNameCache.contains(c.getName()))
150                 {
151                     clear();
152                 }
153             }
154         }
155         return classFieldMap;
156     }
157 
158     /**
159      * Creates a class map for specific class and registers it in the
160      * cache.  Also adds the qualified name to the name-&gt;class map
161      * for later Classloader change detection.
162      *
163      * @param c The class for which the class map gets generated.
164      * @return A ClassMap object.
165      */
166     public ClassMap put(final Class c)
167     {
168         final ClassMap classMap = new ClassMap(c, log);
169         final ClassFieldMap classFieldMap = new ClassFieldMap(c, log);
170         synchronized (classMapCache)
171         {
172             classMapCache.put(c, classMap);
173             classFieldMapCache.put(c, classFieldMap);
174             classNameCache.add(c.getName());
175         }
176         return classMap;
177     }
178 
179 }