You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@uima.apache.org by sc...@apache.org on 2014/03/18 21:36:13 UTC

svn commit: r1579031 - in /uima/uimaj/trunk/uimaj-core/src: main/java/org/apache/uima/analysis_engine/impl/ main/java/org/apache/uima/resource/metadata/ main/java/org/apache/uima/resource/metadata/impl/ main/java/org/apache/uima/util/ test/java/org/apa...

Author: schor
Date: Tue Mar 18 20:36:12 2014
New Revision: 1579031

URL: http://svn.apache.org/r1579031
Log:
[UIMA-3684] improve caching of bean info for xml parsing

Added:
    uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/util/ConcurrentHashMapWithProducer.java
Modified:
    uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/AnalysisEngineDescription_impl.java
    uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/metadata/MetaDataObject.java
    uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/metadata/impl/ConfigurationParameterSettings_impl.java
    uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/metadata/impl/MetaDataObject_impl.java
    uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/resource/metadata/impl/MetaDataObject_implTest.java
    uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/resource/metadata/impl/TestFruitBagObject.java
    uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/resource/metadata/impl/TestFruitObject.java

Modified: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/AnalysisEngineDescription_impl.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/AnalysisEngineDescription_impl.java?rev=1579031&r1=1579030&r2=1579031&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/AnalysisEngineDescription_impl.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/AnalysisEngineDescription_impl.java Tue Mar 18 20:36:12 2014
@@ -21,6 +21,7 @@ package org.apache.uima.analysis_engine.
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.Writer;
+import java.lang.reflect.Method;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -36,6 +37,7 @@ import java.util.TreeMap;
 
 import org.apache.uima.Constants;
 import org.apache.uima.UIMAFramework;
+import org.apache.uima.UIMARuntimeException;
 import org.apache.uima.analysis_engine.AnalysisEngine;
 import org.apache.uima.analysis_engine.AnalysisEngineDescription;
 import org.apache.uima.analysis_engine.metadata.AnalysisEngineMetaData;
@@ -82,21 +84,29 @@ import org.xml.sax.SAXException;
 public class AnalysisEngineDescription_impl extends ResourceCreationSpecifier_impl implements
         AnalysisEngineDescription {
 
+  static final private Method getterForAEwImports;
+  static { 
+    try {
+      getterForAEwImports = AnalysisEngineDescription_impl.class.getDeclaredMethod("getDelegateAnalysisEngineSpecifiersWithImports");
+    } catch (Exception e) {
+      throw new UIMARuntimeException(e);
+    }
+  }
   /**
    * Name of the "delegateAnalysisEngineSpecifiers" property. Change this if interface changes.
    */
-  protected String PROP_DELEGATE_ANALYSIS_ENGINE_SPECIFIERS = "delegateAnalysisEngineSpecifiers";
-
+  final protected String PROP_DELEGATE_ANALYSIS_ENGINE_SPECIFIERS = "delegateAnalysisEngineSpecifiers";
+  
   /**
    * Name of the "delegateAnalysisEngineSpecifiersWithImports" property. Change this if interface
    * changes.
    */
-  protected String PROP_DELEGATE_ANALYSIS_ENGINE_SPECIFIERS_WITH_IMPORTS = "delegateAnalysisEngineSpecifiersWithImports";
+  final protected String PROP_DELEGATE_ANALYSIS_ENGINE_SPECIFIERS_WITH_IMPORTS = "delegateAnalysisEngineSpecifiersWithImports";
 
   /**
    * Name of the "delegateAnalysisEngineSpecifiers" XML Element. Change this if schema changes.
    */
-  protected String ELEM_DELEGATE_ANALYSIS_ENGINE_SPECIFIERS = "delegateAnalysisEngineSpecifiers";
+  final protected String ELEM_DELEGATE_ANALYSIS_ENGINE_SPECIFIERS = "delegateAnalysisEngineSpecifiers";
 
   private String mFrameworkImplementation;
 
@@ -630,6 +640,16 @@ public class AnalysisEngineDescription_i
     }
     return false;
   }
+  
+  @Override
+  public List<MetaDataAttr> getAdditionalAttributes() {
+    return Collections.singletonList(
+        new MetaDataAttr(
+            PROP_DELEGATE_ANALYSIS_ENGINE_SPECIFIERS_WITH_IMPORTS,
+            getterForAEwImports,
+            null,  // no writer
+            Map.class));
+  }
 
   /**
    * Overridden to add Delegate AE Specifiers to the result list. Default introspection
@@ -637,7 +657,9 @@ public class AnalysisEngineDescription_i
    * import/export methods, though, so that set methods are not required.
    * 
    * @see MetaDataObject#listAttributes()
+   * @deprecated never called anymore - getAdditionalAttributes called instead
    */
