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