javax.validation.Validation Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source
* Copyright 2009, Red Hat, Inc. and/or its affiliates, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* Licensed 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 javax.validation;
import java.lang.ref.SoftReference;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.WeakHashMap;
import javax.validation.bootstrap.GenericBootstrap;
import javax.validation.bootstrap.ProviderSpecificBootstrap;
import javax.validation.spi.BootstrapState;
import javax.validation.spi.ValidationProvider;
/**
* This class is the entry point for Bean Validation. There are three ways
* to bootstrap it:
*
* -
* The easiest approach is to build the default {@code ValidatorFactory}.
*
{@code ValidatorFactory factory = Validation.buildDefaultValidatorFactory();}
* In this case, the default validation provider resolver
* will be used to locate available providers.
* The chosen provider is defined as followed:
*
* - if the XML configuration defines a provider, this provider is used
* - if the XML configuration does not define a provider or if no XML configuration
* is present the first provider returned by the
* {@code ValidationProviderResolver} instance is used.
*
*
* -
* The second bootstrap approach allows to choose a custom
* {@code ValidationProviderResolver}. The chosen
* {@code ValidationProvider} is then determined in the same way
* as in the default bootstrapping case (see above).
*
{@code
* Configuration> configuration = Validation
* .byDefaultProvider()
* .providerResolver( new MyResolverStrategy() )
* .configure();
* ValidatorFactory factory = configuration.buildValidatorFactory();}
*
*
* -
* The third approach allows you to specify explicitly and in
* a type safe fashion the expected provider.
*
* Optionally you can choose a custom {@code ValidationProviderResolver}.
*
{@code
* ACMEConfiguration configuration = Validation
* .byProvider(ACMEProvider.class)
* .providerResolver( new MyResolverStrategy() ) // optionally set the provider resolver
* .configure();
* ValidatorFactory factory = configuration.buildValidatorFactory();}
*
*
*
* Note:
*
* -
* The {@code ValidatorFactory} object built by the bootstrap process should be cached
* and shared amongst {@code Validator} consumers.
*
* -
* This class is thread-safe.
*
*
*
* @author Emmanuel Bernard
* @author Hardy Ferentschik
*/
public class Validation {
/**
* Build and return a {@code ValidatorFactory} instance based on the
* default Bean Validation provider and following the XML configuration.
*
* The provider list is resolved using the default validation provider resolver
* logic.
* The code is semantically equivalent to
* {@code Validation.byDefaultProvider().configure().buildValidatorFactory()}
*
* @return {@code ValidatorFactory} instance.
*
* @throws ValidationException if the ValidatorFactory cannot be built
*/
public static ValidatorFactory buildDefaultValidatorFactory() {
return byDefaultProvider().configure().buildValidatorFactory();
}
/**
* Build a {@code Configuration}. The provider list is resolved
* using the strategy provided to the bootstrap state.
*
* Configuration<?> configuration = Validation
* .byDefaultProvider()
* .providerResolver( new MyResolverStrategy() )
* .configure();
* ValidatorFactory factory = configuration.buildValidatorFactory();
*
* The provider can be specified in the XML configuration. If the XML
* configuration does not exist or if no provider is specified,
* the first available provider will be returned.
*
* @return instance building a generic {@code Configuration}
* compliant with the bootstrap state provided.
*/
public static GenericBootstrap byDefaultProvider() {
return new GenericBootstrapImpl();
}
/**
* Build a {@code Configuration} for a particular provider implementation.
* Optionally overrides the provider resolution strategy used to determine the provider.
*
* Used by applications targeting a specific provider programmatically.
*
*
* ACMEConfiguration configuration =
* Validation.byProvider(ACMEProvider.class)
* .providerResolver( new MyResolverStrategy() )
* .configure();
*
,
* where {@code ACMEConfiguration} is the
* {@code Configuration} sub interface uniquely identifying the
* ACME Bean Validation provider. and {@code ACMEProvider} is the
* {@code ValidationProvider} implementation of the ACME provider.
*
* @param providerType the {@code ValidationProvider} implementation type
*
* @return instance building a provider specific {@code Configuration}
* sub interface implementation.
*/
public static , U extends ValidationProvider>
ProviderSpecificBootstrap byProvider(Class providerType) {
return new ProviderSpecificBootstrapImpl( providerType );
}
//private class, not exposed
private static class ProviderSpecificBootstrapImpl, U extends ValidationProvider>
implements ProviderSpecificBootstrap {
private final Class validationProviderClass;
private ValidationProviderResolver resolver;
public ProviderSpecificBootstrapImpl(Class validationProviderClass) {
this.validationProviderClass = validationProviderClass;
}
/**
* Optionally define the provider resolver implementation used.
* If not defined, use the default ValidationProviderResolver
*
* @param resolver ValidationProviderResolver implementation used
*
* @return self
*/
public ProviderSpecificBootstrap providerResolver(ValidationProviderResolver resolver) {
this.resolver = resolver;
return this;
}
/**
* Determine the provider implementation suitable for byProvider(Class)
* and delegate the creation of this specific Configuration subclass to the provider.
*
* @return a Configuration sub interface implementation
*/
public T configure() {
if ( validationProviderClass == null ) {
throw new ValidationException(
"builder is mandatory. Use Validation.byDefaultProvider() to use the generic provider discovery mechanism"
);
}
//used mostly as a BootstrapState
GenericBootstrapImpl state = new GenericBootstrapImpl();
if ( resolver == null ) {
resolver = state.getDefaultValidationProviderResolver();
}
else {
//stay null if no resolver is defined
state.providerResolver( resolver );
}
List> resolvers;
try {
resolvers = resolver.getValidationProviders();
}
catch ( RuntimeException re ) {
throw new ValidationException( "Unable to get available provider resolvers.", re );
}
for ( ValidationProvider provider : resolvers ) {
if ( validationProviderClass.isAssignableFrom( provider.getClass() ) ) {
ValidationProvider specificProvider = validationProviderClass.cast( provider );
return specificProvider.createSpecializedConfiguration( state );
}
}
throw new ValidationException( "Unable to find provider: " + validationProviderClass );
}
}
//private class, not exposed
private static class GenericBootstrapImpl implements GenericBootstrap, BootstrapState {
private ValidationProviderResolver resolver;
private ValidationProviderResolver defaultResolver;
public GenericBootstrap providerResolver(ValidationProviderResolver resolver) {
this.resolver = resolver;
return this;
}
public ValidationProviderResolver getValidationProviderResolver() {
return resolver;
}
public ValidationProviderResolver getDefaultValidationProviderResolver() {
if ( defaultResolver == null ) {
defaultResolver = new DefaultValidationProviderResolver();
}
return defaultResolver;
}
public Configuration> configure() {
ValidationProviderResolver resolver = this.resolver == null ?
getDefaultValidationProviderResolver() :
this.resolver;
List> validationProviders;
try {
validationProviders = resolver.getValidationProviders();
}
// don't wrap existing ValidationExceptions in another ValidationException
catch ( ValidationException e ) {
throw e;
}
// if any other exception occurs wrap it in a ValidationException
catch ( RuntimeException re ) {
throw new ValidationException( "Unable to get available provider resolvers.", re );
}
if ( validationProviders.size() == 0 ) {
String msg = "Unable to create a Configuration, because no Bean Validation provider could be found." +
" Add a provider like Hibernate Validator (RI) to your classpath.";
throw new ValidationException( msg );
}
Configuration> config;
try {
config = resolver.getValidationProviders().get( 0 ).createGenericConfiguration( this );
}
catch ( RuntimeException re ) {
throw new ValidationException( "Unable to instantiate Configuration.", re );
}
return config;
}
}
/**
* Find {@code ValidationProvider} according to the default {@code ValidationProviderResolver} defined in the
* Bean Validation specification. This implementation first uses thread's context classloader to locate providers.
* If no suitable provider is found using the aforementioned class loader, it uses current class loader.
* If it still does not find any suitable provider, it tries to locate the built-in provider using the current
* class loader.
*
* @author Emmanuel Bernard
* @author Hardy Ferentschik
*/
private static class DefaultValidationProviderResolver implements ValidationProviderResolver {
public List> getValidationProviders() {
// class loading and ServiceLoader methods should happen in a PrivilegedAction
return GetValidationProviderListAction.getValidationProviderList();
}
}
private static class GetValidationProviderListAction implements PrivilegedAction>> {
//cache per classloader for an appropriate discovery
//keep them in a weak hash map to avoid memory leaks and allow proper hot redeployment
private static final WeakHashMap>>> providersPerClassloader =
new WeakHashMap>>>();
public static List> getValidationProviderList() {
final GetValidationProviderListAction action = new GetValidationProviderListAction();
if ( System.getSecurityManager() != null ) {
return AccessController.doPrivileged( action );
}
else {
return action.run();
}
}
public List> run() {
// Option #1: try first context class loader
ClassLoader classloader = Thread.currentThread().getContextClassLoader();
List> cachedContextClassLoaderProviderList = getCachedValidationProviders( classloader );
if ( cachedContextClassLoaderProviderList != null ) {
// if already processed return the cached provider list
return cachedContextClassLoaderProviderList;
}
List> validationProviderList = loadProviders( classloader );
// Option #2: if we cannot find any service files with the context class loader use the current class loader
if ( validationProviderList.isEmpty() ) {
classloader = DefaultValidationProviderResolver.class.getClassLoader();
List> cachedCurrentClassLoaderProviderList = getCachedValidationProviders(
classloader
);
if ( cachedCurrentClassLoaderProviderList != null ) {
// if already processed return the cached provider list
return cachedCurrentClassLoaderProviderList;
}
validationProviderList = loadProviders( classloader );
}
// cache the detected providers against the classloader in which they were found
cacheValidationProviders( classloader, validationProviderList );
return validationProviderList;
}
private List> loadProviders(ClassLoader classloader) {
ServiceLoader loader = ServiceLoader.load( ValidationProvider.class, classloader );
Iterator providerIterator = loader.iterator();
List> validationProviderList = new ArrayList>();
while ( providerIterator.hasNext() ) {
try {
validationProviderList.add( providerIterator.next() );
}
catch ( ServiceConfigurationError e ) {
// ignore, because it can happen when multiple
// providers are present and some of them are not class loader
// compatible with our API.
}
}
return validationProviderList;
}
private synchronized List> getCachedValidationProviders(ClassLoader classLoader) {
SoftReference>> ref = providersPerClassloader.get( classLoader );
return ref != null ? ref.get() : null;
}
private synchronized void cacheValidationProviders(ClassLoader classLoader, List> providers) {
providersPerClassloader.put( classLoader, new SoftReference>>( providers ) );
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy