You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by sk...@apache.org on 2008/06/12 15:42:36 UTC

svn commit: r667085 - in /myfaces/orchestra/trunk/sandbox: ./ src/main/java/org/apache/myfaces/orchestra/dynaForm/metadata/impl/ejb/

Author: skitching
Date: Thu Jun 12 06:42:36 2008
New Revision: 667085

URL: http://svn.apache.org/viewvc?rev=667085&view=rev
Log:
Use ASM to perform class introspection in preference to BCEL (faster, smaller).

Added:
    myfaces/orchestra/trunk/sandbox/src/main/java/org/apache/myfaces/orchestra/dynaForm/metadata/impl/ejb/AsmHelper.java   (with props)
    myfaces/orchestra/trunk/sandbox/src/main/java/org/apache/myfaces/orchestra/dynaForm/metadata/impl/ejb/EmptyClassVisitor.java   (with props)
Modified:
    myfaces/orchestra/trunk/sandbox/pom.xml
    myfaces/orchestra/trunk/sandbox/src/main/java/org/apache/myfaces/orchestra/dynaForm/metadata/impl/ejb/ClassHelper.java
    myfaces/orchestra/trunk/sandbox/src/main/java/org/apache/myfaces/orchestra/dynaForm/metadata/impl/ejb/ClassHelperFactory.java

Modified: myfaces/orchestra/trunk/sandbox/pom.xml
URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/sandbox/pom.xml?rev=667085&r1=667084&r2=667085&view=diff
==============================================================================
--- myfaces/orchestra/trunk/sandbox/pom.xml (original)
+++ myfaces/orchestra/trunk/sandbox/pom.xml Thu Jun 12 06:42:36 2008
@@ -62,10 +62,30 @@
     </dependency>
 
     <dependency>
+      <!-- 
+      	- WARNING: asm versions are horribly non-compatible between major releases.
+      	- We might need to use maven-shade-plugin to instead import a private version
+      	- of asm into this project, so that orchestra can be used within projects
+      	- that use other versions of asm.
+      	-->
+      <groupId>asm</groupId>
+      <artifactId>asm</artifactId>
+      <version>2.2.3</version>
+      <scope>compile</scope>
+    </dependency>
+
+    <dependency>
+      <!--
+        - Needed only for BcelHelper class. The AsmHelper class is selected
+        - by default, and better. So this dependency is only needed if you
+        - want to exclude the ASM library for some reason, but still want
+        - the ClassHelper functionality.
+        -->
       <groupId>bcel</groupId>
       <artifactId>bcel</artifactId>
       <version>5.1</version>
       <scope>compile</scope>
+      <optional>true</optional>
     </dependency>
 
     <dependency>

