org.apache.bval.jsr303.ApacheValidatorFactory Maven / Gradle / Ivy
/*
* 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;
import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.validation.ConstraintValidatorFactory;
import javax.validation.MessageInterpolator;
import javax.validation.TraversableResolver;
import javax.validation.Validation;
import javax.validation.ValidationException;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.spi.ConfigurationState;
import org.apache.bval.jsr303.xml.AnnotationIgnores;
import org.apache.bval.jsr303.xml.MetaConstraint;
import org.apache.bval.jsr303.xml.ValidationMappingParser;
import org.apache.bval.util.AccessStrategy;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.ClassUtils;
/**
* Description: a factory is a complete configurated object that can create
* validators.
* This instance is not thread-safe.
*/
public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
private static volatile ApacheValidatorFactory DEFAULT_FACTORY;
private static final ConstraintDefaults defaultConstraints =
new ConstraintDefaults();
private MessageInterpolator messageResolver;
private TraversableResolver traversableResolver;
private ConstraintValidatorFactory constraintValidatorFactory;
private final Map properties;
/**
* information from xml parsing
*/
private final AnnotationIgnores annotationIgnores = new AnnotationIgnores();
private final ConstraintCached constraintsCache = new ConstraintCached();
private final Map, Class>[]> defaultSequences;
/**
* access strategies for properties with cascade validation @Valid support
*/
private final Map, List> validAccesses;
private final Map, List>> constraintMap;
/**
* Convenience method to retrieve a default global ApacheValidatorFactory
*
* @return {@link ApacheValidatorFactory}
*/
public static synchronized ApacheValidatorFactory getDefault() {
if (DEFAULT_FACTORY == null) {
DEFAULT_FACTORY =
Validation.byProvider(ApacheValidationProvider.class).configure().buildValidatorFactory()
.unwrap(ApacheValidatorFactory.class);
}
return DEFAULT_FACTORY;
}
/**
* Set a particular {@link ApacheValidatorFactory} instance as the default.
*
* @param aDefaultFactory
*/
public static void setDefault(ApacheValidatorFactory aDefaultFactory) {
DEFAULT_FACTORY = aDefaultFactory;
}
/**
* Create a new ApacheValidatorFactory instance.
*/
public ApacheValidatorFactory(ConfigurationState configurationState) {
properties = new HashMap();
defaultSequences = new HashMap, Class>[]>();
validAccesses = new HashMap, List>();
constraintMap =
new HashMap, List>>();
configure(configurationState);
}
/**
* Configure this {@link ApacheValidatorFactory} from a
* {@link ConfigurationState}.
*
* @param configuration
*/
protected void configure(ConfigurationState configuration) {
getProperties().putAll(configuration.getProperties());
setMessageInterpolator(configuration.getMessageInterpolator());
setTraversableResolver(configuration.getTraversableResolver());
setConstraintValidatorFactory(configuration
.getConstraintValidatorFactory());
ValidationMappingParser parser = new ValidationMappingParser(this);
parser.processMappingConfig(configuration.getMappingStreams());
}
/**
* Get the property map of this {@link ApacheValidatorFactory}.
*
* @return Map
*/
public Map getProperties() {
return properties;
}
/**
* Shortcut method to create a new Validator instance with factory's
* settings
*
* @return the new validator instance
*/
public Validator getValidator() {
return usingContext().getValidator();
}
/**
* {@inheritDoc}
*
* @return the validator factory's context
*/
public ApacheFactoryContext usingContext() {
return new ApacheFactoryContext(this);
}
/**
* {@inheritDoc}
*/
@Override
public synchronized ApacheValidatorFactory clone() {
try {
return (ApacheValidatorFactory) super.clone();
} catch (CloneNotSupportedException e) {
throw new InternalError(); // VM bug.
}
}
/**
* Set the {@link MessageInterpolator} used.
*
* @param messageResolver
*/
public final void setMessageInterpolator(MessageInterpolator messageResolver) {
this.messageResolver = messageResolver;
}
/**
* {@inheritDoc}
*/
public MessageInterpolator getMessageInterpolator() {
return messageResolver;
}
/**
* Set the {@link TraversableResolver} used.
*
* @param traversableResolver
*/
public final void setTraversableResolver(
TraversableResolver traversableResolver) {
this.traversableResolver = traversableResolver;
}
/**
* {@inheritDoc}
*/
public TraversableResolver getTraversableResolver() {
return traversableResolver;
}
/**
* Set the {@link ConstraintValidatorFactory} used.
*
* @param constraintValidatorFactory
*/
public final void setConstraintValidatorFactory(
ConstraintValidatorFactory constraintValidatorFactory) {
this.constraintValidatorFactory = constraintValidatorFactory;
}
/**
* {@inheritDoc}
*/
public ConstraintValidatorFactory getConstraintValidatorFactory() {
return constraintValidatorFactory;
}
/**
* Return an object of the specified type to allow access to the
* provider-specific API. If the Bean Validation provider implementation
* does not support the specified class, the ValidationException is thrown.
*
* @param type the class of the object to be returned.
* @return an instance of the specified class
* @throws ValidationException if the provider does not support the call.
*/
public T unwrap(final Class type) {
if (type.isInstance(this)) {
@SuppressWarnings("unchecked")
T result = (T) this;
return result;
}
// FIXME 2011-03-27 jw:
// This code is unsecure.
// It should allow only a fixed set of classes.
// Can't fix this because don't know which classes this method should support.
if (!(type.isInterface() || Modifier.isAbstract(type
.getModifiers()))) {
return newInstance(type);
}
try {
Class> cls = ClassUtils.getClass(type.getName() + "Impl");
if (type.isAssignableFrom(cls)) {
@SuppressWarnings("unchecked")
T result = (T) newInstance(cls);
return result;
}
} catch (ClassNotFoundException e) {
// do nothing
}
throw new ValidationException("Type " + type + " not supported");
}
private T newInstance(final Class cls) {
return AccessController.doPrivileged(new PrivilegedAction() {
public T run() {
try {
return cls.newInstance();
} catch (final Exception ex) {
throw new ValidationException("Cannot instantiate : " + cls, ex);
}
}
});
}
/**
* Get the detected {@link ConstraintDefaults}.
*
* @return ConstraintDefaults
*/
public ConstraintDefaults getDefaultConstraints() {
return defaultConstraints;
}
/**
* Get the detected {@link AnnotationIgnores}.
*
* @return AnnotationIgnores
*/
public AnnotationIgnores getAnnotationIgnores() {
return annotationIgnores;
}
/**
* Get the constraint cache used.
*
* @return {@link ConstraintCached}
*/
public ConstraintCached getConstraintsCache() {
return constraintsCache;
}
/**
* Add a meta-constraint to this {@link ApacheValidatorFactory}'s runtime
* customizations.
*
* @param beanClass
* @param metaConstraint
*/
public void addMetaConstraint(Class> beanClass,
MetaConstraint, ?> metaConstraint) {
List> slot;
synchronized (constraintMap) {
slot = constraintMap.get(beanClass);
if (slot == null) {
slot = new ArrayList>();
constraintMap.put(beanClass, slot);
}
}
slot.add(metaConstraint);
}
/**
* Mark a property of beanClass
for nested validation.
*
* @param beanClass
* @param accessStrategy
* defining the property to validate
*/
public void addValid(Class> beanClass, AccessStrategy accessStrategy) {
List slot;
synchronized (validAccesses) {
slot = validAccesses.get(beanClass);
if (slot == null) {
slot = new ArrayList();
validAccesses.put(beanClass, slot);
}
}
slot.add(accessStrategy);
}
/**
* Set the default group sequence for a particular bean class.
*
* @param beanClass
* @param groupSequence
*/
public void addDefaultSequence(Class> beanClass, Class>... groupSequence) {
defaultSequences.put(beanClass, safeArray(groupSequence));
}
/**
* Retrieve the runtime constraint configuration for a given class.
*
* @param
* @param beanClass
* @return List of {@link MetaConstraint}s applicable to
* beanClass
*/
public List> getMetaConstraints(
Class beanClass) {
final List> slot = constraintMap.get(beanClass);
if (slot == null) {
return Collections.emptyList();
}
// noinspection RedundantCast
@SuppressWarnings({ "unchecked", "rawtypes" })
final List> result = (List) slot;
return Collections.unmodifiableList(result);
}
/**
* Get the {@link AccessStrategy} {@link List} indicating nested bean
* validations that must be triggered in the course of validating a
* beanClass
graph.
*
* @param beanClass
* @return {@link List} of {@link AccessStrategy}
*/
public List getValidAccesses(Class> beanClass) {
final List slot = validAccesses.get(beanClass);
return slot == null ? Collections. emptyList() : Collections.unmodifiableList(slot);
}
/**
* Get the default group sequence configured for beanClass
.
*
* @param beanClass
* @return group Class array
*/
public Class>[] getDefaultSequence(Class> beanClass) {
return safeArray(defaultSequences.get(beanClass));
}
private static Class>[] safeArray(Class>... array) {
return ArrayUtils.isEmpty(array) ? ArrayUtils.EMPTY_CLASS_ARRAY : ArrayUtils.clone(array);
}
}