• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 org.apache.commons.lang3.Validate;
23 
24 import org.slf4j.Logger;
25 
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.Map;
29 import java.util.Set;
30 
31 /**
32  * This is the internal introspector cache implementation.
33  *
34  * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
35  * @author <a href="mailto:cdauth@cdauth.eu">Candid Dauth</a>
36  * @version $Id$
37  * @since 1.5
38  */
39 public final class IntrospectorCache
40 {
41     /**
42      * define a public string so that it can be looked for if interested
43      */
44     public final static String CACHEDUMP_MSG =
45             "IntrospectorCache detected classloader change. Dumping cache.";
46 
47     /** Class logger */
48     private final Logger log;
49 
50     /**
51      * Holds the method maps for the classes we know about. Map: Class --&gt; ClassMap object.
52      */
53     private final Map<Class<?>, ClassMap> classMapCache = new HashMap<>();
54 
55     /**
56      * Holds the field maps for the classes we know about. Map: Class --&gt; ClassFieldMap object.
57      */
58     private final Map<Class<?>, ClassFieldMap> classFieldMapCache = new HashMap<>();
59 
60     /**
61      * Keep the names of the classes in another map. This is needed for a multi-classloader environment where it is possible
62      * to have Class 'Foo' loaded by a classloader and then get asked to introspect on 'Foo' from another class loader. While these
63      * two Class objects have the same name, a <code>classMethodMaps.get(Foo.class)</code> will return null. For that case, we
64      * keep a set of class names to recognize this case.
65      */
66     private final Set<String> classNameCache = new HashSet<>();
67 
68     /**
69      * Conversion handler
70      */
71     private final TypeConversionHandler conversionHandler;
72 
73     /**
74      * C'tor
75      * @param log logger.
76      * @param conversionHandler conversion handler
77      */
IntrospectorCache(final Logger log, final TypeConversionHandler conversionHandler)78     public IntrospectorCache(final Logger log, final TypeConversionHandler conversionHandler)
79     {
80         this.log = log;
81         this.conversionHandler = conversionHandler;
82     }
83 
84     /**
85      * Clears the internal cache.
86      */
clear()87     public void clear()
88     {
89         synchronized (classMapCache)
90         {
91             classMapCache.clear();
92             classFieldMapCache.clear();
93             classNameCache.clear();
94             log.debug(CACHEDUMP_MSG);
95         }
96     }
97 
98     /**
99      * Lookup a given Class object in the cache. If it does not exist,
100      * check whether this is due to a class change and purge the caches
101      * eventually.
102      *
103      * @param c The class to look up.
104      * @return A ClassMap object or null if it does not exist in the cache.
105      */
get(final Class<?> c)106     public ClassMap get(final Class<?> c)
107     {
108         ClassMap classMap = classMapCache.get(Validate.notNull(c));
109         if (classMap == null)
110         {
111             /*
112              * check to see if we have it by name.
113              * if so, then we have an object with the same
114              * name but loaded through a different class loader.
115              * In that case, we will just dump the cache to be sure.
116              */
117             synchronized (classMapCache)
118             {
119                 if (classNameCache.contains(c.getName()))
120                 {
121                     clear();
122                 }
123             }
124         }
125         return classMap;
126     }
127 
128     /**
129      * Lookup a given Class object in the cache. If it does not exist,
130      * check whether this is due to a class change and purge the caches
131      * eventually.
132      *
133      * @param c The class to look up.
134      * @return A ClassFieldMap object or null if it does not exist in the cache.
135      */
getFieldMap(final Class<?> c)136     public ClassFieldMap getFieldMap(final Class<?> c)
137     {
138         ClassFieldMap classFieldMap = classFieldMapCache.get(Validate.notNull(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      */
put(final Class<?> c)166     public ClassMap put(final Class<?> c)
167     {
168         final ClassMap classMap = new ClassMap(c, log, conversionHandler);
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 }
180