[jboss-cvs] JBossAS SVN: r61752 - in projects/aop/trunk/aop/src: test/org/jboss/test/aop/methodhashing and 1 other directory.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Tue Mar 27 12:03:50 EDT 2007


Author: kabir.khan at jboss.com
Date: 2007-03-27 12:03:50 -0400 (Tue, 27 Mar 2007)
New Revision: 61752

Modified:
   projects/aop/trunk/aop/src/main/org/jboss/aop/util/MethodHashing.java
   projects/aop/trunk/aop/src/test/org/jboss/test/aop/methodhashing/MethodHashingTestCase.java
Log:
Optimize MethodHashing so we don't calculate the hash every time we call findMethodByHash

Modified: projects/aop/trunk/aop/src/main/org/jboss/aop/util/MethodHashing.java
===================================================================
--- projects/aop/trunk/aop/src/main/org/jboss/aop/util/MethodHashing.java	2007-03-27 16:02:50 UTC (rev 61751)
+++ projects/aop/trunk/aop/src/main/org/jboss/aop/util/MethodHashing.java	2007-03-27 16:03:50 UTC (rev 61752)
@@ -31,6 +31,9 @@
 import java.util.Map;
 import java.util.WeakHashMap;
 
+import org.jboss.aop.util.reference.MethodPersistentReference;
+import org.jboss.aop.util.reference.PersistentReference;
+
 /**
  * Create a unique hash for  
  * 
@@ -44,12 +47,20 @@
    // Static --------------------------------------------------------
    static Map hashMap = new WeakHashMap();
 
+   static Map methodHashesByClass = new WeakHashMap();
+
    public static Method findMethodByHash(Class clazz, long hash) throws Exception
    {
-      Method[] methods = SecurityActions.getDeclaredMethods(clazz);
-      for (int i = 0; i < methods.length; i++)
+      return findMethodByHash(clazz, new Long(hash));
+   }
+
+   public static Method findMethodByHash(Class clazz, Long hash) throws Exception
+   {
+      Map hashes = getMethodHashes(clazz);
+      MethodPersistentReference ref = (MethodPersistentReference)hashes.get(hash);
+      if (ref != null)
       {
-         if (methodHash(methods[i]) == hash) return methods[i];
+         return ref.getMethod();
       }
 
       if (clazz.isInterface())
@@ -71,7 +82,7 @@
       }
       return null;
    }
-
+   
    public static Constructor findConstructorByHash(Class clazz, long hash) throws Exception
    {
       Constructor[] cons = SecurityActions.getDeclaredConstructors(clazz);
@@ -90,13 +101,13 @@
       throws Exception
    {
       Class[] parameterTypes = method.getParameterTypes();
-      String methodDesc = method.getName()+"(";
+      StringBuffer methodDesc = new StringBuffer(method.getName()+"(");
       for(int j = 0; j < parameterTypes.length; j++)
       {
-         methodDesc += getTypeString(parameterTypes[j]);
+         methodDesc.append(getTypeString(parameterTypes[j]));
       }
-      methodDesc += ")"+getTypeString(method.getReturnType());
-      return createHash(methodDesc);
+      methodDesc.append(")"+getTypeString(method.getReturnType()));
+      return createHash(methodDesc.toString());
    }
    
    public static long createHash(String methodDesc)
@@ -119,52 +130,16 @@
       throws Exception
    {
       Class[] parameterTypes = method.getParameterTypes();
-      String methodDesc = method.getName()+"(";
+      StringBuffer methodDesc = new StringBuffer(method.getName()+"(");
       for(int j = 0; j < parameterTypes.length; j++)
       {
-         methodDesc += getTypeString(parameterTypes[j]);
+         methodDesc.append(getTypeString(parameterTypes[j]));
       }
-      methodDesc += ")";
-
-      long hash = 0;
-      ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream(512);
-      MessageDigest messagedigest = MessageDigest.getInstance("SHA");
-      DataOutputStream dataoutputstream = new DataOutputStream(new DigestOutputStream(bytearrayoutputstream, messagedigest));
-      dataoutputstream.writeUTF(methodDesc);
-      dataoutputstream.flush();
-      byte abyte0[] = messagedigest.digest();
-      for(int j = 0; j < Math.min(8, abyte0.length); j++)
-         hash += (long)(abyte0[j] & 0xff) << j * 8;
-      return hash;
+      methodDesc.append(")");
+      
+      return createHash(methodDesc.toString());
    }
 
-   /**
-   * Calculate method hashes. This algo is taken from RMI.
-   *
-   * @param   intf  
-   * @return     
-   */
-   public static Map getInterfaceHashes(Class intf)
-   {
-      // Create method hashes
-      Method[] methods = SecurityActions.getDeclaredMethods(intf);
-      HashMap map = new HashMap();
-      for (int i = 0; i < methods.length; i++)
-      {
-         Method method = methods[i];
-         try
-         {
-            long hash = methodHash(method);
-            map.put(method.toString(), new Long(hash));
-         }
-         catch (Exception e)
-         {
-         }
-      }
-      
-      return map;
-   }
-   
    static String getTypeString(Class cl)
    {
       if (cl == Byte.TYPE)
@@ -228,4 +203,65 @@
       return ((Long)methodHashes.get(method.toString())).longValue();
    }
 