Added: myfaces/orchestra/trunk/sandbox/src/main/java/org/apache/myfaces/orchestra/dynaForm/metadata/impl/ejb/AsmHelper.java
URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/sandbox/src/main/java/org/apache/myfaces/orchestra/dynaForm/metadata/impl/ejb/AsmHelper.java?rev=667085&view=auto
==============================================================================
--- myfaces/orchestra/trunk/sandbox/src/main/java/org/apache/myfaces/orchestra/dynaForm/metadata/impl/ejb/AsmHelper.java (added)
+++ myfaces/orchestra/trunk/sandbox/src/main/java/org/apache/myfaces/orchestra/dynaForm/metadata/impl/ejb/AsmHelper.java Thu Jun 12 06:42:36 2008
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.orchestra.dynaForm.metadata.impl.ejb;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.myfaces.orchestra.lib.OrchestraException;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * Reimplement the reflection facilities provided by Class.class, but preserving the order in which
+ * the properties exist within the .class file.
+ * <p>
+ * Presumably the field order in the .class file is the same as the order they are declared within
+ * the source file. And presumably the code author arranged those in some logical order.
+ * <p>
+ * This class uses the ASM library to inspect the class data.
+ */
+public class AsmHelper implements ClassHelper
+{
+    private final Log log = LogFactory.getLog(AsmHelper.class);
+    private static final Field[] FIELDS_TYPE = new Field[0];
+    private static final Method[] METHODS_TYPE = new Method[0];
+
+    private void applyVisitorToClass(Class clazz, ClassVisitor visitor)
+    {
+        // Note: Simply using the constructor ClassReader(clazz.getName())
+        // does not allow control over which classloader the class is loaded
+        // from.
+        String className = clazz.getName().replace('.', '/') + ".class";
+        ClassReader cr = null;
+        InputStream is = null; 
+        try
+        {
+            is = this.getClass().getClassLoader().getResourceAsStream(className);
+            cr = new ClassReader(is);
+            cr.accept(visitor, true);
+        }
+        catch (IOException e)
+        {
+            throw new OrchestraException("Unable to read class " + className, e);
+        }
+        finally
+        {
+            if (is != null)
+            {
+                try
+                {
+                    is.close();
+                }
+                catch(IOException e2)
+                {
+                    // Not likely to occur, and nothing can be done about it.
+                    log.error("Unable to close input stream", e2);
+                }
+            }
+        }
+    }
+
+    /**
+     * Return a list of all the static and non-static fields of the specified class, regardless of
+     * their accessability.
+     * <p>
+     * The array order matches the order in which the fields were declared within the original
+     * source file.
+     */
+    public Field[] getFields(final Class<?> clazz)
+    {
+        final List<Field> fields = new ArrayList<Field>(50);
+        ClassVisitor fv = new EmptyClassVisitor()
+        {
+            @Override
+            public FieldVisitor visitField(int access, String name, String desc, String signature,
+                    Object value)
+            {
+                try
+                {
+                    // Note: no two fields can have the same name, so we don't need to check
+                    // whether the found field is already in the fields list.
+                    Field f = clazz.getDeclaredField(name);
+                    fields.add(f);
+                }
+                catch (NoSuchFieldException e)
+                {
+                    log.warn("Cannot find Field object for " + name);
+                }
+                return super.visitField(access, name, desc, signature, value);
+            }
+        };
+        applyVisitorToClass(clazz, fv);
+        return fields.toArray(FIELDS_TYPE);
+    }
+
+    /**
+     * Given an ASM method descriptor, find the associated java.lang.reflect.Method object.
+     * <p>
+     * Returns null if no such method is found.
+     */
+    private Method getMethod(Class<?> clazz, String methodName, String descriptor)
+    {
+        if (!methodName.startsWith("set") && !methodName.startsWith("get")
+                && !methodName.startsWith("is"))
+        {
+            return null;
+        }
+
+        // TODO: this looping through all methods and calling getMethodDescriptor
+        // on each one is not very efficient. It might be nice to build all the
+        // descriptor->Method mappings once and cache them in a map. The question
+        // then is how long to hold onto that cached data though. And where to
+        // store it, as keeping the cache in a parent classloader will prevent unloading
+        // of any of the classes that those Method objects are on, unless weak
+        // references are used. For now, keep things simple..
+        Method[] methods = clazz.getDeclaredMethods();
+        for(Method m: methods)
+        {
+            String thisMethodDesc = org.objectweb.asm.Type.getMethodDescriptor(m);
+            if (thisMethodDesc.equals(descriptor))
+            {
+                return m;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Return a list of all the get*, set* and is* method on the specified class, regardless of
+     * their accessability.
+     * <p>
+     * The array order matches the order in which the methods were declared within the original
+     * source file.
+     */
+    public Method[] getMethods(final Class<?> clazz)
+    {
+        final List<Method> methods = new ArrayList<Method>(50);
+        ClassVisitor fv = new EmptyClassVisitor()
+        {
+            @Override
+            public MethodVisitor visitMethod(int access, String name, String descriptor,
+                    String signature, String[] exceptions)
+            {
+                // Note that "descriptor" is like signature, except without any info about
+                // java15 generic type params. When a visited method has no generic types
+                // in its prototype, then param signature=null.
+                // 
+                // As there can never be two methods with the same "descriptor", we just
+                // use that to locate the real Method object.
+                Method m = getMethod(clazz, name, descriptor);
+                if (m != null)
+                {
+                    if (!methods.contains(m))
+                    {
+                        methods.add(m);
+                    }
+                }
+                return super.visitMethod(access, name, descriptor, signature, exceptions);
+            }
+
+        };
+        applyVisitorToClass(clazz, fv);
+        return methods.toArray(METHODS_TYPE);
+    }
+}

Propchange: myfaces/orchestra/trunk/sandbox/src/main/java/org/apache/myfaces/orchestra/dynaForm/metadata/impl/ejb/AsmHelper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: myfaces/orchestra/trunk/sandbox/src/main/java/org/apache/myfaces/orchestra/dynaForm/metadata/impl/ejb/ClassHelper.java
URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/sandbox/src/main/java/org/apache/myfaces/orchestra/dynaForm/metadata/impl/ejb/ClassHelper.java?rev=667085&r1=667084&r2=667085&view=diff
==============================================================================
--- myfaces/orchestra/trunk/sandbox/src/main/java/org/apache/myfaces/orchestra/dynaForm/metadata/impl/ejb/ClassHelper.java (original)
+++ myfaces/orchestra/trunk/sandbox/src/main/java/org/apache/myfaces/orchestra/dynaForm/metadata/impl/ejb/ClassHelper.java Thu Jun 12 06:42:36 2008
@@ -22,11 +22,27 @@
 import java.lang.reflect.Method;
 
 /**
- * Interface for helpers to retrieve class meta information
+ * Interface for helpers to retrieve class meta information.
+ * <p>
+ * Implementations must be threadsafe.
  */
 public interface ClassHelper
 {
+    /**
+     * Return a list of all the static and non-static fields of the specified
+     * class, regardless of their accessibility.
+     * <p>
+     * The array order matches the order in which the fields were declared within
+     * the original source file.
+     */
 	public Field[] getFields(Class<?> clazz);
 
+    /**
+     * Return a list of all the get*, set* and is* method on the specified class,
+     * regardless of their accessibility.
+     * <p>
+     * The array order matches the order in which the methods were declared within
+     * the original source file.
+     */
 	public Method[] getMethods(Class<?> clazz);
 }

Modified: myfaces/orchestra/trunk/sandbox/src/main/java/org/apache/myfaces/orchestra/dynaForm/metadata/impl/ejb/ClassHelperFactory.java
URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/sandbox/src/main/java/org/apache/myfaces/orchestra/dynaForm/metadata/impl/ejb/ClassHelperFactory.java?rev=667085&r1=667084&r2=667085&view=diff
==============================================================================
--- myfaces/orchestra/trunk/sandbox/src/main/java/org/apache/myfaces/orchestra/dynaForm/metadata/impl/ejb/ClassHelperFactory.java (original)
+++ myfaces/orchestra/trunk/sandbox/src/main/java/org/apache/myfaces/orchestra/dynaForm/metadata/impl/ejb/ClassHelperFactory.java Thu Jun 12 06:42:36 2008
@@ -20,35 +20,45 @@
 
 public class ClassHelperFactory
 {
-	private static ClassHelper classHelper = null;
+	private static final String[] helperClasses = new String[] {
+	    "org.apache.myfaces.orchestra.dynaForm.metadata.impl.ejb.AsmHelper",
+        "org.apache.myfaces.orchestra.dynaForm.metadata.impl.ejb.BcelHelper"
+	};
 
+    private static final ClassHelper classHelper = newClassHelper();
+    
 	private ClassHelperFactory()
 	{
 	}
 
 	public static ClassHelper get()
 	{
-		if (classHelper != null)
-		{
-			return classHelper;
-		}
-
-		classHelper = create();
-
-		return classHelper;
+	    return classHelper;
 	}
 
-	private static ClassHelper create()
+	public static ClassHelper newClassHelper()
 	{
-		try
-		{
-			Class.forName("org.apache.bcel.classfile.JavaClass");
-			return new BcelHelper();
-		}
-		catch (ClassNotFoundException e)
-		{
-			// bcel not there
-		}
+	    ClassLoader cl = ClassHelperFactory.class.getClassLoader();
+	    for(String helperClassName: helperClasses)
+	    {
+    		try
+    		{
+    			Class<ClassHelper> c = (Class<ClassHelper>) Class.forName(helperClassName, true, cl);
+    			return c.newInstance();
+    		}
+    		catch (ClassNotFoundException e)
+    		{
+    			// not there
+    		}
+    		catch(IllegalAccessException e)
+    		{
+    		    // not available
+    		}
+    		catch(InstantiationException e)
+    		{
+                // not available
+    		}
+	    }
 
 		// last exit
 		return new JavaHelper();

Added: myfaces/orchestra/trunk/sandbox/src/main/java/org/apache/myfaces/orchestra/dynaForm/metadata/impl/ejb/EmptyClassVisitor.java
URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/sandbox/src/main/java/org/apache/myfaces/orchestra/dynaForm/metadata/impl/ejb/EmptyClassVisitor.java?rev=667085&view=auto
==============================================================================
--- myfaces/orchestra/trunk/sandbox/src/main/java/org/apache/myfaces/orchestra/dynaForm/metadata/impl/ejb/EmptyClassVisitor.java (added)
+++ myfaces/orchestra/trunk/sandbox/src/main/java/org/apache/myfaces/orchestra/dynaForm/metadata/impl/ejb/EmptyClassVisitor.java Thu Jun 12 06:42:36 2008
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.orchestra.dynaForm.metadata.impl.ejb;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * An empty implementation of the ASM ClassVisitor interface.
+ */
+public class EmptyClassVisitor implements ClassVisitor
+{
+    public void visitOuterClass(final String owner, final String name, final String desc)
+    {
+    }
+
+    public void visit(final int version, final int access, final String name,
+            final String signature, final String superName, final String[] interfaces)
+    {
+    }
+
+    public void visitAttribute(final Attribute attr)
+    {
+    }
+
+    public void visitEnd()
+    {
+    }
+
+    public void visitInnerClass(final String name, final String outerName, final String innerName,
+            final int access)
+    {
+    }
+
+    public FieldVisitor visitField(final int access, final String name, final String desc,
+            final String signature, final Object value)
+    {
+        return null;
+    }
+
+    public void visitSource(final String source, final String debug)
+    {
+    }
+
+    public AnnotationVisitor visitAnnotation(final String desc, final boolean visible)
+    {
+        return null;
+    }
+
+    public MethodVisitor visitMethod(final int access, final String name, final String desc,
+            final String signature, final String[] exceptions)
+    {
+        return null;
+    }
+}

Propchange: myfaces/orchestra/trunk/sandbox/src/main/java/org/apache/myfaces/orchestra/dynaForm/metadata/impl/ejb/EmptyClassVisitor.java
------------------------------------------------------------------------------
    svn:eol-style = native