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;
       }
   }
  
  
  

Reply via email to