001: package org.apache.velocity.util.introspection;
002:
003: /*
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: import java.util.HashMap;
023: import java.util.HashSet;
024: import java.util.Iterator;
025: import java.util.Map;
026: import java.util.Set;
027:
028: import org.apache.velocity.runtime.log.Log;
029:
030: /**
031: * This is the internal introspector cache implementation.
032: *
033: * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
034: * @version $Id: IntrospectorCacheImpl.java 477003 2006-11-20 01:14:22Z henning $
035: */
036: public final class IntrospectorCacheImpl implements IntrospectorCache {
037: /** Class logger */
038: private final Log log;
039:
040: /**
041: * Holds the method maps for the classes we know about. Map: Class --> ClassMap object.
042: */
043: private final Map classMapCache = new HashMap();
044:
045: /**
046: * Keep the names of the classes in another map. This is needed for a multi-classloader environment where it is possible
047: * to have Class 'Foo' loaded by a classloader and then get asked to introspect on 'Foo' from another class loader. While these
048: * two Class objects have the same name, a <code>classMethodMaps.get(Foo.class)</code> will return null. For that case, we
049: * keep a set of class names to recognize this case.
050: */
051: private final Set classNameCache = new HashSet();
052:
053: /**
054: * Set of IntrospectorCache Listeners.
055: */
056: private final Set listeners = new HashSet();
057:
058: /**
059: * C'tor
060: */
061: public IntrospectorCacheImpl(final Log log) {
062: this .log = log;
063: }
064:
065: /**
066: * Clears the internal cache.
067: */
068: public synchronized void clear() {
069: classMapCache.clear();
070: classNameCache.clear();
071: for (Iterator it = listeners.iterator(); it.hasNext();) {
072: ((IntrospectorCacheListener) it.next()).triggerClear();
073: }
074: }
075:
076: /**
077: * Lookup a given Class object in the cache. If it does not exist,
078: * check whether this is due to a class change and purge the caches
079: * eventually.
080: *
081: * @param c The class to look up.
082: * @return A ClassMap object or null if it does not exist in the cache.
083: */
084: public synchronized ClassMap get(final Class c) {
085: if (c == null) {
086: throw new IllegalArgumentException("class is null!");
087: }
088:
089: ClassMap classMap = (ClassMap) classMapCache.get(c);
090:
091: /*
092: * If we don't have this, check to see if we have it
093: * by name. if so, then we have an object with the same
094: * name but loaded through a different class loader.
095: * In that case, we will just dump the cache to be sure.
096: */
097:
098: if (classMap == null) {
099: if (classNameCache.contains(c.getName())) {
100: clear();
101: }
102: }
103:
104: for (Iterator it = listeners.iterator(); it.hasNext();) {
105: ((IntrospectorCacheListener) it.next()).triggerGet(c,
106: classMap);
107: }
108:
109: return classMap;
110: }
111:
112: /**
113: * Creates a class map for specific class and registers it in the
114: * cache. Also adds the qualified name to the name->class map
115: * for later Classloader change detection.
116: *
117: * @param c The class for which the class map gets generated.
118: * @return A ClassMap object.
119: */
120: public synchronized ClassMap put(final Class c) {
121: ClassMap classMap = new ClassMap(c, log);
122: classMapCache.put(c, classMap);
123: classNameCache.add(c.getName());
124:
125: for (Iterator it = listeners.iterator(); it.hasNext();) {
126: ((IntrospectorCacheListener) it.next()).triggerPut(c,
127: classMap);
128: }
129:
130: return classMap;
131: }
132:
133: /**
134: * Register a Cache listener.
135: *
136: * @param listener A Cache listener object.
137: */
138: public void addListener(final IntrospectorCacheListener listener) {
139: listeners.add(listener);
140: }
141:
142: /**
143: * Remove a Cache listener.
144: *
145: * @param listener A Cache listener object.
146: */
147: public void removeListener(final IntrospectorCacheListener listener) {
148: listeners.remove(listener);
149: }
150: }
|