+   
+   /**
+    * Calculate method hashes. This algo is taken from RMI.
+    *
+    * @param   intf  
+    * @return
+    * @deprecated I can't see why this would have any value to anybody apart from this class. It will be made private     
+    */
+    public static Map getInterfaceHashes(Class intf)
+    {
+       // Create method hashes
+       Method[] methods = SecurityActions.getDeclaredMethods(intf);
+       HashMap map = new HashMap();
+       for (int i = 0; i < methods.length; i++)
+       {
+          Method method = methods[i];
+          try
+          {
+             long hash = methodHash(method);
+             map.put(method.toString(), new Long(hash));
+          }
+          catch (Exception e)
+          {
+          }
+       }
+       
+       return map;
+   }
+    
+   private static Map getMethodHashes(Class clazz)
+   {
+      Map methodHashes = (Map)methodHashesByClass.get(clazz);
+      if (methodHashes == null)
+      {
+         methodHashes = getMethodHashMap(clazz);
+         methodHashesByClass.put(clazz, methodHashes);
+      }
+      return methodHashes;
+   }
+   
+   private static Map getMethodHashMap(Class clazz)
+   {
+      // Create method hashes
+      Method[] methods = SecurityActions.getDeclaredMethods(clazz);
+      HashMap map = new HashMap();
+      for (int i = 0; i < methods.length; i++)
+      {
+         Method method = methods[i];
+         try
+         {
+            long hash = methodHash(method);
+            //Use Clebert's Persistent References so we don't get memory leaks
+            map.put(new Long(hash), new MethodPersistentReference(methods[i], PersistentReference.REFERENCE_SOFT));
+         }
+         catch (Exception e)
+         {
+         }
+      }
+      
+      return map;
+   }
 }

Modified: projects/aop/trunk/aop/src/test/org/jboss/test/aop/methodhashing/MethodHashingTestCase.java
===================================================================
--- projects/aop/trunk/aop/src/test/org/jboss/test/aop/methodhashing/MethodHashingTestCase.java	2007-03-27 16:02:50 UTC (rev 61751)
+++ projects/aop/trunk/aop/src/test/org/jboss/test/aop/methodhashing/MethodHashingTestCase.java	2007-03-27 16:03:50 UTC (rev 61752)
@@ -21,6 +21,7 @@
 */ 
 package org.jboss.test.aop.methodhashing;
 
+import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
 import java.util.Iterator;
 import java.util.Map;
@@ -55,6 +56,8 @@
       super(arg0);
    }
 
+   
+   
    public void testDeclaredMethodHashing() throws Exception
    {
       Class clazz = SubClass.class;
@@ -184,4 +187,59 @@
       assertEquals(methodInfo.getParameterTypes().length, method.getParameterTypes().length);
    }
 
+   /**
+    * Written as a sanity check when optimizing the MethodHashing class
+    */
+   public void testMethodHashingFunctionality() throws Exception
+   {
+      Method test1 = TestSubClass.class.getDeclaredMethod("test1", new Class[] {String.class}); 
+      Method test2 = TestSubClass.class.getDeclaredMethod("test2", new Class[] {String.class, Integer.TYPE}); 
+      long test1Hash = MethodHashing.calculateHash(test1);
+      long test2Hash = MethodHashing.calculateHash(test2);
+      assertEquals(-129615495583814694L, test1Hash);
+      assertEquals(-1228234556488761696l, test2Hash);
+      
+      assertEquals(test1Hash, MethodHashing.methodHash(test1));
+      assertEquals(test2Hash, MethodHashing.methodHash(test2));
+      
+      assertEquals(test1, MethodHashing.findMethodByHash(TestSubClass.class, test1Hash));
+      assertEquals(test2, MethodHashing.findMethodByHash(TestSubClass.class, test2Hash));
+      
+      //This method seems pointless and stupid, but better test it anyway for backwards compatibility
+      Map hashes = MethodHashing.getInterfaceHashes(TestSubClass.class);
+      assertEquals(2, hashes.size());
+      long h1 = ((Long)hashes.get(test1.toString())).longValue();
+      assertEquals(test1Hash, h1);
+      long h2 = ((Long)hashes.get(test2.toString())).longValue();
+      assertEquals(test2Hash, h2);
+      
+      Constructor ctor1 = TestSubClass.class.getDeclaredConstructor(new Class[0]);
+      Constructor ctor2 = TestSubClass.class.getDeclaredConstructor(new Class[] {Integer.TYPE});
+      long con1Hash = MethodHashing.constructorHash(ctor1);
+      long con2Hash = MethodHashing.constructorHash(ctor2);
+      assertEquals(-6451128523270287660L, con1Hash);
+      assertEquals(-4215863789501864959L, con2Hash);
+
+      assertEquals(ctor1, MethodHashing.findConstructorByHash(TestSubClass.class, con1Hash));
+      assertEquals(ctor2, MethodHashing.findConstructorByHash(TestSubClass.class, con2Hash));
+
+   }
+   
+   static class TestBaseClass
+   {
+      TestBaseClass(){}
+      TestBaseClass(int i){}
+      void test3() {}
+   }
+   
+   static class TestSubClass extends TestBaseClass
+   {
+      TestSubClass() {}
+      
+      TestSubClass(int i){super(i);}
+      
+      void test1(String s) {}
+      
+      int test2(String s, int i) {return i;}
+   }
 }




More information about the jboss-cvs-commits mailing list