You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by oh...@apache.org on 2013/12/04 21:35:01 UTC

svn commit: r1547902 - in /commons/proper/beanutils/trunk/src: main/java/org/apache/commons/beanutils/WrapDynaBean.java main/java/org/apache/commons/beanutils/WrapDynaClass.java test/java/org/apache/commons/beanutils/WrapDynaBeanTestCase.java

Author: oheger
Date: Wed Dec  4 20:35:01 2013
New Revision: 1547902

URL: http://svn.apache.org/r1547902
Log:
[BEANUTILS-455] WrapDynaClass now uses an associated PropertyUtilsBean instance for introspection.

Optionally, a PropertyUtilsBean instance can be passed to the createDynaClass()
method. If set, this instance is used to determine the wrapped bean's
property descriptors. The caching of WrapDynaClass instances had to be adapted
because now the associated PropertyUtilsBean object has to be taken into
account.

Modified:
    commons/proper/beanutils/trunk/src/main/java/org/apache/commons/beanutils/WrapDynaBean.java
    commons/proper/beanutils/trunk/src/main/java/org/apache/commons/beanutils/WrapDynaClass.java
    commons/proper/beanutils/trunk/src/test/java/org/apache/commons/beanutils/WrapDynaBeanTestCase.java

Modified: commons/proper/beanutils/trunk/src/main/java/org/apache/commons/beanutils/WrapDynaBean.java
URL: http://svn.apache.org/viewvc/commons/proper/beanutils/trunk/src/main/java/org/apache/commons/beanutils/WrapDynaBean.java?rev=1547902&r1=1547901&r2=1547902&view=diff
==============================================================================
--- commons/proper/beanutils/trunk/src/main/java/org/apache/commons/beanutils/WrapDynaBean.java (original)
+++ commons/proper/beanutils/trunk/src/main/java/org/apache/commons/beanutils/WrapDynaBean.java Wed Dec  4 20:35:01 2013
@@ -132,7 +132,7 @@ public class WrapDynaBean implements Dyn
 
         Object value = null;
         try {
-            value = PropertyUtils.getSimpleProperty(instance, name);
+            value = getPropertyUtils().getSimpleProperty(instance, name);
         } catch (InvocationTargetException ite) {
             Throwable cause = ite.getTargetException();
             throw new IllegalArgumentException
@@ -168,7 +168,7 @@ public class WrapDynaBean implements Dyn
 
         Object value = null;
         try {
-            value = PropertyUtils.getIndexedProperty(instance, name, index);
+            value = getPropertyUtils().getIndexedProperty(instance, name, index);
         } catch (IndexOutOfBoundsException e) {
             throw e;
         } catch (InvocationTargetException ite) {
@@ -203,7 +203,7 @@ public class WrapDynaBean implements Dyn
 
         Object value = null;
         try {
-            value = PropertyUtils.getMappedProperty(instance, name, key);
+            value = getPropertyUtils().getMappedProperty(instance, name, key);
         } catch (InvocationTargetException ite) {
             Throwable cause = ite.getTargetException();
             throw new IllegalArgumentException
@@ -271,7 +271,7 @@ public class WrapDynaBean implements Dyn
     public void set(String name, Object value) {
 
         try {
-            PropertyUtils.setSimpleProperty(instance, name, value);
+            getPropertyUtils().setSimpleProperty(instance, name, value);
         } catch (InvocationTargetException ite) {
             Throwable cause = ite.getTargetException();
             throw new IllegalArgumentException
@@ -305,7 +305,7 @@ public class WrapDynaBean implements Dyn
     public void set(String name, int index, Object value) {
 
         try {
-            PropertyUtils.setIndexedProperty(instance, name, index, value);
+            getPropertyUtils().setIndexedProperty(instance, name, index, value);
         } catch (IndexOutOfBoundsException e) {
             throw e;
         } catch (InvocationTargetException ite) {
@@ -339,7 +339,7 @@ public class WrapDynaBean implements Dyn
     public void set(String name, String key, Object value) {
 
         try {
-            PropertyUtils.setMappedProperty(instance, name, key, value);
+            getPropertyUtils().setMappedProperty(instance, name, key, value);
         } catch (InvocationTargetException ite) {
             Throwable cause = ite.getTargetException();
             throw new IllegalArgumentException
@@ -391,5 +391,19 @@ public class WrapDynaBean implements Dyn
 
     }
 
+    /**
+     * Returns the {@code PropertyUtilsBean} instance to be used for accessing properties.
+     * If available, this object is obtained from the associated {@code WrapDynaClass}.
+     *
+     * @return the associated {@code PropertyUtilsBean}
+     */
+    private PropertyUtilsBean getPropertyUtils() {
 
+        PropertyUtilsBean propUtils = null;
+        if (dynaClass != null) {
+            propUtils = dynaClass.getPropertyUtilsBean();
+        }
+        return (propUtils != null) ? propUtils : PropertyUtilsBean.getInstance();
+
+    }
 }

Modified: commons/proper/beanutils/trunk/src/main/java/org/apache/commons/beanutils/WrapDynaClass.java
URL: http://svn.apache.org/viewvc/commons/proper/beanutils/trunk/src/main/java/org/apache/commons/beanutils/WrapDynaClass.java?rev=1547902&r1=1547901&r2=1547902&view=diff
==============================================================================
--- commons/proper/beanutils/trunk/src/main/java/org/apache/commons/beanutils/WrapDynaClass.java (original)
+++ commons/proper/beanutils/trunk/src/main/java/org/apache/commons/beanutils/WrapDynaClass.java Wed Dec  4 20:35:01 2013
@@ -23,6 +23,7 @@ import java.lang.ref.Reference;
 import java.lang.ref.SoftReference;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
@@ -59,11 +60,13 @@ public class WrapDynaClass implements Dy
      * needed via calls to the <code>createDynaClass(Class)</code> method.
      *
      * @param beanClass JavaBean class to be introspected around
+     * @param propUtils the {@code PropertyUtilsBean} associated with this class
      */
-    private WrapDynaClass(Class<?> beanClass) {
+    private WrapDynaClass(Class<?> beanClass, PropertyUtilsBean propUtils) {
 
         this.beanClassRef = new SoftReference<Class<?>>(beanClass);
         this.beanClassName = beanClass.getName();
+        propertyUtilsBean = propUtils;
         introspect();
 
     }
@@ -81,6 +84,9 @@ public class WrapDynaClass implements Dy
      */
     private Reference<Class<?>> beanClassRef = null;
 
+    /** Stores the associated {@code PropertyUtilsBean} instance. */
+    private final PropertyUtilsBean propertyUtilsBean;
+
     /**
      * The JavaBean <code>Class</code> which is represented by this
      * <code>WrapDynaClass</code>.
@@ -122,18 +128,32 @@ public class WrapDynaClass implements Dy
     // ------------------------------------------------------- Static Variables
 
 
-    private static final ContextClassLoaderLocal<Map<Object, Object>> CLASSLOADER_CACHE =
-        new ContextClassLoaderLocal<Map<Object, Object>>() {
+    private static final ContextClassLoaderLocal<Map<CacheKey, WrapDynaClass>> CLASSLOADER_CACHE =
+        new ContextClassLoaderLocal<Map<CacheKey, WrapDynaClass>>() {
             @Override
-            protected Map<Object, Object> initialValue() {
-                return new WeakHashMap<Object, Object>();
+            protected Map<CacheKey, WrapDynaClass> initialValue() {
+                return new WeakHashMap<CacheKey, WrapDynaClass>();
         }
     };
 
     /**
-     * Get the wrap dyna classes cache
+     * Get the wrap dyna classes cache. Note: This method only exists to
+     * satisfy the deprecated {@code dynaClasses} hash map.
      */
+    @SuppressWarnings("unchecked")
     private static Map<Object, Object> getDynaClassesMap() {
+        @SuppressWarnings("rawtypes")
+        Map cache = CLASSLOADER_CACHE.get();
+        return cache;
+    }
+
+    /**
+     * Returns the cache for the already created class instances. For each
+     * combination of bean class and {@code PropertyUtilsBean} instance an
+     * entry is created in the cache.
+     * @return the cache for already created {@code WrapDynaClass} instances
+     */
+    private static Map<CacheKey, WrapDynaClass> getClassesCache() {
         return CLASSLOADER_CACHE.get();
     }
 
@@ -209,15 +229,24 @@ public class WrapDynaClass implements Dy
         }
         @Override
         public Set<Object> keySet() {
-            return getDynaClassesMap().keySet();
+            // extract the classes from the key to stay backwards compatible
+            Set<Object> result = new HashSet<Object>();
+            for (CacheKey k : getClassesCache().keySet()) {
+                result.add(k.beanClass);
+            }
+            return result;
         }
         @Override
         public Object put(Object key, Object value) {
-            return getDynaClassesMap().put(key, value);
+            return getClassesCache().put(
+                    new CacheKey((Class<?>) key, PropertyUtilsBean.getInstance()),
+                    (WrapDynaClass) value);
         }
         @Override
         public void putAll(Map<? extends Object, ? extends Object> m) {
-            getDynaClassesMap().putAll(m);
+            for (Map.Entry<? extends Object, ? extends Object> e : m.entrySet()) {
+                put(e.getKey(), e.getValue());
+            }
         }
         @Override
         public Object remove(Object key) {
@@ -358,7 +387,7 @@ public class WrapDynaClass implements Dy
      */
     public static void clear() {
 
-        getDynaClassesMap().clear();
+        getClassesCache().clear();
 
     }
 
@@ -372,19 +401,49 @@ public class WrapDynaClass implements Dy
      */
     public static WrapDynaClass createDynaClass(Class<?> beanClass) {
 
-            WrapDynaClass dynaClass =
-                    (WrapDynaClass) getDynaClassesMap().get(beanClass);
-            if (dynaClass == null) {
-                dynaClass = new WrapDynaClass(beanClass);
-                getDynaClassesMap().put(beanClass, dynaClass);
-            }
-            return (dynaClass);
+        return createDynaClass(beanClass, null);
+
+    }
+
+
+    /**
+     * Create (if necessary) and return a new {@code WrapDynaClass} instance
+     * for the specified bean class using the given {@code PropertyUtilsBean}
+     * instance for introspection. Using this method a specially configured
+     * {@code PropertyUtilsBean} instance can be hooked into the introspection
+     * mechanism of the managed bean. The argument is optional; if no
+     * {@code PropertyUtilsBean} object is provided, the default instance is used.
+     * @param beanClass Bean class for which a WrapDynaClass is requested
+     * @param pu the optional {@code PropertyUtilsBean} to be used for introspection
+     * @return A new <i>Wrap</i> {@link DynaClass}
+     * @since 1.9
+     */
+    public static WrapDynaClass createDynaClass(Class<?> beanClass, PropertyUtilsBean pu) {
+
+        PropertyUtilsBean propUtils = (pu != null) ? pu : PropertyUtilsBean.getInstance();
+        CacheKey key = new CacheKey(beanClass, propUtils);
+        WrapDynaClass dynaClass = getClassesCache().get(key);
+        if (dynaClass == null) {
+            dynaClass = new WrapDynaClass(beanClass, propUtils);
+            getClassesCache().put(key, dynaClass);
+        }
+        return (dynaClass);
 
     }
 
 
     // ------------------------------------------------------ Protected Methods
 
+    /**
+     * Returns the {@code PropertyUtilsBean} instance associated with this class. This
+     * bean is used for introspection.
+     *
+     * @return the associated {@code PropertyUtilsBean} instance
+     * @since 1.9
+     */
+    protected PropertyUtilsBean getPropertyUtilsBean() {
+        return propertyUtilsBean;
+    }
 
     /**
      * Introspect our bean class to identify the supported properties.
@@ -394,7 +453,7 @@ public class WrapDynaClass implements Dy
         // Look up the property descriptors for this bean class
         Class<?> beanClass = getBeanClass();
         PropertyDescriptor[] regulars =
-                PropertyUtils.getPropertyDescriptors(beanClass);
+                getPropertyUtilsBean().getPropertyDescriptors(beanClass);
         if (regulars == null) {
             regulars = new PropertyDescriptor[0];
         }
@@ -431,5 +490,49 @@ public class WrapDynaClass implements Dy
 
     }
 
+    /**
+     * A class representing the combined key for the cache of {@code WrapDynaClass}
+     * instances. A single key consists of a bean class and an instance of
+     * {@code PropertyUtilsBean}. Instances are immutable.
+     */
+    private static class CacheKey {
+        /** The bean class. */
+        private final Class<?> beanClass;
+
+        /** The instance of PropertyUtilsBean. */
+        private final PropertyUtilsBean propUtils;
+
+        /**
+         * Creates a new instance of {@code CacheKey}.
+         *
+         * @param beanCls the bean class
+         * @param pu the instance of {@code PropertyUtilsBean}
+         */
+        public CacheKey(Class<?> beanCls, PropertyUtilsBean pu) {
+            beanClass = beanCls;
+            propUtils = pu;
+        }
 
+        @Override
+        public int hashCode() {
+            int factor = 31;
+            int result = 17;
+            result = factor * beanClass.hashCode() + result;
+            result = factor * propUtils.hashCode() + result;
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof CacheKey)) {
+                return false;
+            }
+
+            CacheKey c = (CacheKey) obj;
+            return beanClass.equals(c.beanClass) && propUtils.equals(c.propUtils);
+        }
+    }
 }

Modified: commons/proper/beanutils/trunk/src/test/java/org/apache/commons/beanutils/WrapDynaBeanTestCase.java
URL: http://svn.apache.org/viewvc/commons/proper/beanutils/trunk/src/test/java/org/apache/commons/beanutils/WrapDynaBeanTestCase.java?rev=1547902&r1=1547901&r2=1547902&view=diff
==============================================================================
--- commons/proper/beanutils/trunk/src/test/java/org/apache/commons/beanutils/WrapDynaBeanTestCase.java (original)
+++ commons/proper/beanutils/trunk/src/test/java/org/apache/commons/beanutils/WrapDynaBeanTestCase.java Wed Dec  4 20:35:01 2013
@@ -330,4 +330,39 @@ public class WrapDynaBeanTestCase extend
         assertSame("Wrong DynaClass", clazz, bean.getDynaClass());
         checkSimplePropertyAccess();
     }
+
+    /**
+     * Tests whether caching works for WrapDynaClass objects.
+     */
+    public void testGetWrapDynaClassFromCache() {
+        WrapDynaClass clazz = WrapDynaClass.createDynaClass(TestBean.class);
+        assertSame("Instance not cached", clazz,
+                WrapDynaClass.createDynaClass(TestBean.class));
+    }
+
+    /**
+     * Tests whether the PropertyUtilsBean instance associated with a WrapDynaClass is
+     * taken into account when accessing an instance from the cache.
+     */
+    public void testGetWrapDynaClassFromCacheWithPropUtils() {
+        WrapDynaClass clazz = WrapDynaClass.createDynaClass(TestBean.class);
+        PropertyUtilsBean pu = new PropertyUtilsBean();
+        WrapDynaClass clazz2 = WrapDynaClass.createDynaClass(TestBean.class, pu);
+        assertNotSame("Got same instance from cache", clazz, clazz2);
+    }
+
+    /**
+     * Tests whether a custom PropertyUtilsBean instance can be used for introspection of
+     * bean properties.
+     */
+    public void testIntrospectionWithCustomPropUtils() {
+        PropertyUtilsBean pu = new PropertyUtilsBean();
+        pu.addBeanIntrospector(new FluentPropertyBeanIntrospector());
+        WrapDynaClass dynaClass = WrapDynaClass.createDynaClass(
+                FluentIntrospectionTestBean.class, pu);
+        FluentIntrospectionTestBean obj = new FluentIntrospectionTestBean();
+        bean = new WrapDynaBean(obj, dynaClass);
+        bean.set("fluentProperty", "testvalue");
+        assertEquals("Property not set", "testvalue", obj.getStringProperty());
+    }
 }