werken 00/11/01 12:45:33
Modified: src/java/org/apache/velocity/util/introspection
ClassMap.java Introspector.java MethodMap.java
Log:
Tweaked introspection code. Modified method-caching
code to reduce number of Hashtables and accesses
to the cache.
Revision Changes Path
1.3 +44 -33
jakarta-velocity/src/java/org/apache/velocity/util/introspection/ClassMap.java
Index: ClassMap.java
===================================================================
RCS file:
/home/cvs/jakarta-velocity/src/java/org/apache/velocity/util/introspection/ClassMap.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- ClassMap.java 2000/11/01 18:31:35 1.2
+++ ClassMap.java 2000/11/01 20:45:30 1.3
@@ -64,26 +64,27 @@
*
* @author <a href="mailto:[EMAIL PROTECTED]">Jason van Zyl</a>
* @author <a href="mailto:[EMAIL PROTECTED]">Bob McWhirter</a>
- * @version $Id: ClassMap.java,v 1.2 2000/11/01 18:31:35 jvanzyl Exp $
+ * @version $Id: ClassMap.java,v 1.3 2000/11/01 20:45:30 werken Exp $
*/
public class ClassMap
{
+ static final private class CacheMiss { }
+
+ static final private CacheMiss CACHE_MISS = new CacheMiss();
+
/**
* Class passed into the constructor used to as
* the basis for the Method map.
*/
+
private Class clazz;
- /**
- * Map of methods that can be accessed directly
- * with a method key.
- */
- private Map directHits = new Hashtable();
+
/**
- * Map of nulls that represent methodKeys that
- * will never return a valid method.
+ * Cache of Methods, or CACHE_MISS, keyed by method
+ * name and actual arguments used to find it.
*/
- private Map directMisses = new Hashtable();
+ private Map methodCache = new Hashtable();
private MethodMap methodMap = new MethodMap();
@@ -93,42 +94,52 @@
public ClassMap(Class clazz)
{
this.clazz = clazz;
- populateDirectHits();
+ populateMethodCache();
}
/**
* Find a Method using the methodKey
- * provided. First try a direct hit, if
- * that doesn't work then we have to do some
- * work to find out if we can return a Method
- * or not. Will implement this ASAP. If we
- * find a valid Method then we can generate
- * a methodKey and add it to the
- * directHits Map.
+ * provided.
+ *
+ * Look in the methodMap for an entry. If found,
+ * it'll either be a CACHE_MISS, in which case we
+ * simply give up, or it'll be a Method, in which
+ * case, we return it.
+ *
+ * If nothing is found, then we must actually go
+ * and introspect the method from the MethodMap.
*/
public Method findMethod(String name, Object[] params)
{
String methodKey = makeMethodKey(name, params);
-
- if (directMisses.containsKey(methodKey))
+
+ Object cacheEntry = methodCache.get( methodKey );
+
+ if (cacheEntry == CACHE_MISS)
+ {
return null;
-
- if (directHits.containsKey(methodKey))
- return (Method) directHits.get(methodKey);
- else
+ }
+
+ if (cacheEntry == null)
{
- Method method = methodMap.find(name, params);
+ cacheEntry = methodMap.find( name,
+ params );
- if (method == null)
- directMisses.put(methodKey, "");
+ if ( cacheEntry == null )
+ {
+ methodCache.put( methodKey,
+ CACHE_MISS );
+ }
else
{
- directHits.put(methodKey, method);
- return method;
- }
+ methodCache.put( methodKey,
+ cacheEntry );
+ }
}
-
- return null;
+
+ // Yes, this might just be null.
+
+ return (Method) cacheEntry;
}
/**
@@ -136,7 +147,7 @@
* are taken from all the public methods
* that our class provides.
*/
- private void populateDirectHits()
+ private void populateMethodCache()
{
Method[] methods = clazz.getMethods();
StringBuffer methodKey;
@@ -146,7 +157,7 @@
if (Modifier.isPublic(methods[i].getModifiers()))
{
methodMap.add(methods[i]);
- directHits.put(makeMethodKey(methods[i]), methods[i]);
+ methodCache.put(makeMethodKey(methods[i]), methods[i]);
}
}
}
1.5 +27 -6
jakarta-velocity/src/java/org/apache/velocity/util/introspection/Introspector.java
Index: Introspector.java
===================================================================
RCS file:
/home/cvs/jakarta-velocity/src/java/org/apache/velocity/util/introspection/Introspector.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- Introspector.java 2000/11/01 18:31:36 1.4
+++ Introspector.java 2000/11/01 20:45:30 1.5
@@ -83,7 +83,7 @@
* and stored for
* @author <a href="mailto:[EMAIL PROTECTED]">Jason van Zyl</a>
* @author <a href="mailto:[EMAIL PROTECTED]">Bob McWhirter</a>
- * @version $Id: Introspector.java,v 1.4 2000/11/01 18:31:36 jvanzyl Exp $
+ * @version $Id: Introspector.java,v 1.5 2000/11/01 20:45:30 werken Exp $
*/
// isAssignable checks for arguments that are subclasses
@@ -100,15 +100,36 @@
// If this is the first time seeing this class
// then create a method map for this class and
// store it in Hashtable of class method maps.
- if (!classMethodMaps.containsKey(c.getName()))
- classMethodMaps.put(c.getName(), new ClassMap(c));
+ if (!classMethodMaps.containsKey(c))
+ {
+ // Lots of threads might be whizzing through here,
+ // so we do a double-checked lock, which only involves
+ // synchronization when there's a key-miss. Avoids
+ // doing duplicate work, and constructing objects twice
+ // in particular race conditions
+
+ // Though, some folks say that double-checked-locking
+ // doesn't necessarily work-as-expected in Java on
+ // multi-proc machines. Doesn't make things worse,
+ // but just doesn't help as much as you'd imagine it
+ // would. Darn re-ordering of instructions.
+
+ synchronized (classMethodMaps)
+ {
+ if (!classMethodMaps.containsKey(c))
+ {
+ classMethodMaps.put(c, new ClassMap(c));
+ }
+ }
+ }
+
return findMethod(c, name, params);
}
private static Method findMethod(Class c, String name, Object[] params)
{
- ClassMap classMethodMap = (ClassMap) classMethodMaps.get(c.getName());
+ ClassMap classMethodMap = (ClassMap) classMethodMaps.get(c);
return classMethodMap.findMethod(name, params);
}
@@ -135,13 +156,13 @@
Object[] params =
{
new String(),
- new String()
+ new StringBuffer()
};
Object[] args =
{
"this",
- "that"
+ new StringBuffer()
};
Test t = new Test();
1.3 +30 -16
jakarta-velocity/src/java/org/apache/velocity/util/introspection/MethodMap.java
Index: MethodMap.java
===================================================================
RCS file:
/home/cvs/jakarta-velocity/src/java/org/apache/velocity/util/introspection/MethodMap.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- MethodMap.java 2000/11/01 18:31:37 1.2
+++ MethodMap.java 2000/11/01 20:45:31 1.3
@@ -65,7 +65,7 @@
*
* @author <a href="mailto:[EMAIL PROTECTED]">Jason van Zyl</a>
* @author <a href="mailto:[EMAIL PROTECTED]">Bob McWhirter</a>
- * @version $Id: MethodMap.java,v 1.2 2000/11/01 18:31:37 jvanzyl Exp $
+ * @version $Id: MethodMap.java,v 1.3 2000/11/01 20:45:31 werken Exp $
*/
public class MethodMap
@@ -90,29 +90,43 @@
public Method find(String methodName, Object[] params)
{
List methodList = (List) methodByNameMap.get(methodName);
-
+
if (methodList == null)
+ {
return null;
+ }
+
+ Class[] parameterTypes = null;
+ Method method = null;
- for (int i = 0; i < methodList.size(); i++)
+ int i = 0;
+ int numMethods = methodList.size();
+
+ while ( (method == null) && ( i < numMethods ) )
{
- Method method = (Method) methodList.get(i);
- Class[] parameterTypes = method.getParameterTypes();
+ method = (Method) methodList.get(i);
+ parameterTypes = method.getParameterTypes();
// The methods we are trying to compare must
// the same number of arguments.
- if (parameterTypes.length != params.length)
- continue;
-
- // Make sure the given parameter is a valid
- // subclass of the method parameter in question.
- for (int j = 0; j < parameterTypes.length; j++)
- if (!parameterTypes[j].isAssignableFrom(params[j].getClass()))
- break;
-
- return method;
+
+ if (parameterTypes.length == params.length)
+ {
+ // Make sure the given parameter is a valid
+ // subclass of the method parameter in question.
+
+ for (int j = 0; j < parameterTypes.length; j++)
+ {
+ if (!parameterTypes[j].isAssignableFrom(params[j].getClass()))
+ {
+ method = null;
+ break;
+ }
+ }
+ }
+ ++i;
}
- return null;
+ return method;
}
}