+  @Deprecated
   public List<NameClassPair> listAttributes() {
     List<NameClassPair> result = super.listAttributes();
     result.add(new NameClassPair(PROP_DELEGATE_ANALYSIS_ENGINE_SPECIFIERS_WITH_IMPORTS, Map.class

Modified: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/metadata/MetaDataObject.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/metadata/MetaDataObject.java?rev=1579031&r1=1579030&r2=1579031&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/metadata/MetaDataObject.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/metadata/MetaDataObject.java Tue Mar 18 20:36:12 2014
@@ -52,7 +52,9 @@ public interface MetaDataObject extends 
    *         contains the name of a parameter and the Class of its value. For primitive types, the
    *         wrapper classes will be returned (e.g. <code>java.lang.Integer</code> instead of
    *         int).
+   * @deprecated - use getAttributes() instead, don't override it, use getAdditionalAttributes to specify additional ones
    */
+  @Deprecated
   public List<NameClassPair> listAttributes();
 
   /**

Modified: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/metadata/impl/ConfigurationParameterSettings_impl.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/metadata/impl/ConfigurationParameterSettings_impl.java?rev=1579031&r1=1579030&r2=1579031&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/metadata/impl/ConfigurationParameterSettings_impl.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/metadata/impl/ConfigurationParameterSettings_impl.java Tue Mar 18 20:36:12 2014
@@ -19,11 +19,14 @@
 
 package org.apache.uima.resource.metadata.impl;
 
+import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.uima.UIMARuntimeException;
 import org.apache.uima.UIMA_IllegalArgumentException;
 import org.apache.uima.resource.metadata.ConfigurationParameterSettings;
 import org.apache.uima.resource.metadata.MetaDataObject;
@@ -48,6 +51,14 @@ public class ConfigurationParameterSetti
 
   static final long serialVersionUID = 3476535733588304983L;
 
+  private static final Method methodGetSettingsForGroups;
+  static {
+    try {
+      methodGetSettingsForGroups = ConfigurationParameterSettings.class.getDeclaredMethod("getSettingsForGroups");
+    } catch (Exception e) {
+      throw new UIMARuntimeException(e);
+    }
+  }
   /**
    * Settings for parameters that are not in any group.
    */
@@ -216,13 +227,25 @@ public class ConfigurationParameterSetti
     return XMLIZATION_INFO;
   }
 
+  @Override
+  public List<MetaDataAttr> getAdditionalAttributes() {
+    return Collections.singletonList(
+        new MetaDataAttr(
+            "settingsForGroups",
+            methodGetSettingsForGroups,
+            null,  // no writer
+            Map.class));
+  }
+  
   /**
    * Overridden to add the settingsForGroups property to the result list. Default introspection
    * implementation won't return it because it has no set method. We've also overridden the XML
    * import/export methods, though, so that set methods are not required.
    * 
    * @see MetaDataObject#listAttributes()
+   * @deprecated - use getAdditionalAttributes instead
    */
+  @Deprecated
   public List<NameClassPair> listAttributes() {
     List<NameClassPair> result = super.listAttributes();
     result.add(new NameClassPair("settingsForGroups", Map.class.getName()));

Modified: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/metadata/impl/MetaDataObject_impl.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/metadata/impl/MetaDataObject_impl.java?rev=1579031&r1=1579030&r2=1579031&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/metadata/impl/MetaDataObject_impl.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/metadata/impl/MetaDataObject_impl.java Tue Mar 18 20:36:12 2014
@@ -33,18 +33,18 @@ import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
 
 import org.apache.uima.UIMARuntimeException;
 import org.apache.uima.UIMA_IllegalArgumentException;
 import org.apache.uima.UIMA_UnsupportedOperationException;
 import org.apache.uima.internal.util.XMLUtils;
 import org.apache.uima.resource.metadata.MetaDataObject;
+import org.apache.uima.util.ConcurrentHashMapWithProducer;
 import org.apache.uima.util.InvalidXMLException;
 import org.apache.uima.util.NameClassPair;
 import org.apache.uima.util.XMLParser;
@@ -79,10 +79,9 @@ import org.xml.sax.helpers.AttributesImp
  * Therefore subclasses of this class must be valid JavaBeans and either use the standard naming
  * conventions for getters and setters. BeanInfo augmentation is ignored; the implementation here
  * uses the flag IGNORE_ALL_BEANINFO. See <a href="http://java.sun.com/docs/books/tutorial/javabeans/"> 
- * The Java Beans Tutorial</a> for more
- * information.
+ * The Java Beans Tutorial</a> for more information.
  * 
- * To support XML Comments, which can occur inbetween any sub-elements, including array values,
+ * To support XML Comments, which can occur between any sub-elements, including array values,
  * the "data" for all objects is stored in a pair of ArrayLists; one holds the "name" of the slot,
  * the other the value; comments are interspersed within this list where they occur.
  * 
@@ -102,11 +101,69 @@ public abstract class MetaDataObject_imp
 
   private static final Attributes EMPTY_ATTRIBUTES = new AttributesImpl();
 
-  // Class level cache (static) for introspection - 30x speedup in CDE for large descriptor
-  private static transient ConcurrentMap<Class<? extends MetaDataObject_impl>, PropertyDescriptor[]> mPropertyDescriptorsMap =
-      new ConcurrentHashMap<Class<? extends MetaDataObject_impl>, PropertyDescriptor[]>();
-//    Collections.synchronizedMap(new IdentityHashMap<Class<? extends MetaDataObject_impl>, PropertyDescriptor[]>());  
+  /*
+   * Cache for Java Bean lookup
+   * 
+   *   Key: the Class object of the MetaDataObject, corresponds to some XML element
+   *   Value: a list of objects, one for each "attribute", holding:
+   *     the attribute name
+   *     the reader (a Method)
+   *     the writer (a Method)
+   *     the java Class of the data type of this attribute <converted to a wrapper class for primitives>
+   */
+  
+  public static class MetaDataAttr {
+    @Override
+    public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((clazz == null) ? 0 : clazz.hashCode());
+      result = prime * result + ((name == null) ? 0 : name.hashCode());
+      return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj)
+        return true;
+      if (obj == null)
+        return false;
+      if (getClass() != obj.getClass())
+        return false;
+      MetaDataAttr other = (MetaDataAttr) obj;
+      if (clazz == null) {
+        if (other.clazz != null)
+          return false;
+      } else if (!clazz.equals(other.clazz))
+        return false;
+      if (name == null) {
+        if (other.name != null)
+          return false;
+      } else if (!name.equals(other.name))
+        return false;
+      return true;
+    }
 
+    final String name;
+    final Method reader;
+    final Method writer;
+    final Class clazz;
+    
+    public MetaDataAttr(String name, Method reader, Method writer, Class clazz) {
+      this.name = name;
+      this.reader = reader;
+      this.writer = writer;
+      this.clazz = clazz;
+    }
+  }
+    
+  private static final List<MetaDataAttr> ATTRIBUTE_0 = new ArrayList<MetaDataObject_impl.MetaDataAttr>(0);
+  
+  // Cache for Java Bean info lookup
+  // Class level cache (static) for introspection - 30x speedup in CDE for large descriptor
+  private static final transient ConcurrentHashMapWithProducer<Class<? extends MetaDataObject_impl>, MetaDataAttr[]> class2attrsMap =
+      new ConcurrentHashMapWithProducer<Class<? extends MetaDataObject_impl>, MetaDataAttr[]>();
+      
   private transient URL mSourceUrl;
   
   // This is only used if we are capturing comments and ignorable whitespace in the XML
@@ -127,6 +184,60 @@ public abstract class MetaDataObject_imp
   }
 
   /**
+   * Override this method to include additional attributes
+   * @return additional attributes
+   */
+  public List<MetaDataAttr> getAdditionalAttributes() {
+    return ATTRIBUTE_0;
+  }
+  
+  /**
+   * On first call, looks up the information using JavaBeans introspection, but then
+   * caches the result for subsequent calls.
+   * 
+   * Any class which wants to add additional parameters needs to implement / override
+   * getAdditionalParameters.
+   * @return an array of Attribute objects associated with this class
+   * @throws  
+   */
+  MetaDataAttr[] getAttributes() {
+    final Class<? extends MetaDataObject_impl> clazz = this.getClass();
+    MetaDataAttr[] attrs = class2attrsMap.get(clazz), otherAttrs = null;
+    if (null == attrs) {
+      PropertyDescriptor[] pds;
+      try {
+        pds = Introspector.getBeanInfo(clazz, Introspector.IGNORE_ALL_BEANINFO).getPropertyDescriptors();
+      } catch (IntrospectionException e) {
+        throw new UIMARuntimeException(e);
+      }
+      ArrayList<MetaDataAttr> resultList = new ArrayList<MetaDataAttr>(pds.length);
+      for (PropertyDescriptor pd : pds) {
+        // only include properties with read and write methods,
+        // and don't include the SourceUrl property, which is for
+        // internal bookkeeping and shouldn't affect object equality
+        // and don't include infoset, which is for internal bookkeeping
+        // related to comments and whitespace
+        if (pd.getReadMethod() != null && pd.getWriteMethod() != null
+                && !pd.getName().equals(PROP_NAME_SOURCE_URL)
+                && !pd.getName().equals(PROP_NAME_INFOSET)) {
+          String propName = pd.getName();
+          Class<?> propClass = pd.getPropertyType();
+          // translate primitive types (int, boolean, etc.) to wrapper classes
+          if (propClass.isPrimitive()) {
+            propClass = getWrapperClass(propClass);
+          }
+          resultList.add(new MetaDataAttr(propName, pd.getReadMethod(), pd.getWriteMethod(), propClass));
+        }
+      }
+      resultList.addAll(getAdditionalAttributes());
+      attrs = resultList.toArray(new MetaDataAttr[resultList.size()]);
+      otherAttrs = class2attrsMap.putIfAbsent(clazz, attrs);
+      attrs = (otherAttrs != null) ? otherAttrs : attrs;
+    }
+    return attrs;
+  }
+  
+  /**
    * Returns a list of <code>NameClassPair</code> objects indicating the attributes of this object
    * and the String names of the Classes of the attributes' values. 
    *   For primitive types, the wrapper classes will be
@@ -135,8 +246,11 @@ public abstract class MetaDataObject_imp
    * Several subclasses override this, to add additional items to the list.
    * 
    * @see org.apache.uima.resource.metadata.MetaDataObject#listAttributes()
+   * @deprecated - use getAttributes
    */
+  @Deprecated
   public List<NameClassPair> listAttributes() {
+    
     try {
       PropertyDescriptor[] props = getPropertyDescriptors();
       List<NameClassPair> resultList = new ArrayList<NameClassPair>(props.length);
@@ -164,17 +278,29 @@ public abstract class MetaDataObject_imp
     }
   }
 
+  private Object getAttributeValue(MetaDataAttr attr) {
+    final Method reader = attr.reader;
+    if (reader != null) {
+      try {
+        return reader.invoke(this);
+      } catch (Exception e) {
+        throw new UIMARuntimeException(e);
+      }
+    }
+    return null;
+  }
+  
   /**
    * @see org.apache.uima.resource.metadata.MetaDataObject#getAttributeValue(String)
    */
   public Object getAttributeValue(String aName) {
     try {
-      PropertyDescriptor[] props = getPropertyDescriptors();
-      for (int i = 0; i < props.length; i++) {
-        if (props[i].getName().equals(aName)) {
-          Method reader = props[i].getReadMethod();
+      MetaDataAttr[] attrs = getAttributes();
+      for (MetaDataAttr attr : attrs) {
+        if (attr.name.equals(aName)) {
+          Method reader = attr.reader;
           if (reader != null) {
-            return reader.invoke(this, new Object[0]);
+            return reader.invoke(this);
           }
         }
       }
@@ -195,18 +321,13 @@ public abstract class MetaDataObject_imp
    *         if there is no attribute with the given name.
    */
   public Class getAttributeClass(String aName) {
-    try {
-      // note: listAttributes() is overridden in some subclasses to add additional items
-      List<NameClassPair> attrList = listAttributes();
-      for (NameClassPair ncp : attrList) {
-        if (ncp.getName().equals(aName)) {
-          return Class.forName(ncp.getClassName()); 
-        }
+    MetaDataAttr[] attrList = getAttributes();
+    for (MetaDataAttr attr : attrList) {
+      if (attr.name.equals(aName)) {
+        return attr.clazz; 
       }
-      return null;
-    } catch (Exception e) {
-      throw new UIMARuntimeException(e);
     }
+    return null;
   }
 
   /**
@@ -218,15 +339,30 @@ public abstract class MetaDataObject_imp
     return true;
   }
 
+  private void setAttributeValue(MetaDataAttr attr, Object aValue) {
+    Method writer = attr.writer;
+    if (writer != null) {
+      try {
+        writer.invoke(this, aValue);
+      } catch (IllegalArgumentException e) {
+        throw new UIMA_IllegalArgumentException(
+                UIMA_IllegalArgumentException.METADATA_ATTRIBUTE_TYPE_MISMATCH, new Object[] {
+                    aValue, attr.name }, e);
+      } catch (Exception e) {
+        throw new UIMARuntimeException(e);
+      }
+    }
+  }
+  
   /**
    * @see org.apache.uima.resource.metadata.MetaDataObject#setAttributeValue(String, Object)
    */
   public void setAttributeValue(String aName, Object aValue) {
     try {
-      PropertyDescriptor[] props = getPropertyDescriptors();
-      for (int i = 0; i < props.length; i++) {
-        if (props[i].getName().equals(aName)) {
-          Method writer = props[i].getWriteMethod();
+      MetaDataAttr[] attrs = getAttributes();
+      for (MetaDataAttr attr : attrs) {
+        if (attr.name.equals(aName)) {
+          Method writer = attr.writer;
           if (writer != null) {
             try {
               writer.invoke(this, new Object[] { aValue });
@@ -317,6 +453,8 @@ public abstract class MetaDataObject_imp
    * Sets the URL from which this object was parsed. Typically only the XML parser sets this. This
    * recursively sets the source URL of all descendants of this object.
    * 
+   * Recursion doesn't happen for sub arrays/maps of maps or arrays.
+   * 
    * @param aUrl
    *          the location of the XML file from which this object was parsed
    */
@@ -324,27 +462,23 @@ public abstract class MetaDataObject_imp
     mSourceUrl = aUrl;
 
     // set recursively on subobjects
-    List<NameClassPair> attrs = listAttributes();
-    Iterator<NameClassPair> i = attrs.iterator();
-    while (i.hasNext()) {
-      String attrName = ((NameClassPair) i.next()).getName();
-      Object val = getAttributeValue(attrName);
+    MetaDataAttr[] attrs = getAttributes();
+    for (MetaDataAttr attr : attrs) {
+      Object val = getAttributeValue(attr);
       if (val instanceof MetaDataObject_impl) {
         ((MetaDataObject_impl) val).setSourceUrl(aUrl);
       } else if (val != null && val.getClass().isArray()) {
-        int len = Array.getLength(val);
-        for (int j = 0; j < len; j++) {
-          Object arrayElem = Array.get(val, j);
-          if (arrayElem instanceof MetaDataObject_impl) {
-            ((MetaDataObject_impl) arrayElem).setSourceUrl(aUrl);
+        Object[] arrayVal = (Object[]) val;
+        for (Object item : arrayVal) {
+          if (item instanceof MetaDataObject_impl) {
+            ((MetaDataObject_impl) item).setSourceUrl(aUrl);
           }
         }
       } else if (val instanceof Map) {
-        Iterator valIter = ((Map) val).values().iterator();
-        while (valIter.hasNext()) {
-          Object mapVal = valIter.next();
-          if (mapVal instanceof MetaDataObject_impl) {
-            ((MetaDataObject_impl) mapVal).setSourceUrl(aUrl);
+        Collection values = ((Map) val).values();
+        for (Object value : values) {
+          if (value instanceof MetaDataObject_impl) {
+            ((MetaDataObject_impl) value).setSourceUrl(aUrl);
           }
         }
       }
@@ -366,13 +500,13 @@ public abstract class MetaDataObject_imp
     }
 
     // now clone all values that are MetaDataObjects
-    List<NameClassPair> attrs = listAttributes();
-    for (NameClassPair ncp : attrs) {
-      String attrName = ncp.getName();
-      Object val = getAttributeValue(attrName);
+    MetaDataAttr[] attrs = getAttributes();
+    for (MetaDataAttr attr : attrs) {
+//      String attrName = ncp.name;
+      Object val = getAttributeValue(attr);
       if (val instanceof MetaDataObject) {
         Object clonedVal = ((MetaDataObject) val).clone();
-        clone.setAttributeValue(attrName, clonedVal);
+        clone.setAttributeValue(attr, clonedVal);
       } else if (val != null && val.getClass().isArray()) {
         // clone the array, and clone any MetaDataObjects in the array
         Class componentType = val.getClass().getComponentType();
@@ -385,7 +519,7 @@ public abstract class MetaDataObject_imp
           }
           Array.set(arrayClone, j, component);
         }
-        clone.setAttributeValue(attrName, arrayClone);
+        clone.setAttributeValue(attr, arrayClone);
       }
     }
     return clone;
@@ -397,12 +531,10 @@ public abstract class MetaDataObject_imp
   public String toString() {
     StringBuffer buf = new StringBuffer();
     buf.append(getClass().getName()).append(": \n");
-    List<NameClassPair> attrList = listAttributes();
-    Iterator<NameClassPair> i = attrList.iterator();
-    while (i.hasNext()) {
-      NameClassPair ncp = (NameClassPair) i.next();
-      buf.append(ncp.getName() + " = ");
-      Object val = getAttributeValue(ncp.getName());
+    MetaDataAttr[] attrList = getAttributes();
+    for (MetaDataAttr attr : attrList) {
+      buf.append(attr.name + " = ");
+      Object val = getAttributeValue(attr);
       if (val == null)
         buf.append("NULL");
       else if (val instanceof Object[]) {
@@ -429,28 +561,28 @@ public abstract class MetaDataObject_imp
    * @return true if and only if this object is equal to <code>aObj</code>
    */
   public boolean equals(Object aObj) {
-    if (!(aObj instanceof MetaDataObject)) {
+    if (!(aObj instanceof MetaDataObject_impl)) {
       return false;
     }
-    MetaDataObject mdo = (MetaDataObject) aObj;
+    MetaDataObject_impl mdo = (MetaDataObject_impl) aObj;
     // get the attributes (and classes) for the two objects
-    List<NameClassPair> theseAttrs = this.listAttributes();
-    List<NameClassPair> thoseAttrs = mdo.listAttributes();
+    
+    MetaDataAttr[] theseAttrs = this.getAttributes();
+    MetaDataAttr[] thoseAttrs = mdo.getAttributes();
     // attribute lists must be same length
-    if (theseAttrs.size() != thoseAttrs.size()) {
+    if (theseAttrs.length != thoseAttrs.length) {
       return false;
     }
     // iterate through all attributes in this object
-    Iterator<NameClassPair> i = theseAttrs.iterator();
-    while (i.hasNext()) {
-      NameClassPair ncp = i.next();
+    List<MetaDataAttr> thoseAttrsAsList = Arrays.asList(thoseAttrs);
+    for (MetaDataAttr attr : theseAttrs) {
       // other object must contain this attribute
-      if (!thoseAttrs.contains(ncp)) {
+      if (!thoseAttrsAsList.contains(attr)) {
         return false;
       }
       // get values and test equivalency
-      Object val1 = this.getAttributeValue(ncp.getName());
-      Object val2 = mdo.getAttributeValue(ncp.getName());
+      Object val1 = this.getAttributeValue(attr);
+      Object val2 = mdo.getAttributeValue(attr);
       if (!valuesEqual(val1, val2)) {
         return false;
       }
@@ -533,34 +665,21 @@ public abstract class MetaDataObject_imp
     int hashCode = 0;
 
     // add the hash codes of all attributes
-    List<NameClassPair> attrs = listAttributes();
-    Iterator<NameClassPair> i = attrs.iterator();
-    while (i.hasNext()) {
-      String attrName = ((NameClassPair) i.next()).getName();
-      Object val = getAttributeValue(attrName);
+    MetaDataAttr[] attrs = getAttributes();
+    for (MetaDataAttr attr : attrs) {
+      String attrName = attr.name;
+      Object val = getAttributeValue(attr);
       if (val != null) {
         if (val instanceof Object[]) {
-          Object[] arr = (Object[]) val;
-          for (int j = 0; j < arr.length; j++) {
-            if (arr[j] != null) {
-              hashCode += arr[j].hashCode();
-            }
-          }
-        } else if (val instanceof Map) // only need to do this to handle Maps w/ array vals
-        {
-          Set entrySet = ((Map) val).entrySet();
-          Iterator it = entrySet.iterator();
-          while (it.hasNext()) {
-            Map.Entry entry = (Map.Entry) it.next();
+          hashCode += Arrays.hashCode((Object[]) val);
+        } else if (val instanceof Map) {// only need to do this to handle Maps w/ array vals
+             // note doesn't handle maps whose values are maps.  
+          Set<Map.Entry> entrySet = ((Map) val).entrySet();
+          for (Map.Entry entry : entrySet) {
             hashCode += entry.getKey().hashCode();
             Object subval = entry.getValue();
             if (subval instanceof Object[]) {
-              Object[] arr = (Object[]) subval;
-              for (int j = 0; j < arr.length; j++) {
-                if (arr[j] != null) {
-                  hashCode += arr[j].hashCode();
-                }
-              }
+              hashCode += Arrays.hashCode((Object[]) subval);
             } else {
               hashCode += subval.hashCode();
             }
@@ -1367,14 +1486,12 @@ public abstract class MetaDataObject_imp
    *         <code>MetaDataObject_impl</code>.
    * 
    * @throws IntrospectionException if introspection fails
+   * @deprecated - use getAttributes instead
    */
-  protected PropertyDescriptor[] getPropertyDescriptors() throws IntrospectionException { 
-    PropertyDescriptor[] pd = mPropertyDescriptorsMap.get(this.getClass()), pdOther = null;
-    if (null == pd) {
-      pd = Introspector.getBeanInfo(this.getClass(), Introspector.IGNORE_ALL_BEANINFO).getPropertyDescriptors();
-      pdOther = mPropertyDescriptorsMap.putIfAbsent(this.getClass(), pd);
-    }
-    return (pdOther != null) ? pdOther : pd;
+  @Deprecated
+  // never called, we hope.  No longer caches anything.  
+  protected PropertyDescriptor[] getPropertyDescriptors() throws IntrospectionException {    
+    return Introspector.getBeanInfo(this.getClass(), Introspector.IGNORE_ALL_BEANINFO).getPropertyDescriptors();
   }
 
   

Added: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/util/ConcurrentHashMapWithProducer.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/util/ConcurrentHashMapWithProducer.java?rev=1579031&view=auto
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/util/ConcurrentHashMapWithProducer.java (added)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/util/ConcurrentHashMapWithProducer.java Tue Mar 18 20:36:12 2014
@@ -0,0 +1,67 @@
+/*
+ * 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.uima.util;
+
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+
+
+/**
+ * A concurrent map supporting a value-creating get.
+ * There's a small window where the value producing function could be called multiple times
+ * on different threads, but the first one will be used and the others thrown away.
+ */
+public class ConcurrentHashMapWithProducer<K, V> extends ConcurrentHashMap<K, V> {  
+
+  private static final long serialVersionUID = 1L;
+
+  public ConcurrentHashMapWithProducer() {
+    super();
+  }
+
+  public ConcurrentHashMapWithProducer(int initialCapacity, float loadFactor, int concurrencyLevel) {
+    super(initialCapacity, loadFactor, concurrencyLevel);
+  }
+
+
+  public ConcurrentHashMapWithProducer(int initialCapacity, float loadFactor) {
+    super(initialCapacity, loadFactor);
+  }
+
+
+  public ConcurrentHashMapWithProducer(int initialCapacity) {
+    super(initialCapacity);
+  }
+
+
+  public ConcurrentHashMapWithProducer(Map<? extends K, ? extends V> m) {
+    super(m);
+  }
+
+  public V get(K key, Callable<V> valueProducer) throws Exception {
+    V value = get(key), otherValue = null;
+    if (null == value) {
+      otherValue = putIfAbsent(key,  value = valueProducer.call());
+      value = (otherValue != null) ? otherValue : value;
+    }
+    return value;
+  }
+}

Modified: uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/resource/metadata/impl/MetaDataObject_implTest.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/resource/metadata/impl/MetaDataObject_implTest.java?rev=1579031&r1=1579030&r2=1579031&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/resource/metadata/impl/MetaDataObject_implTest.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/resource/metadata/impl/MetaDataObject_implTest.java Tue Mar 18 20:36:12 2014
@@ -22,7 +22,9 @@ package org.apache.uima.resource.metadat
 import java.io.ByteArrayInputStream;
 import java.io.StringWriter;
 import java.io.Writer;
+import java.util.Arrays;
 import java.util.HashSet;
+import java.util.Set;
 
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
@@ -35,6 +37,7 @@ import org.apache.uima.internal.util.Ser
 import org.apache.uima.resource.metadata.ConfigurationParameterSettings;
 import org.apache.uima.resource.metadata.MetaDataObject;
 import org.apache.uima.resource.metadata.NameValuePair;
+import org.apache.uima.resource.metadata.impl.MetaDataObject_impl.MetaDataAttr;
 import org.apache.uima.test.junit_extension.JUnitExtension;
 import org.apache.uima.util.NameClassPair;
 import org.apache.uima.util.XMLParser;
@@ -95,23 +98,47 @@ public class MetaDataObject_implTest ext
     fruitBag.setAttributeValue("fruits", fruitArray);
   }
 
+//  /**
+//   * Tests the {@link MetaDataObject#listAttributes()} method.
+//   */
+//  public void testListAttributes() throws Exception {
+//    try {
+//      HashSet<NameClassPair> apple1Attrs = new HashSet<NameClassPair>(apple1.listAttributes());
+//      HashSet<NameClassPair> orangeAttrs = new HashSet<NameClassPair>(orange.listAttributes());
+//      HashSet<NameClassPair> bagAttrs = new HashSet<NameClassPair>(fruitBag.listAttributes());
+//
+//      Assert.assertEquals(TestFruitObject.getAttributeSet(), apple1Attrs);
+//      Assert.assertEquals(TestFruitObject.getAttributeSet(), orangeAttrs);
+//      Assert.assertEquals(TestFruitBagObject.getAttributeSet(), bagAttrs);
+//    } catch (RuntimeException e) {
+//      JUnitExtension.handleException(e);
+//    }
+//  }
+
   /**
-   * Tests the {@link MetaDataObject#listAttributes()} method.
+   * Test the getAttributes method
    */
-  public void testListAttributes() throws Exception {
+  public void testGetAttributes() throws Exception {
     try {
-      HashSet<NameClassPair> apple1Attrs = new HashSet<NameClassPair>(apple1.listAttributes());
-      HashSet<NameClassPair> orangeAttrs = new HashSet<NameClassPair>(orange.listAttributes());
-      HashSet<NameClassPair> bagAttrs = new HashSet<NameClassPair>(fruitBag.listAttributes());
-
-      Assert.assertEquals(TestFruitObject.getAttributeSet(), apple1Attrs);
-      Assert.assertEquals(TestFruitObject.getAttributeSet(), orangeAttrs);
-      Assert.assertEquals(TestFruitBagObject.getAttributeSet(), bagAttrs);
+      HashSet<MetaDataAttr> apple1Attrs = new HashSet<MetaDataAttr>(Arrays.asList(apple1.getAttributes()));
+      HashSet<MetaDataAttr> orangeAttrs = new HashSet<MetaDataAttr>(Arrays.asList(orange.getAttributes()));
+      HashSet<MetaDataAttr> bagAttrs = new HashSet<MetaDataAttr>(Arrays.asList(fruitBag.getAttributes()));
+      
+      Set<MetaDataAttr> r = TestFruitObject.getMetaDataAttrSet();
+      for (MetaDataAttr r1 : r) {
+        if (!apple1Attrs.contains(r1)) {
+          System.out.println("found bad one");
+        }
+      }
+      System.out.println(r.equals (apple1Attrs));
+      assertEquals(TestFruitObject.getMetaDataAttrSet(), apple1Attrs);
+      assertEquals(TestFruitObject.getMetaDataAttrSet(), orangeAttrs);
+      assertEquals(TestFruitBagObject.getMetaDataAttrSet(), bagAttrs);
     } catch (RuntimeException e) {
-      JUnitExtension.handleException(e);
+    JUnitExtension.handleException(e);
     }
   }
-
+  
   /**
    * Tests the {@link MetaDataObject#equals(Object)} method.
    */

Modified: uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/resource/metadata/impl/TestFruitBagObject.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/resource/metadata/impl/TestFruitBagObject.java?rev=1579031&r1=1579030&r2=1579031&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/resource/metadata/impl/TestFruitBagObject.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/resource/metadata/impl/TestFruitBagObject.java Tue Mar 18 20:36:12 2014
@@ -22,18 +22,14 @@ package org.apache.uima.resource.metadat
 import java.util.HashSet;
 import java.util.Set;
 
-import org.apache.uima.util.NameClassPair;
-
 /**
  * A simple concrete MetaDataObject implementation for use in testing the MetaDataObject_impl class.
  */
 public class TestFruitBagObject extends MetaDataObject_impl {
-  
+
   static final private XmlizationInfo XMLIZATION_INFO = new XmlizationInfo("fruitBag",
           new PropertyXmlInfo[] { new PropertyXmlInfo("fruits") });
 
-  private String mName;
-
   private TestFruitObject[] mFruits;
   
   public TestFruitBagObject() {
@@ -48,13 +44,26 @@ public class TestFruitBagObject extends 
     mFruits = aFruits;
   }
 
-  /**
-   * For testing purposes - a hardcoded attribute set. Should be compared with the results of
-   * {@link #listAttributes()}.
-   */
-  static public Set<NameClassPair> getAttributeSet() {
-    Set<NameClassPair> result = new HashSet<NameClassPair>();
-    result.add(new NameClassPair("fruits", TestFruitObject[].class.getName()));
+//  /**
+//   * For testing purposes - a hardcoded attribute set. Should be compared with the results of
+//   * {@link #listAttributes()}.
+//   */
+//  static public Set<NameClassPair> getAttributeSet() {
+//    Set<NameClassPair> result = new HashSet<NameClassPair>();
+//    result.add(new NameClassPair("fruits", TestFruitObject[].class.getName()));
+//    return result;
+//  }
+  
+  static public Set<MetaDataAttr> getMetaDataAttrSet() {
+    Set<MetaDataAttr> result = new HashSet<MetaDataAttr>();
+    try {
+    result.add(new MetaDataAttr("fruits", 
+        TestFruitBagObject.class.getDeclaredMethod("getFruits"), 
+        TestFruitBagObject.class.getDeclaredMethod("setFruits", TestFruitObject[].class),
+        TestFruitObject[].class));
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
     return result;
   }
 

Modified: uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/resource/metadata/impl/TestFruitObject.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/resource/metadata/impl/TestFruitObject.java?rev=1579031&r1=1579030&r2=1579031&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/resource/metadata/impl/TestFruitObject.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/resource/metadata/impl/TestFruitObject.java Tue Mar 18 20:36:12 2014
@@ -106,6 +106,45 @@ public class TestFruitObject extends Met
     result.add(new NameClassPair("commonUses", String[].class.getName()));
     return result;
   }
+  
+  static public Set<MetaDataAttr> getMetaDataAttrSet() {
+    HashSet<MetaDataAttr> result = new HashSet<MetaDataAttr>();
+    try {
+    result.add(new MetaDataAttr(
+        "name", 
+        TestFruitObject.class.getDeclaredMethod("getName"),
+        TestFruitObject.class.getDeclaredMethod("setName", String.class), 
+        String.class));
+    result.add(new MetaDataAttr(
+        "avgWeightLbs", 
+        TestFruitObject.class.getDeclaredMethod("getAvgWeightLbs"),
+        TestFruitObject.class.getDeclaredMethod("setAvgWeightLbs", float.class),
+        Float.class));
+    result.add(new MetaDataAttr(
+        "color", 
+        TestFruitObject.class.getDeclaredMethod("getColor"),
+        TestFruitObject.class.getDeclaredMethod("setColor", String.class), 
+        String.class));
+    result.add(new MetaDataAttr(
+        "avgCostCents", 
+        TestFruitObject.class.getDeclaredMethod("getAvgCostCents"),
+        TestFruitObject.class.getDeclaredMethod("setAvgCostCents", int.class), 
+        Integer.class));
+    result.add(new MetaDataAttr(
+        "citrus", 
+        TestFruitObject.class.getDeclaredMethod("isCitrus"),
+        TestFruitObject.class.getDeclaredMethod("setCitrus", boolean.class), 
+        Boolean.class));
+    result.add(new MetaDataAttr(
+        "commonUses", 
+        TestFruitObject.class.getDeclaredMethod("getCommonUses"),
+        TestFruitObject.class.getDeclaredMethod("setCommonUses", String[].class), 
+        String[].class));
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+    return result;
+  }
 
   private String mName;