You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bval.apache.org by mb...@apache.org on 2011/02/17 18:28:16 UTC

svn commit: r1071712 - in /incubator/bval/sandbox/lang3-work: bval-core/src/main/java/org/apache/bval/util/ bval-jsr303/src/main/java/org/apache/bval/jsr303/ bval-jsr303/src/main/java/org/apache/bval/jsr303/util/

Author: mbenson
Date: Thu Feb 17 17:28:16 2011
New Revision: 1071712

URL: http://svn.apache.org/viewvc?rev=1071712&view=rev
Log:
when processing classes with the Jsr303MetaBeanFactory, ensure that a new MetaProperty is only retained when some processing took place.  This prevents overlarge amounts of metadata being stored as a result of field-reading in particular; specifically the use of AOP weaving can add arbitrarily numerous numbers of fields to a class which realistically need no validation.  ValidationContextTraversal has been modified to handle validations requested specifically for non-constrained, but valid, properties.

Modified:
    incubator/bval/sandbox/lang3-work/bval-core/src/main/java/org/apache/bval/util/PropertyAccess.java
    incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303MetaBeanFactory.java
    incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ValidationContextTraversal.java

Modified: incubator/bval/sandbox/lang3-work/bval-core/src/main/java/org/apache/bval/util/PropertyAccess.java
URL: http://svn.apache.org/viewvc/incubator/bval/sandbox/lang3-work/bval-core/src/main/java/org/apache/bval/util/PropertyAccess.java?rev=1071712&r1=1071711&r2=1071712&view=diff
==============================================================================
--- incubator/bval/sandbox/lang3-work/bval-core/src/main/java/org/apache/bval/util/PropertyAccess.java (original)
+++ incubator/bval/sandbox/lang3-work/bval-core/src/main/java/org/apache/bval/util/PropertyAccess.java Thu Feb 17 17:28:16 2011
@@ -22,12 +22,13 @@ import java.beans.PropertyDescriptor;
 import java.lang.annotation.ElementType;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.lang.reflect.Type;
 import java.util.Map;
 
 /**
- * Description: Undefined dynamic strategy (FIELD or METHOD access) Uses
- * PropertyUtils or tries to determine field to access the value<br/>
+ * Description: Undefined dynamic strategy (FIELD or METHOD access) Uses PropertyUtils or tries to determine field to
+ * access the value<br/>
  */
 public class PropertyAccess extends AccessStrategy {
     private final Class<?> beanClass;
@@ -49,7 +50,7 @@ public class PropertyAccess extends Acce
      * {@inheritDoc}
      */
     public ElementType getElementType() {
-        return (rememberField != null) ? ElementType.FIELD : ElementType.METHOD;
+        return rememberField != null ? ElementType.FIELD : ElementType.METHOD;
     }
 
     private static Object getPublicProperty(Object bean, String property) throws InvocationTargetException,
@@ -87,32 +88,65 @@ public class PropertyAccess extends Acce
      * {@inheritDoc}
      */
     public Type getJavaType() {
-        /*
-         * if(Map.class.isAssignableFrom(beanClass)) { return beanClass. }
-         */
-        if (rememberField != null) { // use cached field of previous access
+        Type result = getTypeInner();
+        return result == null ? Object.class : result;
+    }
+
+    /**
+     * Learn whether this {@link PropertyAccess} references a known property.
+     * 
+     * @return boolean
+     */
+    public boolean isKnown() {
+        return getTypeInner() != null;
+    }
+
+    /**
+     * Find out what, if any, type can be calculated.
+     * 
+     * @return type found or <code>null</code>
+     */
+    private Type getTypeInner() {
+        if (rememberField != null) {
+            return rememberField.getGenericType();
+        }
+        Method readMethod = getPropertyReadMethod(propertyName, beanClass);
+        if (readMethod != null) {
+            return readMethod.getGenericReturnType();
+        }
+        Field fld = getField(propertyName, beanClass);
+        if (fld != null) {
+            cacheField(fld);
             return rememberField.getGenericType();
         }
+        return null;
+    }
+
+    private static Method getPropertyReadMethod(String propertyName, Class<?> beanClass) {
         for (PropertyDescriptor each : PropertyUtils.getPropertyDescriptors(beanClass)) {
-            if (each.getName().equals(propertyName) && each.getReadMethod() != null) {
-                return each.getReadMethod().getGenericReturnType();
+            if (each.getName().equals(propertyName)) {
+                return each.getReadMethod();
             }
         }
+        return null;
+    }
+
+    private static Field getField(String propertyName, Class<?> beanClass) {
         try { // try public field
-            return beanClass.getField(propertyName).getGenericType();
+            return beanClass.getField(propertyName);
         } catch (NoSuchFieldException ex2) {
             // search for private/protected field up the hierarchy
             Class<?> theClass = beanClass;
             while (theClass != null) {
                 try {
-                    return theClass.getDeclaredField(propertyName).getGenericType();
+                    return theClass.getDeclaredField(propertyName);
                 } catch (NoSuchFieldException ex3) {
                     // do nothing
                 }
                 theClass = theClass.getSuperclass();
             }
         }
-        return Object.class; // unknown type: allow any type??
+        return null;
     }
 
     /**
@@ -130,7 +164,6 @@ public class PropertyAccess extends Acce
             if (rememberField != null) { // cache field of previous access
                 return rememberField.get(bean);
             }
-
             try { // try public method
                 return getPublicProperty(bean, propertyName);
             } catch (NoSuchMethodException ex) {
@@ -144,31 +177,19 @@ public class PropertyAccess extends Acce
     }
 
     private Object getFieldValue(Object bean) throws IllegalAccessException {
-        Object value;
-        try { // try public field
-            Field aField = bean.getClass().getField(propertyName);
-            value = aField.get(bean);
-            rememberField = aField;
-            return value;
-        } catch (NoSuchFieldException ex2) {
-            // search for private/protected field up the hierarchy
-            Class<?> theClass = bean.getClass();
-            while (theClass != null) {
-                try {
-                    Field aField = theClass.getDeclaredField(propertyName);
-                    if (!aField.isAccessible()) {
-                        aField.setAccessible(true);
-                    }
-                    value = aField.get(bean);
-                    rememberField = aField;
-                    return value;
-                } catch (NoSuchFieldException ex3) {
-                    // do nothing
-                }
-                theClass = theClass.getSuperclass();
-            }
-            throw new IllegalArgumentException("cannot access field " + propertyName);
+        Field field = getField(propertyName, beanClass);
+        if (field != null) {
+            cacheField(field);
+            return rememberField.get(bean);
+        }
+        throw new IllegalArgumentException("cannot access field " + propertyName);
+    }
+
+    private void cacheField(Field field) {
+        if (!field.isAccessible()) {
+            field.setAccessible(true);
         }
+        this.rememberField = field;
     }
 
     /**

Modified: incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303MetaBeanFactory.java
URL: http://svn.apache.org/viewvc/incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303MetaBeanFactory.java?rev=1071712&r1=1071711&r2=1071712&view=diff
==============================================================================
--- incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303MetaBeanFactory.java (original)
+++ incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303MetaBeanFactory.java Thu Feb 17 17:28:16 2011
@@ -22,7 +22,6 @@ import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
-import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -46,9 +45,8 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * Description: process the class annotations for JSR303 constraint validations
- * to build the MetaBean with information from annotations and JSR303 constraint
- * mappings (defined in xml)<br/>
+ * Description: process the class annotations for JSR303 constraint validations to build the MetaBean with information
+ * from annotations and JSR303 constraint mappings (defined in xml)<br/>
  */
 public class Jsr303MetaBeanFactory implements MetaBeanFactory {
     /** Shared log instance */
@@ -74,8 +72,7 @@ public class Jsr303MetaBeanFactory imple
     }
 
     /**
-     * {@inheritDoc} Add the validation features to the metabean that come from
-     * JSR303 annotations in the beanClass.
+     * {@inheritDoc} Add the validation features to the metabean that come from JSR303 annotations in the beanClass.
      */
     public void buildMetaBean(MetaBean metabean) {
         try {
@@ -127,11 +124,15 @@ public class Jsr303MetaBeanFactory imple
             // create a property for those fields for which there is not yet a
             // MetaProperty
             if (!factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotations(field)) {
-                if (metaProperty == null) {
-                    metaProperty = addMetaProperty(metabean, field.getName(), field.getGenericType());
+                AccessStrategy access = new FieldAccess(field);
+                boolean create = metaProperty == null;
+                if (create) {
+                    metaProperty = addMetaProperty(metabean, access);
+                }
+                if (!annotationProcessor.processAnnotations(metaProperty, beanClass, field, access,
+                    new AppendValidationToMeta(metaProperty)) && create) {
+                    metabean.putProperty(metaProperty.getName(), null);
                 }
-                annotationProcessor.processAnnotations(metaProperty, beanClass, field, new FieldAccess(field),
-                    new AppendValidationToMeta(metaProperty));
             }
         }
         final Method[] methods = SecureActions.getDeclaredMethods(beanClass);
@@ -142,14 +143,18 @@ public class Jsr303MetaBeanFactory imple
             }
             if (propName != null) {
                 if (!factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotations(method)) {
+                    AccessStrategy access = new MethodAccess(propName, method);
                     MetaProperty metaProperty = metabean.getProperty(propName);
+                    boolean create = metaProperty == null;
                     // create a property for those methods for which there is
                     // not yet a MetaProperty
-                    if (metaProperty == null) {
-                        metaProperty = addMetaProperty(metabean, propName, method.getGenericReturnType());
+                    if (create) {
+                        metaProperty = addMetaProperty(metabean, access);
+                    }
+                    if (!annotationProcessor.processAnnotations(metaProperty, beanClass, method, access,
+                        new AppendValidationToMeta(metaProperty)) && create) {
+                        metabean.putProperty(propName, null);
                     }
-                    annotationProcessor.processAnnotations(metaProperty, beanClass, method, new MethodAccess(propName,
-                        method), new AppendValidationToMeta(metaProperty));
                 }
             } else if (hasValidationConstraintsDefined(method)) {
                 throw new ValidationException("Property " + method.getName() + " does not follow javabean conventions.");
@@ -160,8 +165,7 @@ public class Jsr303MetaBeanFactory imple
     }
 
     /**
-     * Learn whether a given Method has validation constraints defined via
-     * JSR303 annotations.
+     * Learn whether a given Method has validation constraints defined via JSR303 annotations.
      * 
      * @param method
      * @return <code>true</code> if constraints detected
@@ -208,25 +212,32 @@ public class Jsr303MetaBeanFactory imple
         InvocationTargetException {
         for (MetaConstraint<?, ? extends Annotation> meta : factoryContext.getFactory().getMetaConstraints(beanClass)) {
             MetaProperty metaProperty;
-            if (meta.getAccessStrategy() == null) { // class level
+            AccessStrategy access = meta.getAccessStrategy();
+            boolean create = false;
+            if (access == null) { // class level
                 metaProperty = null;
             } else { // property level
-                metaProperty = metabean.getProperty(meta.getAccessStrategy().getPropertyName());
-                if (metaProperty == null) {
-                    metaProperty =
-                        addMetaProperty(metabean, meta.getAccessStrategy().getPropertyName(), meta.getAccessStrategy()
-                            .getJavaType());
+                metaProperty = metabean.getProperty(access.getPropertyName());
+                create = metaProperty == null;
+                if (create) {
+                    metaProperty = addMetaProperty(metabean, access);
                 }
             }
-            annotationProcessor.processAnnotation(meta.getAnnotation(), metaProperty, beanClass, meta
-                .getAccessStrategy(), new AppendValidationToMeta(metaProperty == null ? metabean : metaProperty));
+            if (!annotationProcessor.processAnnotation(meta.getAnnotation(), metaProperty, beanClass,
+                meta.getAccessStrategy(), new AppendValidationToMeta(metaProperty == null ? metabean : metaProperty))
+                && create) {
+                metabean.putProperty(access.getPropertyName(), null);
+            }
         }
         for (AccessStrategy access : factoryContext.getFactory().getValidAccesses(beanClass)) {
             MetaProperty metaProperty = metabean.getProperty(access.getPropertyName());
-            if (metaProperty == null) {
-                metaProperty = addMetaProperty(metabean, access.getPropertyName(), access.getJavaType());
+            boolean create = metaProperty == null;
+            if (create) {
+                metaProperty = addMetaProperty(metabean, access);
+            }
+            if (!annotationProcessor.addAccessStrategy(metaProperty, access) && create) {
+                metabean.putProperty(access.getPropertyName(), null);
             }
-            annotationProcessor.addAccessStrategy(metaProperty, access);
         }
     }
 
@@ -268,13 +279,19 @@ public class Jsr303MetaBeanFactory imple
         log.debug("Default group sequence for bean {} is: {}", beanClass.getName(), groupSeq);
     }
 
-    private static MetaProperty addMetaProperty(MetaBean parentMetaBean, String propName, Type type) {
-        MetaProperty metaProperty;
-        metaProperty = new MetaProperty();
-        metaProperty.setName(propName);
-        metaProperty.setType(type);
-        parentMetaBean.putProperty(propName, metaProperty);
-        return metaProperty;
+    /**
+     * Add a {@link MetaProperty} to a {@link MetaBean}.
+     * @param parentMetaBean
+     * @param access
+     * @return the created {@link MetaProperty}
+     */
+    public static MetaProperty addMetaProperty(MetaBean parentMetaBean, AccessStrategy access) {
+        final MetaProperty result = new MetaProperty();
+        final String name = access.getPropertyName();
+        result.setName(name);
+        result.setType(access.getJavaType());
+        parentMetaBean.putProperty(name, result);
+        return result;
     }
 
 }

Modified: incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ValidationContextTraversal.java
URL: http://svn.apache.org/viewvc/incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ValidationContextTraversal.java?rev=1071712&r1=1071711&r2=1071712&view=diff
==============================================================================
--- incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ValidationContextTraversal.java (original)
+++ incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ValidationContextTraversal.java Thu Feb 17 17:28:16 2011
@@ -19,6 +19,7 @@ package org.apache.bval.jsr303.util;
 import java.lang.reflect.Type;
 
 import org.apache.bval.DynamicMetaBean;
+import org.apache.bval.jsr303.Jsr303MetaBeanFactory;
 import org.apache.bval.jsr303.UnknownPropertyException;
 import org.apache.bval.jsr303.util.PathNavigation.CallbackProcedure;
 import org.apache.bval.model.MetaBean;
@@ -122,7 +123,13 @@ public class ValidationContextTraversal 
         }
         MetaProperty mp = metaBean.getProperty(token);
         if (mp == null) {
-            throw new UnknownPropertyException("unknown property '" + token + "' in " + metaBean.getId());
+            PropertyAccess access = new PropertyAccess(rawType, token);
+            if (access.isKnown()) {
+                // add heretofore unknown, but valid, property on the fly:
+                mp = Jsr303MetaBeanFactory.addMetaProperty(metaBean, access);
+            } else {
+                throw new UnknownPropertyException("unknown property '" + token + "' in " + metaBean.getId());
+            }
         }
         validationContext.setMetaProperty(mp);
         setType(mp.getType());
@@ -169,7 +176,7 @@ public class ValidationContextTraversal 
     public Type getType() {
         return type;
     }
-    
+
     /**
      * @return the rawType
      */