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/10 20:37:28 UTC

svn commit: r1069539 - /incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/

Author: mbenson
Date: Thu Feb 10 19:37:27 2011
New Revision: 1069539

URL: http://svn.apache.org/viewvc?rev=1069539&view=rev
Log:
add timestamp-based caching of merged metadata for improved performance

Added:
    incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/CacheMeta.java   (with props)
    incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicModelManager.java
      - copied, changed from r1069161, incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicModel.java
    incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/Timestamped.java   (with props)
Removed:
    incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicModel.java
Modified:
    incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicMetaGraphManagerImpl.java
    incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicValidatorContext.java
    incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicValidatorFactory.java
    incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/MetaContainer.java

Added: incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/CacheMeta.java
URL: http://svn.apache.org/viewvc/incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/CacheMeta.java?rev=1069539&view=auto
==============================================================================
--- incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/CacheMeta.java (added)
+++ incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/CacheMeta.java Thu Feb 10 19:37:27 2011
@@ -0,0 +1,40 @@
+/*
+ *  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.bval.jsr303.dynamic;
+
+import org.apache.bval.model.FeaturesCapable;
+
+/**
+ * Interface describing an entity that caches some {@link FeaturesCapable}.
+ * 
+ * @version $Rev$ $Date$
+ */
+public interface CacheMeta<T extends FeaturesCapable> {
+    /**
+     * Cache meta.
+     * 
+     * @param meta
+     */
+    void cache(T meta);
+
+    /**
+     * Get the cached meta, if any.
+     * 
+     * @return T
+     */
+    T getCached();
+}

Propchange: incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/CacheMeta.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicMetaGraphManagerImpl.java
URL: http://svn.apache.org/viewvc/incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicMetaGraphManagerImpl.java?rev=1069539&r1=1069538&r2=1069539&view=diff
==============================================================================
--- incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicMetaGraphManagerImpl.java (original)
+++ incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicMetaGraphManagerImpl.java Thu Feb 10 19:37:27 2011
@@ -16,11 +16,8 @@
  */
 package org.apache.bval.jsr303.dynamic;
 
-import static org.apache.bval.jsr303.dynamic.DynamicModel.getRequiredContainer;
-import static org.apache.bval.jsr303.dynamic.DynamicModel.getRequiredProperty;
-import static org.apache.bval.jsr303.dynamic.DynamicModel.mergeFeatures;
-import static org.apache.bval.jsr303.dynamic.DynamicModel.Features.DYNAMIC_CONSTRAINT_COLLECTION;
-import static org.apache.bval.jsr303.dynamic.DynamicModel.Features.META_CONTAINER;
+import static org.apache.bval.jsr303.dynamic.DynamicModelManager.Features.DYNAMIC_CONSTRAINT_COLLECTION;
+import static org.apache.bval.jsr303.dynamic.DynamicModelManager.Features.META_CONTAINER;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
@@ -30,12 +27,16 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.Deque;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.NoSuchElementException;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
 import javax.validation.Path;
+import javax.validation.Valid;
+import javax.validation.ValidationException;
 
 import org.apache.bval.MetaBeanFinder;
 import org.apache.bval.jsr303.AnnotationProcessor;
@@ -90,6 +91,7 @@ final class DynamicMetaGraphManagerImpl 
          * {@inheritDoc}
          */
         public void handleIndexOrKey(String value) {
+            // actual index/key value doesn't matter, so:
             handleGenericInIterable();
         }
 
@@ -166,7 +168,7 @@ final class DynamicMetaGraphManagerImpl 
             if (property != null) {
                 metaBean = property.getMetaBean();
             }
-            property = getRequiredProperty(metaBean, name);
+            property = DynamicModelManager.get().getRequiredProperty(metaBean, name);
         }
 
         /**
@@ -177,197 +179,374 @@ final class DynamicMetaGraphManagerImpl 
         }
 
         private MetaContainer<?> getContainer() {
-            return getRequiredContainer(property == null ? metaBean : property);
+            return DynamicModelManager.get().getRequiredContainer(property == null ? metaBean : property);
         }
     }
 
-    final ThreadLocal<DynamicValidationState> currentValidationState = new ThreadLocal<DynamicValidationState>();
-
-    private final Logger log = LoggerFactory.getLogger(getClass());
+    private class ReadOnlyMetaBean extends MetaBean implements Timestamped {
+        private static final long serialVersionUID = 1L;
 
-    private final DynamicMetaGraphManager.Interface writable = new DynamicMetaGraphManager.Interface() {
+        final long millis = timeContext.millis;
 
         /**
-         * {@inheritDoc}
+         * Create a new DynamicMetaGraphManagerImpl.ReadOnlyMetaBean instance.
          */
-        public MetaBean getMetaBean(Class<?> type) {
-            MetaBean result;
-            if (dynamicBeanInfo.containsKey(type)) {
-                result = dynamicBeanInfo.get(type);
-            } else {
-                result = new MetaBean();
-                result.setBeanClass(type);
-                MetaBean faster = dynamicBeanInfo.putIfAbsent(type, result);
-                if (faster != null) {
-                    return faster;
+        ReadOnlyMetaBean(MetaBean metaBean) {
+            setId(metaBean.getId());
+            setName(metaBean.getName());
+            setBeanClass(metaBean.getBeanClass());
+            getFeatures().putAll(metaBean.getFeatures());
+            setValidations(metaBean.getValidations().clone());
+
+            MetaProperty[] properties = ObjectUtils.clone(metaBean.getProperties());
+            if (properties != null) {
+                for (int i = properties.length - 1; i >= 0; i--) {
+                    if (properties[i] != null) {
+                        properties[i] = properties[i].copy();
+                    }
                 }
             }
-            return result;
+            setProperties(properties);
         }
 
         /**
          * {@inheritDoc}
          */
-        public <T extends FeaturesCapable> T getMeta(Class<?> type, String propertyPath) {
-            MetaBean root = getMetaBean(type);
-            @SuppressWarnings("unchecked")
-            T result = (T) PathNavigation.navigateAndReturn(propertyPath, new NavigateOrBuildGraph(root));
-            return result;
+        public void touch() {
         }
-    };
 
-    private final DynamicMetaGraphManager.Interface readOnly = new DynamicMetaGraphManager.Interface() {
         /**
          * {@inheritDoc}
          */
-        public MetaBean getMetaBean(Class<?> type) {
-            final MetaBean initial;
-            final PathImpl path;
-            final Object rootBean;
-            final MetaBean rootMetaBean;
+        public long millis() {
+            return millis;
+        }
 
-            DynamicValidationState validationState = currentValidationState.get();
-            if (validationState == null) {
-                initial = metaBeanFinder.findForClass(type);
-                path = PathImpl.create(null);
-                rootBean = null;
-                rootMetaBean = initial;
-            } else {
-                rootMetaBean = validationState.getRootMetaBean();
-                path = validationState.getPropertyPath();
-                initial = path.isRootPath() ? rootMetaBean : metaBeanFinder.findForClass(type);
-                rootBean = validationState.getRootBean();
+    }
+
+    /**
+     * Iterator implementation that expands dynamic info from a typedPaths collection; results are sorted in order of
+     * descending class specificity within descending path length (which likewise amounts to specificity). This is,
+     * incidentally, the opposite order to that in which results would be merged--once again because of specificity
+     * concerns. The rationale for this is that the longer path is more specific and thus more likely to stem from the
+     * more important part of your domain model.
+     */
+    private class LeafIterator implements Iterable<MetaBean>, Iterator<MetaBean> {
+        private final Iterator<Pair<? extends PathImpl, Class<?>>> wrapped;
+
+        /** next result */
+        private Object next;
+
+        /** current path to leaf info */
+        private PathImpl path;
+
+        /** iterator of dynamic info assignable from current root */
+        private Iterator<MetaBean> dynamicInfoIter;
+
+        /** whether the iterator is used to find a cached result */
+        private final boolean findCached;
+
+        /**
+         * Create a new LeafIterator instance.
+         * 
+         * @param typedPaths
+         */
+        LeafIterator(Iterable<Pair<? extends PathImpl, Class<?>>> typedPaths) {
+            this(typedPaths, false);
+        }
+
+        /**
+         * Create a new LeafIterator instance.
+         * 
+         * @param typedPaths
+         * @param findCached
+         *            if <code>true</code>, results are filtered specifically for finding cached results: the available
+         *            dynamic metadata for the most specific class/longest path combination must be a {@link CacheMeta}
+         *            instance whose {@link MetaBean} payload is up-to-date compared to the {@link CacheMeta} instance;
+         *            otherwise the entire iterator is empty. Otherwise, the first result <em>is</em> the cached result
+         *            and subsequent results are assumed to serve no purpose beyond timestamp checking and thus limited
+         *            to {@link Timestamped} instances.
+         */
+        LeafIterator(Iterable<Pair<? extends PathImpl, Class<?>>> typedPaths, boolean findCached) {
+            this.wrapped = typedPaths.iterator();
+            this.findCached = findCached;
+            next = read();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean hasNext() {
+            return next != null;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public MetaBean next() {
+            if (next == null) {
+                throw new NoSuchElementException();
             }
             try {
-                List<Pair<? extends PathImpl, Class<?>>> typedPaths =
-                    getTypedPaths(path, rootBean, rootMetaBean.getBeanClass());
+                return (MetaBean) next;
+            } finally {
+                next = read();
+            }
+        }
 
-                /*
-                 * For each step of the path, we need to know whether the bean we are looking for matches a mapped
-                 * property, so we build a stack of path/bean pairs. We use a stack so that when we process, the longest
-                 * path goes last and therefore wins over short paths: the rationale here is that the longer path is
-                 * more specific thus more likely to be rooted at the more important part of your domain model.
-                 */
-                Deque<MetaBean> toMerge = new ArrayDeque<MetaBean>();
-                for (Pair<? extends PathImpl, Class<?>> pair : typedPaths) {
-                    List<MetaBean> dynamicInfo = getAssignableDynamicInfo(pair.right);
+        /**
+         * {@inheritDoc}
+         */
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Iterator<MetaBean> iterator() {
+            return this;
+        }
 
-                    PathImpl relativePath = pair.left;
-                    for (MetaBean root : dynamicInfo) {
-                        MetaBean leaf = findLeaf(root, relativePath);
+        private Object read() {
+            outer: do {
+                if (dynamicInfoIter != null) {
+                    while (dynamicInfoIter.hasNext()) {
+                        MetaBean root = dynamicInfoIter.next();
+                        MetaBean leaf = findLeaf(root, path);
                         if (leaf != null) {
-                            toMerge.push(leaf);
+                            if (!findCached) {
+                                return leaf;
+                            }
+                            // next == null -> first result
+                            if (next == null) {
+                                // must be a CacheMeta instance
+                                if (leaf instanceof CacheMeta) {
+                                    @SuppressWarnings("unchecked")
+                                    // bearing an up-to-date payload:
+                                    MetaBean cached = ((CacheMeta<MetaBean>) leaf).getCached();
+                                    if (cached != null && Timestamped.Utils.COMPARATOR.compare(cached, leaf) >= 0) {
+                                        return cached;
+                                    }
+                                }
+                                return null;
+                            }
+                            // must be a Timestamped instance
+                            if (leaf instanceof Timestamped == false) {
+                                continue;
+                            }
                         }
                     }
                 }
-                if (!toMerge.isEmpty()) {
+                while (wrapped.hasNext()) {
+                    Pair<? extends PathImpl, Class<?>> pair = wrapped.next();
+                    path = pair.left;
+                    List<MetaBean> dynamicInfo = getAssignableDynamicInfo(pair.right);
+                    if (dynamicInfo.isEmpty()) {
+                        continue;
+                    }
+                    dynamicInfoIter = dynamicInfo.iterator();
+                    continue outer;
+                }
+                break;
+            } while (true);
+            return null;
+        }
 
-                    MetaBean result = initial.copy();
+    }
 
-                    HashSet<String> mergeProperties = new HashSet<String>();
+    final ThreadLocal<DynamicValidationState> currentValidationState = new ThreadLocal<DynamicValidationState>();
 
-                    for (MetaBean leaf : toMerge) {
-                        // copy bean + property features and constraints:
-                        mergeFeatures(result, leaf);
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    private final Timestamped.Context timeContext = new Timestamped.Context();
 
-                        for (MetaProperty sourceProperty : leaf.getProperties()) {
-                            mergeProperties.add(sourceProperty.getName());
-                            MetaProperty targetProperty = getRequiredProperty(result, sourceProperty.getName());
-                            mergeFeatures(targetProperty, sourceProperty);
-                        }
+    private final DynamicMetaGraphManager.Interface writable = timeContext
+        .wrap(new DynamicMetaGraphManager.Interface() {
+
+            /**
+             * {@inheritDoc}
+             */
+            public MetaBean getMetaBean(Class<?> type) {
+                if (dynamicBeanInfo.containsKey(type)) {
+                    return dynamicBeanInfo.get(type);
+                }
+                MetaBean result = timeContext.new Bean();
+                result.setBeanClass(type);
+                MetaBean faster = dynamicBeanInfo.putIfAbsent(type, result);
+                return faster == null ? result : faster;
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            public <T extends FeaturesCapable> T getMeta(Class<?> type, String propertyPath) {
+                MetaBean root = getMetaBean(type);
+                @SuppressWarnings("unchecked")
+                T result = (T) PathNavigation.navigateAndReturn(propertyPath, new NavigateOrBuildGraph(root));
+                return result;
+            }
+        });
+
+    private final DynamicMetaGraphManager.Interface readOnly = timeContext
+        .wrap(new DynamicMetaGraphManager.Interface() {
+            /**
+             * {@inheritDoc}
+             */
+            public MetaBean getMetaBean(Class<?> type) {
+                final MetaBean initial;
+                final PathImpl path;
+                final Object rootBean;
+                final MetaBean rootMetaBean;
+
+                DynamicValidationState validationState = currentValidationState.get();
+                if (validationState == null) {
+                    initial = metaBeanFinder.findForClass(type);
+                    path = PathImpl.create(null);
+                    rootBean = null;
+                    rootMetaBean = initial;
+                } else {
+                    rootMetaBean = validationState.getRootMetaBean();
+                    path = validationState.getPropertyPath();
+                    initial = path.isRootPath() ? rootMetaBean : metaBeanFinder.findForClass(type);
+                    rootBean = validationState.getRootBean();
+                }
+                try {
+                    List<Pair<? extends PathImpl, Class<?>>> typedPaths =
+                        getTypedPaths(path, rootBean, rootMetaBean.getBeanClass());
+
+                    MetaBean cached = findCachedMetaBean(typedPaths, initial);
+                    if (cached != null) {
+                        return cached;
                     }
 
-                    // now the dynamic constraints should FINALLY be fully
-                    // populated:
+                    // no existing cached result; thus we must merge metadata
 
-                    {
-                        Collection<Annotation> constraints =
-                            result.getFeature(DYNAMIC_CONSTRAINT_COLLECTION, EMPTY_DYNAMIC_ANNOTATIONS);
-                        for (Annotation constraint : constraints) {
-                            annotationProcessor.processAnnotation(constraint, result.getBeanClass(),
-                                new AppendValidationToMeta(result));
-                        }
+                    // as described in LeafIterator javadoc, read the entire iterator for reverse processing:
+                    Deque<MetaBean> toMerge = new ArrayDeque<MetaBean>();
+                    for (MetaBean leaf : new LeafIterator(typedPaths)) {
+                        toMerge.push(leaf);
                     }
-                    if (!mergeProperties.isEmpty()) {
-                        for (MetaProperty property : result.getProperties()) {
-                            String propertyName = property.getName();
-                            if (!mergeProperties.contains(propertyName)) {
-                                continue;
+                    if (!toMerge.isEmpty()) {
+                        final MetaBean result = new ReadOnlyMetaBean(initial);
+                        final DynamicModelManager manager = DynamicModelManager.get();
+
+                        HashSet<String> mergeProperties = new HashSet<String>();
+
+                        for (MetaBean leaf : toMerge) {
+                            // copy bean + property features and constraints:
+                            manager.mergeFeatures(result, leaf);
+
+                            for (MetaProperty sourceProperty : leaf.getProperties()) {
+                                mergeProperties.add(sourceProperty.getName());
+                                MetaProperty targetProperty =
+                                    manager.getRequiredProperty(result, sourceProperty.getName());
+                                manager.mergeFeatures(targetProperty, sourceProperty);
                             }
+                        }
+
+                        // now the dynamic constraints should FINALLY be fully populated:
+
+                        {
                             Collection<Annotation> constraints =
-                                property.getFeature(DYNAMIC_CONSTRAINT_COLLECTION, EMPTY_DYNAMIC_ANNOTATIONS);
+                                result.getFeature(DYNAMIC_CONSTRAINT_COLLECTION, EMPTY_DYNAMIC_ANNOTATIONS);
                             for (Annotation constraint : constraints) {
-                                annotationProcessor.processAnnotation(constraint, property, result.getBeanClass(),
-                                    new PropertyAccess(result.getBeanClass(), propertyName), new AppendValidationToMeta(
-                                        property));
+                                annotationProcessor.processAnnotation(constraint, result.getBeanClass(),
+                                    new AppendValidationToMeta(result));
                             }
                         }
+                        if (!mergeProperties.isEmpty()) {
+                            for (MetaProperty property : result.getProperties()) {
+                                String propertyName = property.getName();
+                                if (!mergeProperties.contains(propertyName)) {
+                                    continue;
+                                }
+                                Collection<Annotation> constraints =
+                                    property.getFeature(DYNAMIC_CONSTRAINT_COLLECTION, EMPTY_DYNAMIC_ANNOTATIONS);
+                                for (Annotation constraint : constraints) {
+                                    annotationProcessor.processAnnotation(constraint, property, result.getBeanClass(),
+                                        new PropertyAccess(result.getBeanClass(), propertyName),
+                                        new AppendValidationToMeta(property));
+                                }
+                            }
+                        }
+                        MetaBean mostSpecific = toMerge.getLast();
+                        if (mostSpecific instanceof CacheMeta) {
+                            @SuppressWarnings("unchecked")
+                            CacheMeta<MetaBean> cacheMeta = (CacheMeta<MetaBean>) mostSpecific;
+                            Timestamped.Utils.stamp(result);
+                            cacheMeta.cache(result);
+                        }
+                        return result;
                     }
-                    return result;
-                }
-            } catch (Exception e) {
-                StringBuilder msg =
-                    new StringBuilder("Encountered error applying dynamic constraints for type ")
-                        .append(type.getName());
-                if (!path.isRootPath()) {
-                    msg.append(": path ").append(path);
+                } catch (Exception e) {
+                    StringBuilder msg =
+                        new StringBuilder("Encountered error applying dynamic constraints for type ").append(type
+                            .getName());
+                    if (!path.isRootPath()) {
+                        msg.append(": path ").append(path);
+                    }
+                    log.error(msg.toString(), e);
                 }
-                log.error(msg.toString(), e);
+                return initial;
             }
-            return initial;
-        }
 
-        /**
-         * {@inheritDoc}
-         */
-        @SuppressWarnings("unchecked")
-        public <T extends FeaturesCapable> T getMeta(Class<?> type, String propertyPath) {
-            MetaBean rootMetaBean = metaBeanFinder.findForClass(type);
+            /**
+             * {@inheritDoc}
+             */
+            @SuppressWarnings("unchecked")
+            public <T extends FeaturesCapable> T getMeta(Class<?> type, String propertyPath) {
+                MetaBean rootMetaBean = metaBeanFinder.findForClass(type);
 
-            PathImpl path = PathImpl.createPathFromString(propertyPath);
-            String propertyName;
-            NodeImpl leafNode = path.getLeafNode();
-            if (leafNode == null || leafNode.getName() == null) {
-                propertyName = null;
-            } else {
-                propertyName = leafNode.getName();
-                if (leafNode.isInIterable()) {
-                    leafNode.setName(null);
+                PathImpl path = PathImpl.createPathFromString(propertyPath);
+                String propertyName;
+                NodeImpl leafNode = path.getLeafNode();
+                if (leafNode == null || leafNode.getName() == null) {
+                    propertyName = null;
                 } else {
-                    path.removeLeafNode();
+                    propertyName = leafNode.getName();
+                    if (leafNode.isInIterable()) {
+                        leafNode.setName(null);
+                    } else {
+                        path.removeLeafNode();
+                    }
                 }
-            }
-            FindType findType = new FindType(type);
-            PathNavigation.navigate(path.toString(), findType);
-            Class<?> beanType = findType.rawType;
+                FindType findType = new FindType(type);
+                PathNavigation.navigate(path.toString(), findType);
+                Class<?> beanType = findType.rawType;
 
-            FeaturesCapable result;
-            try {
+                FeaturesCapable result;
                 currentValidationState.set(new DynamicValidationStateBean(null, rootMetaBean, path));
-                result = getMetaBean(beanType);
-                if (propertyName != null) {
-                    MetaProperty prop = ((MetaBean) result).getProperty(propertyName);
-                    if (prop == null) {
-                        // build from its metabean
-
-                        findType = new FindType(beanType);
-                        PathNavigation.navigateAndReturn(propertyName, findType);
-                        prop = new MetaProperty();
-                        prop.setName(propertyName);
-                        ((MetaBean) result).putProperty(propertyName, prop);
-                        prop.setType(findType.type);
-
-                        MetaBean metaBean = getMetaBean(prop.getTypeClass());
-                        prop.setMetaBean(metaBean);
+                try {
+                    result = getMetaBean(beanType);
+                    if (propertyName != null) {
+                        // do not use e.g. DynamicModel.getRequiredProperty(); this is a RO result:
+                        MetaProperty prop = ((MetaBean) result).getProperty(propertyName);
+                        if (prop == null) {
+                            // build from its metabean:
+                            findType = new FindType(beanType);
+                            PathNavigation.navigateAndReturn(propertyName, findType);
+                            prop = new MetaProperty();
+                            prop.setName(propertyName);
+                            ((MetaBean) result).putProperty(propertyName, prop);
+                            prop.setType(findType.type);
+
+                            MetaBean metaBean = getMetaBean(prop.getTypeClass());
+                            prop.setMetaBean(metaBean);
+
+                            // make sure the source (dynamic) properties know how to
+                            // cascade so that when/if they copy over the target will
+                            // cascade them as well:
+                            annotationProcessor.addAccessStrategy(prop, new PropertyAccess(metaBean.getBeanClass(),
+                                propertyName));
+                        }
+                        result = prop;
                     }
-                    result = prop;
+                    return (T) result;
+                } finally {
+                    currentValidationState.remove();
                 }
-                return (T) result;
-            } finally {
-                currentValidationState.remove();
             }
-        }
-    };
+        });
 
     private final ConcurrentMap<Class<?>, MetaBean> dynamicBeanInfo = new ConcurrentHashMap<Class<?>, MetaBean>();
     private final AnnotationProcessor annotationProcessor;
@@ -406,6 +585,75 @@ final class DynamicMetaGraphManagerImpl 
         dynamicBeanInfo.clear();
     }
 
+    /**
+     * Apply a constraint into the writable meta-graph.
+     * @param beanType
+     * @param propertyPath
+     * @param annotation
+     */
+    void constrain(Class<?> beanType, String propertyPath, Annotation annotation) {
+        final boolean setManager = DynamicModelManager.THREAD_BOUND_MANAGER.get() == null;
+
+        if (setManager) {
+            DynamicModelManager.THREAD_BOUND_MANAGER.set(timeContext);
+        }
+        try {
+            FeaturesCapable meta = writable().getMeta(beanType, propertyPath);
+            if (annotation.annotationType().equals(Valid.class)) {
+                if (meta instanceof MetaProperty) {
+                    MetaProperty property = (MetaProperty) meta;
+                    MetaBean owner = property.getParentMetaBean();
+                    try {
+                        annotationProcessor.processAnnotation(annotation, property, owner.getBeanClass(),
+                            new PropertyAccess(owner.getBeanClass(), property.getName()), new AppendValidationToMeta(
+                                property));
+                    } catch (ValidationException e) {
+                        throw e;
+                    } catch (Exception e) {
+                        throw new ValidationException(e);
+                    }
+                } // else ignore
+            } else {
+                Collection<Annotation> constraints = DynamicModelManager.get().getRequiredDynamicConstraints(meta);
+                ConstraintAppender.FACTORY.constraintAppender(annotation).append(annotation, constraints);
+            }
+        } finally {
+            if (setManager) {
+                DynamicModelManager.THREAD_BOUND_MANAGER.remove();
+            }
+        }
+    }
+
+    /**
+     * Find an available cached result.
+     * 
+     * @param typedPaths
+     * @param initial
+     *            MetaBean from the (factory-level) "static" cache
+     * @return MetaBean
+     */
+    private MetaBean findCachedMetaBean(List<Pair<? extends PathImpl, Class<?>>> typedPaths, MetaBean initial) {
+        Iterator<MetaBean> iter = new LeafIterator(typedPaths, /* findCacheMeta: */true);
+        if (iter.hasNext()) {
+            // cached payload guaranteed not to be null:
+            MetaBean result = iter.next();
+            // check timestamps, first vs. initial:
+            if (Timestamped.Utils.COMPARATOR.compare(result, initial) >= 0) {
+                // checking against entire timestamped context may provide a shortcut:
+                if (Timestamped.Utils.COMPARATOR.compare(result, timeContext) < 0) {
+                    // check all timestamped constituent metadata:
+                    while (iter.hasNext()) {
+                        if (Timestamped.Utils.COMPARATOR.compare(result, iter.next()) < 0) {
+                            return null;
+                        }
+                    }
+                }
+                return result;
+            }
+        }
+        return null;
+    }
+
     private List<MetaBean> getAssignableDynamicInfo(Class<?> type) {
         List<Class<?>> classSequence = new ArrayList<Class<?>>();
         ClassHelper.fillFullClassHierarchyAsList(classSequence, type);
@@ -464,9 +712,14 @@ final class DynamicMetaGraphManagerImpl 
         return metaBean;
     }
 
-    /*
-     * Build up a list of pairs of path : class representing each object in the currently known path,
-     * i.e. what we're trying to complete the bean for.
+    /**
+     * Build up a list of pairs of path : class representing each object in the currently known path, i.e. what we're
+     * trying to complete the bean for.
+     * 
+     * @param fullPath
+     * @param rootBean
+     * @param rootType
+     * @return List in descending order of path length
      */
     private static List<Pair<? extends PathImpl, Class<?>>> getTypedPaths(final PathImpl fullPath, Object rootBean,
         Class<?> rootType) {
@@ -476,7 +729,8 @@ final class DynamicMetaGraphManagerImpl 
             nodes.add(node);
         }
 
-        List<Pair<? extends PathImpl, Class<?>>> result = new ArrayList<Pair<? extends PathImpl, Class<?>>>();
+        List<Pair<? extends PathImpl, Class<?>>> result =
+            new ArrayList<Pair<? extends PathImpl, Class<?>>>((int) (nodes.size() * 1.5));
 
         Class<?> rawType = rootType;
         Type type = rawType;
@@ -510,7 +764,7 @@ final class DynamicMetaGraphManagerImpl 
                 type = access.getJavaType();
                 if (node.getName() != null) {
                     // substitute a name-only node and repeat the loop:
-                    nodes.add(0, new NodeImpl(node.getName()));
+                    nodes.addFirst(new NodeImpl(node.getName()));
                 }
             } else if (node.getName() != null) {
                 PropertyAccess access = new PropertyAccess(rawType, node.getName());

Copied: incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicModelManager.java (from r1069161, incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicModel.java)
URL: http://svn.apache.org/viewvc/incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicModelManager.java?p2=incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicModelManager.java&p1=incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicModel.java&r1=1069161&r2=1069539&rev=1069539&view=diff
==============================================================================
--- incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicModel.java (original)
+++ incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicModelManager.java Thu Feb 10 19:37:27 2011
@@ -16,7 +16,8 @@
  */
 package org.apache.bval.jsr303.dynamic;
 
-import static org.apache.bval.jsr303.dynamic.DynamicModel.Features.*;
+import static org.apache.bval.jsr303.dynamic.DynamicModelManager.Features.DYNAMIC_CONSTRAINT_COLLECTION;
+import static org.apache.bval.jsr303.dynamic.DynamicModelManager.Features.META_CONTAINER;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Array;
@@ -45,11 +46,12 @@ import org.apache.bval.util.KeyedAccess;
 import org.apache.bval.util.PropertyAccess;
 
 /**
- * Utilities for working with the core + dynamic meta-model classes.
+ * Utility class for working with the core + dynamic meta-model classes.
  * 
  * @version $Rev$ $Date$
  */
-public class DynamicModel {
+public class DynamicModelManager {
+
     /**
      * Feature keys.
      */
@@ -64,17 +66,37 @@ public class DynamicModel {
 
     }
 
-    private DynamicModel() {
+    /** static instance */
+    private static final DynamicModelManager INSTANCE = new DynamicModelManager();
 
+    /**
+     * Get the available {@link DynamicModelManager} instance.
+     * 
+     * @return {@link DynamicModelManager}
+     */
+    public static DynamicModelManager get() {
+        DynamicModelManager result = THREAD_BOUND_MANAGER.get();
+        return result == null ? DynamicModelManager.INSTANCE : result;
     }
 
     /**
+     * Only subclasses may call the constructor.
+     */
+    protected DynamicModelManager() {
+    }
+
+    /**
+     * {@link ThreadLocal} to hold a {@link DynamicModelManager} instance.
+     */
+    static final ThreadLocal<DynamicModelManager> THREAD_BOUND_MANAGER = new ThreadLocal<DynamicModelManager>();
+
+    /**
      * Copy source to target MetaBeans.
      * 
      * @param target
      * @param source
      */
-    public static void copy(MetaBean target, MetaBean source) {
+    public void copy(MetaBean target, MetaBean source) {
         if (source == null) {
             return;
         }
@@ -92,17 +114,15 @@ public class DynamicModel {
      * @param name
      * @return MetaProperty
      */
-    public static MetaProperty getRequiredProperty(MetaBean metaBean, String name) {
+    public MetaProperty getRequiredProperty(MetaBean metaBean, String name) {
         MetaProperty result = metaBean.getProperty(name);
         if (result == null) {
-            result = new MetaProperty();
+            result = createMetaProperty(metaBean);
             result.setName(name);
             result.setType(new PropertyAccess(metaBean.getBeanClass(), name).getJavaType());
-            if (result.getMetaBean() == null) {
-                MetaBean propertyBean = new MetaBean();
-                propertyBean.setBeanClass(result.getTypeClass());
-                result.setMetaBean(propertyBean);
-            }
+            MetaBean propertyBean = createMetaBean();
+            propertyBean.setBeanClass(result.getTypeClass());
+            result.setMetaBean(propertyBean);
             metaBean.putProperty(name, result);
         }
         return result;
@@ -116,14 +136,15 @@ public class DynamicModel {
      * @throws IllegalArgumentException
      *             if the property doesn't seem to refer to a container
      */
-    public static MetaContainer<?> getRequiredContainer(FeaturesCapable meta) {
+    public MetaContainer<?> getRequiredContainer(FeaturesCapable meta) {
         MetaContainer<?> result = meta.getFeature(META_CONTAINER);
-        Type type = meta instanceof MetaProperty ? ((MetaProperty) meta).getType() : ((MetaBean) meta).getBeanClass();
         if (result == null) {
+            Type type =
+                meta instanceof MetaProperty ? ((MetaProperty) meta).getType() : ((MetaBean) meta).getBeanClass();
             if (KeyedAccess.getJavaElementType(type) != null) {
-                result = new MetaMap(type);
+                result = createKeyedContainer(type);
             } else if (IndexedAccess.getJavaElementType(type) != null) {
-                result = new MetaCollection(type);
+                result = createIndexedContainer(type);
             } else {
                 throw new IllegalArgumentException(String.format("don't know how to make a container of %s", meta));
             }
@@ -138,7 +159,7 @@ public class DynamicModel {
      * @param target
      * @param source
      */
-    public static void copy(MetaProperty target, MetaProperty source) {
+    public void copy(MetaProperty target, MetaProperty source) {
         mergeFeatures(target, source);
 
         if (KeyedAccess.getJavaElementType(source.getType()) != null
@@ -173,13 +194,13 @@ public class DynamicModel {
     }
 
     /**
-     * Typesafe helper method.
+     * Copy {@link MetaContainer}s.
      * 
      * @param <K>
      * @param targetContainer
      * @param sourceContainer
      */
-    private static <K> void copy(MetaContainer<K> targetContainer, MetaContainer<K> sourceContainer) {
+    protected <K> void copy(MetaContainer<K> targetContainer, MetaContainer<K> sourceContainer) {
         copy(targetContainer.getPrototype(), sourceContainer.getPrototype());
         for (Map.Entry<K, MetaBean> e : sourceContainer.getElements().entrySet()) {
             copy(targetContainer.getElement(e.getKey()), e.getValue());
@@ -192,10 +213,10 @@ public class DynamicModel {
      * @param meta
      * @return Collection<Annotation>
      */
-    public static Collection<Annotation> getRequiredDynamicConstraints(FeaturesCapable meta) {
+    public Collection<Annotation> getRequiredDynamicConstraints(FeaturesCapable meta) {
         Collection<Annotation> result = meta.getFeature(DYNAMIC_CONSTRAINT_COLLECTION);
         if (result == null) {
-            result = Collections.synchronizedList(new ArrayList<Annotation>());
+            result = createDynamicConstraintsCollection(meta);
             meta.optimizeRead(false);
             meta.putFeature(DYNAMIC_CONSTRAINT_COLLECTION, result);
             meta.optimizeRead(true);
@@ -209,9 +230,9 @@ public class DynamicModel {
      * @param target
      * @param source
      */
-    public static void mergeFeatures(FeaturesCapable target, FeaturesCapable source) {
+    public void mergeFeatures(FeaturesCapable target, FeaturesCapable source) {
         for (Map.Entry<String, Object> e : source.getFeatures().entrySet()) {
-            if (DYNAMIC_CONSTRAINT_COLLECTION.equals(e.getKey())) { 
+            if (DYNAMIC_CONSTRAINT_COLLECTION.equals(e.getKey())) {
                 mergeConstraints(target, source);
             } else if (META_CONTAINER.contains(e.getKey())) {
                 continue;
@@ -222,29 +243,43 @@ public class DynamicModel {
         }
     }
 
-    private static <T> T mergeFeatureValues(T target, T source) {
-        if (target == null || source == null) {
-            return source;
-        }
-        if (source.getClass().isArray() && !source.getClass().getComponentType().isPrimitive()) {
-            @SuppressWarnings("unchecked")
-            T result = (T) mergeArrays((Object[]) target, (Object[]) source);
-            return result;
-        }
-        if (source instanceof Collection<?> && target instanceof Collection<?>) {
-            @SuppressWarnings({ "unchecked", "rawtypes" })
-            T result = (T) mergeCollections((Collection) target, (Collection) source);
-            return result;
-        }
-        if (source instanceof Map<?, ?> && target instanceof Map<?, ?>) {
-            @SuppressWarnings({ "unchecked", "rawtypes" })
-            T result = (T) mergeMaps((Map) target, (Map) source);
-            return result;
+    /**
+     * Merge feature values.
+     * 
+     * @param <T>
+     * @param target
+     * @param source
+     * @return T
+     */
+    protected <T> T mergeFeatureValues(T target, T source) {
+        if (target != null && source != null) {
+            if (source.getClass().isArray() && !source.getClass().getComponentType().isPrimitive()) {
+                @SuppressWarnings("unchecked")
+                T result = (T) mergeArrays((Object[]) target, (Object[]) source);
+                return result;
+            }
+            if (source instanceof Collection<?> && target instanceof Collection<?>) {
+                @SuppressWarnings({ "unchecked", "rawtypes" })
+                T result = (T) mergeCollections((Collection) target, (Collection) source);
+                return result;
+            }
+            if (source instanceof Map<?, ?> && target instanceof Map<?, ?>) {
+                @SuppressWarnings({ "unchecked", "rawtypes" })
+                T result = (T) mergeMaps((Map) target, (Map) source);
+                return result;
+            }
         }
         return source;
     }
 
-    private static boolean mergeConstraints(FeaturesCapable target, FeaturesCapable source) {
+    /**
+     * Merge {@link FeaturesCapable}-held bean validation constraints.
+     * 
+     * @param target
+     * @param source
+     * @return whether a change resulted
+     */
+    protected boolean mergeConstraints(FeaturesCapable target, FeaturesCapable source) {
         Collection<Annotation> targetConstraints = target.getFeature(DYNAMIC_CONSTRAINT_COLLECTION);
         Collection<Annotation> sourceConstraints = source.getFeature(DYNAMIC_CONSTRAINT_COLLECTION);
         if (sourceConstraints == null || sourceConstraints.isEmpty()) {
@@ -274,7 +309,15 @@ public class DynamicModel {
         return result;
     }
 
-    private static <T> T[] mergeArrays(T[] target, T[] source) {
+    /**
+     * Merge array elements.
+     * 
+     * @param <T>
+     * @param target
+     * @param source
+     * @return merged array
+     */
+    protected <T> T[] mergeArrays(T[] target, T[] source) {
         LinkedHashSet<T> targetSet = new LinkedHashSet<T>();
         Collections.addAll(targetSet, target);
         Collection<T> coll = mergeCollections(targetSet, Arrays.asList(source));
@@ -283,7 +326,15 @@ public class DynamicModel {
         return coll.toArray(result);
     }
 
-    private static <E> Collection<E> mergeCollections(Collection<? extends E> target, Collection<? extends E> source) {
+    /**
+     * Merge collections.
+     * 
+     * @param <E>
+     * @param target
+     * @param source
+     * @return interface-compatible collection
+     */
+    protected <E> Collection<E> mergeCollections(Collection<? extends E> target, Collection<? extends E> source) {
         Collection<E> result;
         if (target instanceof SortedSet<?>) {
             @SuppressWarnings("unchecked")
@@ -299,7 +350,16 @@ public class DynamicModel {
         return result;
     }
 
-    private static <K, V> Map<K, V> mergeMaps(Map<? extends K, ? extends V> target, Map<? extends K, ? extends V> source) {
+    /**
+     * Merge maps.
+     * 
+     * @param <K>
+     * @param <V>
+     * @param target
+     * @param source
+     * @return interface-compatible map
+     */
+    protected <K, V> Map<K, V> mergeMaps(Map<? extends K, ? extends V> target, Map<? extends K, ? extends V> source) {
         Map<K, V> result;
         if (target instanceof SortedMap<?, ?>) {
             @SuppressWarnings("unchecked")
@@ -315,4 +375,40 @@ public class DynamicModel {
         }
         return result;
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected MetaProperty createMetaProperty(MetaBean owner) {
+        return new MetaProperty();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected MetaBean createMetaBean() {
+        return new MetaBean();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected MetaContainer<Object> createKeyedContainer(Type type) {
+        return new MetaMap(type);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected MetaContainer<Integer> createIndexedContainer(Type type) {
+        return new MetaCollection(type);
+    }
+
+    /**
+     * 
+     * {@inheritDoc}
+     */
+    protected Collection<Annotation> createDynamicConstraintsCollection(FeaturesCapable owner) {
+        return Collections.synchronizedList(new ArrayList<Annotation>());
+    }
 }

Modified: incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicValidatorContext.java
URL: http://svn.apache.org/viewvc/incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicValidatorContext.java?rev=1069539&r1=1069538&r2=1069539&view=diff
==============================================================================
--- incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicValidatorContext.java (original)
+++ incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicValidatorContext.java Thu Feb 10 19:37:27 2011
@@ -16,13 +16,8 @@
  */
 package org.apache.bval.jsr303.dynamic;
 
-import static org.apache.bval.jsr303.dynamic.DynamicModel.*;
-
 import java.lang.annotation.Annotation;
-import java.util.Collection;
 
-import javax.validation.Valid;
-import javax.validation.ValidationException;
 import javax.validation.Validator;
 import javax.validation.ValidatorContext;
 
@@ -32,16 +27,11 @@ import org.apache.bval.MetaBeanFinder;
 import org.apache.bval.MetaBeanManager;
 import org.apache.bval.jsr303.AnnotationProcessor;
 import org.apache.bval.jsr303.ApacheFactoryContext;
-import org.apache.bval.jsr303.AppendValidationToMeta;
-import org.apache.bval.model.FeaturesCapable;
 import org.apache.bval.model.MetaBean;
-import org.apache.bval.model.MetaProperty;
-import org.apache.bval.util.PropertyAccess;
 
 /**
- * {@link ValidatorContext} implementation supporting context-bound dynamic
- * constraints.
- *
+ * {@link ValidatorContext} implementation supporting context-bound dynamic constraints.
+ * 
  * @version $Rev$ $Date$
  */
 public class DynamicValidatorContext extends ApacheFactoryContext implements DynamicMetaGraphManager {
@@ -54,6 +44,7 @@ public class DynamicValidatorContext ext
 
         /**
          * Create a new DynamicMetaBeanFinder instance.
+         * 
          * @param staticFinder
          */
         public DynamicMetaBeanFinder(MetaBeanFinder staticFinder) {
@@ -82,17 +73,15 @@ public class DynamicValidatorContext ext
     }
 
     private final DynamicMetaGraphManagerImpl metaGraphManager;
-    private final AnnotationProcessor annotationProcessor;
 
     /**
      * Create a new {@link DynamicValidatorContext} instance.
-     *
+     * 
      * @param validatorFactory
      */
     public DynamicValidatorContext(DynamicValidatorFactory validatorFactory) {
         super(validatorFactory);
         this.metaGraphManager = ((DynamicMetaBeanFinder) getMetaBeanFinder()).metaGraphManager;
-        this.annotationProcessor = ((DynamicMetaBeanFinder) getMetaBeanFinder()).annotationProcessor;
     }
 
     /**
@@ -105,7 +94,7 @@ public class DynamicValidatorContext ext
 
     /**
      * Constrain a bean type at the class level.
-     *
+     * 
      * @param beanType
      * @param constraintCollectionManipulator
      * @return <code>this</code> following the chained invocation pattern
@@ -117,32 +106,14 @@ public class DynamicValidatorContext ext
 
     /**
      * Constrain a property path relative to a root bean type.
-     *
+     * 
      * @param beanType
      * @param propertyPath
      * @param constraintCollectionManipulator
      * @return <code>this</code> following the chained invocation pattern
      */
     public DynamicValidatorContext constrain(Class<?> beanType, String propertyPath, Annotation annotation) {
-        FeaturesCapable meta = writable().getMeta(beanType, propertyPath);
-        if (annotation.annotationType().equals(Valid.class)) {
-            if (meta instanceof MetaProperty) {
-                MetaProperty property = (MetaProperty) meta;
-                MetaBean owner = property.getParentMetaBean();
-                try {
-                    annotationProcessor.processAnnotation(annotation, property, owner.getBeanClass(),
-                        new PropertyAccess(owner.getBeanClass(), property.getName()), new AppendValidationToMeta(
-                            property));
-                } catch (ValidationException e) {
-                    throw e;
-                } catch (Exception e) {
-                    throw new ValidationException(e);
-                }
-            } // else ignore
-        } else {
-            Collection<Annotation> constraints = getRequiredDynamicConstraints(meta);
-            ConstraintAppender.FACTORY.constraintAppender(annotation).append(annotation, constraints);
-        }
+        metaGraphManager.constrain(beanType, propertyPath, annotation);
         return this;
     }
 
@@ -184,10 +155,9 @@ public class DynamicValidatorContext ext
     }
 
     /**
-     * Register a {@link DynamicValidationState} instance to the current thread.
-     * Etiquette requires the caller to clear the instance when no longer
-     * needed.
-     *
+     * Register a {@link DynamicValidationState} instance to the current thread. Etiquette requires the caller to clear
+     * the instance when no longer needed.
+     * 
      * @param validationState
      * @see #clearValidationState()
      */

Modified: incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicValidatorFactory.java
URL: http://svn.apache.org/viewvc/incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicValidatorFactory.java?rev=1069539&r1=1069538&r2=1069539&view=diff
==============================================================================
--- incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicValidatorFactory.java (original)
+++ incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/DynamicValidatorFactory.java Thu Feb 10 19:37:27 2011
@@ -16,37 +16,157 @@
  */
 package org.apache.bval.jsr303.dynamic;
 
+import java.util.List;
+import java.util.Map;
+
 import javax.validation.spi.ConfigurationState;
 
+import org.apache.bval.MetaBeanBuilder;
+import org.apache.bval.MetaBeanCache;
+import org.apache.bval.MetaBeanFactory;
 import org.apache.bval.MetaBeanFinder;
+import org.apache.bval.MetaBeanManager;
+import org.apache.bval.jsr303.ApacheFactoryContext;
 import org.apache.bval.jsr303.ApacheValidatorFactory;
+import org.apache.bval.jsr303.xml.MetaConstraint;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.util.AccessStrategy;
 
 /**
- * {@link ApacheValidatorFactory} extension that creates a
- * {@link DynamicValidatorContext}.
- *
+ * {@link ApacheValidatorFactory} extension that creates a {@link DynamicValidatorContext}.
+ * 
  * @version $Rev$ $Date$
  */
 public class DynamicValidatorFactory extends ApacheValidatorFactory {
+    private class TimestampedMetaBeanBuilder extends MetaBeanBuilder {
+        private final MetaBeanBuilder idDelegate;
+
+        /**
+         * Create a new TimestampedMetaBeanBuilder instance.
+         * 
+         * @param idDelegate
+         */
+        private TimestampedMetaBeanBuilder(MetaBeanBuilder idDelegate, MetaBeanFactory[] builders) {
+            super(builders);
+            this.idDelegate = idDelegate;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public Map<String, MetaBean> buildAll() throws Exception {
+            return idDelegate.buildAll();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public MetaBean buildForId(String beanInfoId) throws Exception {
+            return idDelegate.buildForId(beanInfoId);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public MetaBean buildForClass(Class<?> clazz) throws Exception {
+            final MetaBean result = timeContext == null ? new MetaBean() : timeContext.new Bean();
+            if (clazz != null) { // local class here?
+                result.setBeanClass(clazz);
+                result.setId(clazz.getName()); // default id = full class name!
+            }
+            for (MetaBeanFactory factory : getFactories()) {
+                factory.buildMetaBean(result);
+            }
+            return result;
+        }
+    }
+
+    /**
+     * Package-private {@link MetaBeanFinder} instance.
+     */
     final MetaBeanFinder metaBeanFinder;
 
+    private final MetaBeanCache cache;
+    private Timestamped.Context timeContext;
+
     /**
      * Create a new {@link DynamicValidatorFactory} instance.
-     *
+     * 
      * @param configurationState
      */
     public DynamicValidatorFactory(ConfigurationState configurationState) {
         super(configurationState);
-        this.metaBeanFinder = super.usingContext().getMetaBeanFinder();
+
+        metaBeanFinder = new ApacheFactoryContext(this) {
+            /**
+             * {@inheritDoc}
+             */
+            @Override
+            protected MetaBeanFinder createMetaBeanManager(List<MetaBeanFactory> builders) {
+                MetaBeanManager result = (MetaBeanManager) super.createMetaBeanManager(builders);
+                return new MetaBeanManager(new TimestampedMetaBeanBuilder(result.getBuilder(),
+                    builders.toArray(new MetaBeanFactory[builders.size()])));
+            }
+        }.getMetaBeanFinder();
+
+        cache = ((MetaBeanManager) metaBeanFinder).getCache();
     }
 
     /*
      * (non-Javadoc)
-     *
+     * 
      * @see org.apache.bval.jsr303.ApacheValidatorFactory#usingContext()
      */
     @Override
     public DynamicValidatorContext usingContext() {
         return new DynamicValidatorContext(this);
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void addMetaConstraint(Class<?> beanClass, MetaConstraint<?, ?> metaConstraint) {
+        super.addMetaConstraint(beanClass, metaConstraint);
+        invalidateCachedBean(beanClass);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void addValid(Class<?> beanClass, AccessStrategy accessStrategy) {
+        super.addValid(beanClass, accessStrategy);
+        invalidateCachedBean(beanClass);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void addDefaultSequence(Class<?> beanClass, Class<?>[] groupSequence) {
+        super.addDefaultSequence(beanClass, groupSequence);
+        invalidateCachedBean(beanClass);
+    }
+
+    private void invalidateCachedBean(Class<?> beanClass) {
+        MetaBean cached = cache.findForClass(beanClass);
+        if (cached != null) {
+            cache.removeFromCache(cached);
+        } else if (timeContext == null) {
+            //if there is no timeContext, don't create it on account of a noop cache removal:
+            return;
+        }
+        //double read to avoid unnecessary synchronization:
+        if (timeContext == null) {
+            synchronized (this) {
+                if (timeContext == null) {
+                    timeContext = new Timestamped.Context();
+                }
+            }
+        }
+        timeContext.touch();
+    }
 }

Modified: incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/MetaContainer.java
URL: http://svn.apache.org/viewvc/incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/MetaContainer.java?rev=1069539&r1=1069538&r2=1069539&view=diff
==============================================================================
--- incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/MetaContainer.java (original)
+++ incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/MetaContainer.java Thu Feb 10 19:37:27 2011
@@ -16,8 +16,6 @@
  */
 package org.apache.bval.jsr303.dynamic;
 
-import static org.apache.bval.jsr303.dynamic.DynamicModel.*;
-
 import java.lang.reflect.Type;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
@@ -31,10 +29,14 @@ import org.apache.commons.lang3.reflect.
  * @version $Rev$ $Date$
  */
 public abstract class MetaContainer<K> {
-    private final Class<K> idType;
-    private final Type elementType;
-    private final MetaBean prototype;
-    private final ConcurrentMap<K, MetaBean> elements = new ConcurrentHashMap<K, MetaBean>();
+    /** id type */
+    protected final Class<K> idType;
+    /** element type */
+    protected final Type elementType;
+    /** prototype MetaBean */
+    protected final MetaBean prototype;
+    /** non-prototype element MetaBean map */
+    protected final ConcurrentMap<K, MetaBean> elements = new ConcurrentHashMap<K, MetaBean>();
 
     /**
      * Create a new MetaContainer instance.
@@ -135,7 +137,7 @@ public abstract class MetaContainer<K> {
     protected MetaBean merge(MetaBean... sources) {
         MetaBean result = new MetaBean();
         for (MetaBean source : sources) {
-            copy(result, source);
+            DynamicModelManager.get().copy(result, source);
         }
         return result;
     }

Added: incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/Timestamped.java
URL: http://svn.apache.org/viewvc/incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/Timestamped.java?rev=1069539&view=auto
==============================================================================
--- incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/Timestamped.java (added)
+++ incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/Timestamped.java Thu Feb 10 19:37:27 2011
@@ -0,0 +1,799 @@
+/*
+ *  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.bval.jsr303.dynamic;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Type;
+import java.util.AbstractSet;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+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.bval.model.FeaturesCapable;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.model.Validation;
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.ObjectUtils;
+
+/**
+ * Timestamped interface.
+ * 
+ * @version $Rev$ $Date$
+ */
+interface Timestamped {
+
+    /**
+     * Touch.
+     */
+    void touch();
+
+    /**
+     * Get the timestamp.
+     * 
+     * @return long
+     */
+    long millis();
+
+    /**
+     * {@link ConcurrentMap} implementation that is aware of an owning {@link Timestamped} object.
+     * 
+     * @param <K>
+     * @param <V>
+     */
+    static class OwnedMap<K, V> extends ConcurrentHashMap<K, V> {
+        private static final long serialVersionUID = 1L;
+        static final List<String> COLLECTION_VIEW_MODIFIER_METHODS = Collections.unmodifiableList(Arrays.asList(
+            "remove", "removeAll", "retainAll"));
+
+        private class CollectionViewInvocationHandler implements InvocationHandler {
+
+            final Collection<?> coll;
+
+            /**
+             * Create a new CollectionViewInvocationHandler instance.
+             * 
+             * @param coll
+             */
+            private CollectionViewInvocationHandler(Collection<?> coll) {
+                super();
+                this.coll = coll;
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                Object result = method.invoke(coll, args);
+                if ("iterator".equals(method.getName())) {
+                    result =
+                        Proxy
+                            .newProxyInstance(Thread.currentThread().getContextClassLoader(), getAllInterfaces(result),
+                                new CollectionViewIteratorInvocationHandler((Iterator<?>) result));
+                } else if (COLLECTION_VIEW_MODIFIER_METHODS.contains(method.getName())) {
+                    fireChange(((Boolean) result).booleanValue());
+                }
+                return result;
+            }
+        }
+
+        private class CollectionViewIteratorInvocationHandler implements InvocationHandler {
+            final Iterator<?> iter;
+
+            /**
+             * Create a new CollectionViewIteratorInvocationHandler instance.
+             * 
+             * @param iter
+             */
+            private CollectionViewIteratorInvocationHandler(Iterator<?> iter) {
+                super();
+                this.iter = iter;
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                try {
+                    return method.invoke(iter, args);
+                } finally {
+                    fireChange("remove".equals(method.getName()));
+                }
+            }
+        }
+
+        private final Timestamped owner;
+
+        /**
+         * Create a new OwnedMap instance.
+         * 
+         * @param owner
+         */
+        OwnedMap(Timestamped owner) {
+            super();
+            this.owner = owner;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public V put(K key, V value) {
+            V result = super.put(key, value);
+            fireChange(!ObjectUtils.equals(result, value));
+            return result;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public void clear() {
+            boolean fire = !isEmpty();
+            super.clear();
+            fireChange(fire);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public void putAll(Map<? extends K, ? extends V> m) {
+            boolean fire = this.equals(m);
+            super.putAll(m);
+            fireChange(fire);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public V putIfAbsent(K key, V value) {
+            V result = super.putIfAbsent(key, value);
+            fireChange(result == null);
+            return result;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public V remove(Object key) {
+            boolean fire = this.containsKey(key);
+            try {
+                return super.remove(key);
+            } finally {
+                fireChange(fire);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public boolean remove(Object key, Object value) {
+            boolean result = super.remove(key, value);
+            fireChange(result);
+            return result;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean replace(K key, V oldValue, V newValue) {
+            boolean result = super.replace(key, oldValue, newValue);
+            fireChange(result);
+            return result;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public V replace(K key, V value) {
+            V result = super.replace(key, value);
+            fireChange(result != null);
+            return result;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public Set<K> keySet() {
+            return proxyCollection(super.keySet());
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public Collection<V> values() {
+            return proxyCollection(super.values());
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public Set<java.util.Map.Entry<K, V>> entrySet() {
+            return proxyCollection(super.entrySet());
+        }
+
+        private void fireChange(boolean test) {
+            if (test) {
+                owner.touch();
+            }
+        }
+
+        @SuppressWarnings("unchecked")
+        private <T extends Collection<?>> T proxyCollection(T coll) {
+            return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), getAllInterfaces(coll),
+                new CollectionViewInvocationHandler(coll));
+        }
+
+        private static Class<?>[] getAllInterfaces(Object o) {
+            List<Class<?>> ifaces = ClassUtils.getAllInterfaces(o.getClass());
+            return ifaces.toArray(new Class[ifaces.size()]);
+        }
+
+    }
+
+    /**
+     * {@link Timestamped}-aware {@link Set} implementation, backed by {@link OwnedMap}.
+     * 
+     * @param <T>
+     * 
+     * @version $Rev$ $Date$
+     */
+    static class OwnedSet<T> extends AbstractSet<T> {
+        private final OwnedMap<Object, Object> map;
+
+        /**
+         * Create a new OwnedSet instance.
+         * 
+         * @param owner
+         */
+        OwnedSet(Timestamped owner) {
+            super();
+            this.map = new OwnedMap<Object, Object>(owner);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public boolean add(T e) {
+            return map.putIfAbsent(ObjectUtils.defaultIfNull(e, ObjectUtils.NULL), ObjectUtils.NULL) == null;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public boolean remove(Object o) {
+            return super.remove(ObjectUtils.defaultIfNull(o, ObjectUtils.NULL));
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public void clear() {
+            map.clear();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public boolean contains(Object o) {
+            return map.containsKey(ObjectUtils.defaultIfNull(o, ObjectUtils.NULL));
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public Iterator<T> iterator() {
+            final Iterator<Object> wrapped = map.keySet().iterator();
+            return new Iterator<T>() {
+
+                public boolean hasNext() {
+                    return wrapped.hasNext();
+                }
+
+                @SuppressWarnings("unchecked")
+                public T next() {
+                    Object next = wrapped.next();
+                    return next == ObjectUtils.NULL ? null : (T) next;
+                }
+
+                public void remove() {
+                    wrapped.remove();
+                }
+
+            };
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public int size() {
+            return map.size();
+        }
+
+    }
+
+    /**
+     * Timestamped Context.
+     */
+    static class Context extends DynamicModelManager implements Timestamped {
+        volatile long millis = System.currentTimeMillis();
+
+        /**
+         * Wrap an object's interface-based calls to bind our {@link MetaCreator} instance on the executing thread.
+         * 
+         * @param target
+         * @return proxy
+         */
+        @SuppressWarnings("unchecked")
+        <T> T wrap(final T target) {
+            final List<Class<?>> ifaces = ClassUtils.getAllInterfaces(target.getClass());
+            return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
+                ifaces.toArray(new Class[ifaces.size()]), new InvocationHandler() {
+
+                    /**
+                     * {@inheritDoc}
+                     */
+                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                        final boolean setManager = DynamicModelManager.THREAD_BOUND_MANAGER.get() == null;
+
+                        if (setManager) {
+                            DynamicModelManager.THREAD_BOUND_MANAGER.set(Context.this);
+                        }
+                        try {
+                            return method.invoke(target, args);
+                        } catch (InvocationTargetException e) {
+                            throw e.getTargetException();
+                        } finally {
+                            if (setManager) {
+                                DynamicModelManager.THREAD_BOUND_MANAGER.remove();
+                            }
+                        }
+                    }
+
+                });
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        protected MetaProperty createMetaProperty(MetaBean owner) {
+            return owner instanceof Bean ? ((Bean) owner).new Property() : super.createMetaProperty(owner);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        protected MetaBean createMetaBean() {
+            return new Bean();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        protected MetaContainer<Object> createKeyedContainer(Type type) {
+            return new AwareMap(type);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        protected MetaContainer<Integer> createIndexedContainer(Type type) {
+            return new AwareCollection(type);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        protected Collection<Annotation> createDynamicConstraintsCollection(FeaturesCapable owner) {
+            Timestamped t = null;
+            if (owner instanceof Timestamped) {
+                t = (Timestamped) owner;
+            } else if (owner instanceof MetaProperty) {
+                MetaBean parent = ((MetaProperty) owner).getParentMetaBean();
+                if (parent instanceof Timestamped) {
+                    t = (Timestamped) parent;
+                }
+            }
+            return t == null ? super.createDynamicConstraintsCollection(owner) : Collections
+                .synchronizedSet(new OwnedSet<Annotation>(t));
+        }
+
+        /**
+         * {@link Timestamped} {@link MetaBean}.
+         */
+        class Bean extends MetaBean implements Timestamped, CacheMeta<MetaBean> {
+
+            private static final long serialVersionUID = 1L;
+
+            /**
+             * {@link Timestamped} {@link MetaProperty}.
+             */
+            class Property extends MetaProperty implements Timestamped {
+
+                private static final long serialVersionUID = 1L;
+
+                private volatile long millis;
+
+                {
+                    touch();
+                }
+
+                /**
+                 * {@inheritDoc}
+                 */
+                public void touch() {
+                    Bean.this.touch();
+                    this.millis = Bean.this.millis;
+                }
+
+                /**
+                 * {@inheritDoc}
+                 */
+                public long millis() {
+                    return millis;
+                }
+
+                /**
+                 * {@inheritDoc}
+                 */
+                @Override
+                protected ConcurrentMap<String, Object> createFeaturesMap() {
+                    return new Timestamped.OwnedMap<String, Object>(this);
+                }
+
+            }
+
+            private volatile long millis;
+
+            private transient MetaBean cached;
+
+            {
+                touch();
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            public long millis() {
+                return millis;
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            public void touch() {
+                Context.this.touch();
+                this.millis = Context.this.millis;
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            public void cache(MetaBean meta) {
+                this.cached = meta;
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            public MetaBean getCached() {
+                return cached;
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            @Override
+            public void putProperty(String name, MetaProperty property) {
+                super.putProperty(name, property);
+                touch();
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            @Override
+            public void setValidations(Validation[] validations) {
+                super.setValidations(validations);
+                touch();
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            @Override
+            public void setProperties(MetaProperty[] properties) {
+                super.setProperties(properties);
+                touch();
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            @Override
+            protected ConcurrentMap<String, Object> createFeaturesMap() {
+                return new Timestamped.OwnedMap<String, Object>(this);
+            }
+
+        }
+
+        /**
+         * Merging {@link MetaContainer} {@link MetaBean}.
+         */
+        static class InIterableBean extends MetaBean implements Timestamped, CacheMeta<MetaBean> {
+
+            private static final long serialVersionUID = 1L;
+
+            private long millis;
+
+            /**
+             * Most-specific source {@link MetaBean}.
+             */
+            private final Bean bean;
+
+            /**
+             * Create a new Timestamped.Context.MergeBean instance.
+             */
+            public InIterableBean(Bean cacheMeta) {
+                this.bean = cacheMeta;
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            public void cache(MetaBean meta) {
+                bean.cache(meta);
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            public MetaBean getCached() {
+                return bean.getCached();
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            public void touch() {
+                this.millis = System.currentTimeMillis();
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            public long millis() {
+                return millis;
+            }
+
+        }
+
+        /**
+         * Helper for Timestamped Context aware meta-containers.
+         */
+        static class AwareContainerHelper {
+            private static final Object PROTOTYPE_KEY = new Object();
+
+            private final ConcurrentMap<Object, InIterableBean> cache =
+                new ConcurrentHashMap<Object, Timestamped.Context.InIterableBean>();
+
+            /**
+             * Learn whether a given target item is up to date with regard to a set of source items.
+             * 
+             * @param target
+             * @param sources
+             * @return boolean
+             */
+            static boolean isUpToDate(Object target, Object... sources) {
+                for (Object source : sources) {
+                    if (Timestamped.Utils.COMPARATOR.compare(target, source) < 0) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+
+            /**
+             * Get the merged {@link InIterableBean} representing a particular contained item.
+             * 
+             * @param id
+             * @param sources
+             * @return {@link InIterableBean}
+             */
+            InIterableBean collectionMerge(Object id, MetaBean... sources) {
+                if (sources.length == 1) {
+                    id = PROTOTYPE_KEY;
+                }
+                assert id != null;
+                if (cache.containsKey(id)) {
+                    InIterableBean cached = cache.get(id);
+                    if (isUpToDate(cached, (Object[]) sources)) {
+                        return cached;
+                    }
+                }
+
+                int last = sources.length - 1;
+                Bean cacheHolder = (Bean) sources[last];
+
+                InIterableBean result = new InIterableBean(cacheHolder);
+                for (MetaBean source : sources) {
+                    DynamicModelManager.get().copy(result, source);
+                }
+                cache.put(id, result);
+                return result;
+            }
+
+        }
+
+        /**
+         * Timestamped context-aware {@link MetaCollection}.
+         */
+        class AwareCollection extends MetaCollection {
+            private final AwareContainerHelper helper = new AwareContainerHelper();
+
+            /**
+             * Create a new AwareCollection instance.
+             * 
+             * @param type
+             */
+            public AwareCollection(Type type) {
+                super(type);
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            @Override
+            public MetaBean getMergedElement(Integer id) {
+                return id != null && elements.containsKey(id) ? helper.collectionMerge(id, prototype, elements.get(id))
+                    : helper.collectionMerge(id, prototype);
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            @Override
+            protected MetaBean createMetaBean() {
+                return Context.this.new Bean();
+            }
+
+        }
+
+        /**
+         * Timestamped context-aware {@link MetaMap}.
+         */
+        class AwareMap extends MetaMap {
+            private final AwareContainerHelper helper = new AwareContainerHelper();
+
+            /**
+             * Create a new AwareMap instance.
+             * 
+             * @param type
+             */
+            public AwareMap(Type type) {
+                super(type);
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            @Override
+            public MetaBean getMergedElement(Object id) {
+                id = ObjectUtils.defaultIfNull(id, ObjectUtils.NULL);
+                return elements.containsKey(id) ? helper.collectionMerge(id, prototype, elements.get(id)) : helper
+                    .collectionMerge(id, prototype);
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            @Override
+            protected MetaBean createMetaBean() {
+                return Context.this.new Bean();
+            }
+
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void touch() {
+            this.millis = System.currentTimeMillis();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public long millis() {
+            return millis;
+        }
+
+    }
+
+    /**
+     * {@link Timestamped} utility methods.
+     */
+    public static class Utils {
+        /**
+         * Timestamp-based comparator.
+         * 
+         * @see #read(Object)
+         */
+        public static final Comparator<Object> COMPARATOR = new Comparator<Object>() {
+
+            /**
+             * {@inheritDoc}
+             * 
+             * Return negative/positive/0 as <code>o1</code> is older than/newer than/of equal age as <code>o2</code>.
+             * 
+             * @see Utils#read(Object)
+             * 
+             * @param o1
+             * @param o2
+             * @return int comparison
+             */
+            public int compare(Object o1, Object o2) {
+                long l1 = read(o1);
+                long l2 = read(o2);
+                return l1 == l2 ? 0 : l1 < l2 ? -1 : 1;
+            }
+        };
+
+        /**
+         * Stamp a {@link Timestamped} argument.
+         * 
+         * @param o
+         */
+        public static void stamp(Object o) {
+            if (o instanceof Timestamped) {
+                ((Timestamped) o).touch();
+            }
+        }
+
+        /**
+         * Get a usable timestamp value.
+         * 
+         * @param o
+         * @return for {@link Timestamped}, {@link Timestamped#millis()}; else {@link Long#MIN_VALUE}
+         */
+        public static long read(Object o) {
+            return o instanceof Timestamped ? ((Timestamped) o).millis() : Long.MIN_VALUE;
+        }
+    }
+}

Propchange: incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/Timestamped.java
------------------------------------------------------------------------------
    svn:eol-style = native