javax.validation.Validation Maven / Gradle / Ivy
// $Id: Validation.java 17620 2009-10-04 19:19:28Z hardy.ferentschik $
/*
* 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.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
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
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
*
ValidationProviderResolver
instance is used.
*
*
* -
* The second bootstrap approach allows to choose a custom
*
ValidationProviderResolver
. The chosen
* 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
ValidationProviderResolver
.
* {@code
* ACMEConfiguration configuration = Validation
* .byProvider(ACMEProvider.class)
* .providerResolver( new MyResolverStrategy() ) // optionally set the provider resolver
* .configure();
* ValidatorFactory factory = configuration.buildValidatorFactory();}
*
*
*
* Note:
*
* -
* The
ValidatorFactory
object built by the bootstrap process should be cached
* and shared amongst Validator
consumers.
*
* -
* This class is thread-safe.
*
*
*
* @author Emmanuel Bernard
* @author Hardy Ferentschik
*/
public class Validation {
/**
* Build and return a 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
* Validation.byDefaultProvider().configure().buildValidatorFactory()
*
* @return ValidatorFactory
instance.
*
* @throws ValidationException if the ValidatorFactory cannot be built
*/
public static ValidatorFactory buildDefaultValidatorFactory() {
return byDefaultProvider().configure().buildValidatorFactory();
}
/**
* Build a 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 exsist or if no provider is specified,
* the first available provider will be returned.
*
* @return instance building a generic Configuration
* compliant with the bootstrap state provided.
*/
public static GenericBootstrap byDefaultProvider() {
return new GenericBootstrapImpl();
}
/**
* Build a 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 ACMEConfiguration
is the
* Configuration
sub interface uniquely identifying the
* ACME Bean Validation provider. and ACMEProvider
is the
* ValidationProvider
implementation of the ACME provider.
*
* @param providerType the ValidationProvider
implementation type
*
* @return instance building a provider specific 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> resolvers;
try {
resolvers = resolver.getValidationProviders();
}
catch ( RuntimeException re ) {
throw new ValidationException( "Unable to get available provider resolvers.", re );
}
if ( resolvers.size() == 0 ) {
//FIXME looks like an assertion error almost
throw new ValidationException( "Unable to find a default provider" );
}
Configuration> config;
try {
config = resolver.getValidationProviders().get( 0 ).createGenericConfiguration( this );
}
catch ( RuntimeException re ) {
throw new ValidationException( "Unable to instantiate Configuration.", re );
}
return config;
}
}
/**
* Find ValidationProvider
according to the default ValidationProviderResolver
defined in the
* Bean Validation specification. This implementation uses the current classloader or the classloader which has loaded
* the current class if the current class loader is unavailable. The classloader is used to retrieve the Service Provider files.
*
* This class implements the Service Provider pattern described here.
* Since we cannot rely on Java 6 we have to reimplement the Service
functionality.
*
*
* @author Emmanuel Bernard
* @author Hardy Ferentschik
*/
private static class DefaultValidationProviderResolver implements ValidationProviderResolver {
//cache per classloader for an appropriate discovery
//keep them in a weak hashmap to avoid memory leaks and allow proper hot redeployment
//TODO use a WeakConcurrentHashMap
//FIXME The List does keep a strong reference to the key ClassLoader, use the same model as JPA CachingPersistenceProviderResolver
private static final Map>> providersPerClassloader =
new WeakHashMap>>();
private static final String SERVICES_FILE = "META-INF/services/" + ValidationProvider.class.getName();
public List> getValidationProviders() {
ClassLoader classloader = GetClassLoader.fromContext();
if ( classloader == null ) {
classloader = GetClassLoader.fromClass( DefaultValidationProviderResolver.class );
}
List> providers;
synchronized ( providersPerClassloader ) {
providers = providersPerClassloader.get( classloader );
}
if ( providers == null ) {
providers = new ArrayList>();
String name = null;
try {
Enumeration providerDefinitions = classloader.getResources( SERVICES_FILE );
while ( providerDefinitions.hasMoreElements() ) {
URL url = providerDefinitions.nextElement();
InputStream stream = url.openStream();
try {
BufferedReader reader = new BufferedReader( new InputStreamReader( stream ), 100 );
name = reader.readLine();
while ( name != null ) {
name = name.trim();
if ( !name.startsWith( "#" ) ) {
final Class> providerClass = loadClass(
name,
DefaultValidationProviderResolver.class
);
providers.add(
( ValidationProvider ) providerClass.newInstance()
);
}
name = reader.readLine();
}
}
finally {
stream.close();
}
}
}
catch ( IOException e ) {
throw new ValidationException( "Unable to read " + SERVICES_FILE, e );
}
catch ( ClassNotFoundException e ) {
//TODO is it better to not fail the whole loading because of a black sheep?
throw new ValidationException( "Unable to load Bean Validation provider " + name, e );
}
catch ( IllegalAccessException e ) {
throw new ValidationException( "Unable to instanciate Bean Validation provider" + name, e );
}
catch ( InstantiationException e ) {
throw new ValidationException( "Unable to instanciate Bean Validation provider" + name, e );
}
synchronized ( providersPerClassloader ) {
providersPerClassloader.put( classloader, providers );
}
}
return providers;
}
private static Class> loadClass(String name, Class> caller) throws ClassNotFoundException {
try {
//try context classloader, if fails try caller classloader
ClassLoader loader = GetClassLoader.fromContext();
if ( loader != null ) {
return loader.loadClass( name );
}
}
catch ( ClassNotFoundException e ) {
//trying caller classloader
if ( caller == null ) {
throw e;
}
}
return Class.forName( name, true, GetClassLoader.fromClass( caller ) );
}
}
private static class GetClassLoader implements PrivilegedAction {
private final Class> clazz;
public static ClassLoader fromContext() {
final GetClassLoader action = new GetClassLoader( null );
if ( System.getSecurityManager() != null ) {
return AccessController.doPrivileged( action );
}
else {
return action.run();
}
}
public static ClassLoader fromClass(Class> clazz) {
if ( clazz == null ) {
throw new IllegalArgumentException( "Class is null" );
}
final GetClassLoader action = new GetClassLoader( clazz );
if ( System.getSecurityManager() != null ) {
return AccessController.doPrivileged( action );
}
else {
return action.run();
}
}
private GetClassLoader(Class> clazz) {
this.clazz = clazz;
}
public ClassLoader run() {
if ( clazz != null ) {
return clazz.getClassLoader();
}
else {
return Thread.currentThread().getContextClassLoader();
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy