All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jakarta.validation.Validation Maven / Gradle / Ivy

There is a newer version: 11.0.0-M4
Show newest version
/*
 * Jakarta Bean Validation API
 *
 * License: Apache License, Version 2.0
 * See the license.txt file in the root directory or .
 */
package jakarta.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 jakarta.validation.bootstrap.GenericBootstrap;
import jakarta.validation.bootstrap.ProviderSpecificBootstrap;
import jakarta.validation.spi.BootstrapState;
import jakarta.validation.spi.ValidationProvider;

/**
 * This class is the entry point for Jakarta Bean Validation.
 * 

* There are three ways to bootstrap it: *

    *
  • The easiest approach is to build the default {@link ValidatorFactory}. *
     * 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 * {@link ValidationProviderResolver} instance is used.
    • *
    *
  • *
  • * The second bootstrap approach allows to choose a custom * {@code ValidationProviderResolver}. The chosen * {@link ValidationProvider} is then determined in the same way * as in the default bootstrapping case (see above). *
     * 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}. *

     * 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 { /** * Builds and returns a {@link ValidatorFactory} instance based on the * default Jakarta 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 NoProviderFoundException if no Jakarta Bean Validation provider was found * @throws ValidationException if a Jakarta Bean Validation provider was found but the * {@code ValidatorFactory} cannot be built */ public static ValidatorFactory buildDefaultValidatorFactory() { return byDefaultProvider().configure().buildValidatorFactory(); } /** * Builds a {@link 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(); } /** * Builds a {@link 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 Jakarta Bean Validation provider. and {@code ACMEProvider} is the * {@link ValidationProvider} implementation of the ACME provider. * * @param providerType the {@code ValidationProvider} implementation type * @param the type of the {@code Configuration} corresponding to this * {@code ValidationProvider} * @param the type of the {@code ValidationProvider} implementation * * @return instance building a provider specific {@code Configuration} * sub interface implementation */ public static , U extends ValidationProvider> ProviderSpecificBootstrap byProvider(Class providerType) { return new ProviderSpecificBootstrapImpl<>( providerType ); } /** * Not a public API; it can be used reflectively by code that integrates with Jakarta Bean Validation, e.g. application * servers, to clear the provider cache maintained by the default provider resolver. *

* This is a strictly unsupported API, its definition may be changed or removed at any time. Its purpose is to * explore the addition of standardized methods for dealing with provider caching in a future revision of the spec. */ @SuppressWarnings("unused") private static void clearDefaultValidationProviderResolverCache() { GetValidationProviderListAction.clearCache(); } //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 defines the provider resolver implementation used. * If not defined, use the default ValidationProviderResolver. * * @param resolver {@link ValidationProviderResolver} implementation used * * @return self */ @Override public ProviderSpecificBootstrap providerResolver(ValidationProviderResolver resolver) { this.resolver = resolver; return this; } /** * Determines the provider implementation suitable for {@link #byProvider(Class)} * and delegates the creation of this specific {@link Configuration} subclass to the * provider. * * @return a {@code Configuration} sub interface implementation */ @Override 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 no resolver is given, simply instantiate the given provider if ( resolver == null ) { U provider = run( NewProviderInstance.action( validationProviderClass ) ); return provider.createSpecializedConfiguration( state ); } 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

P run(PrivilegedAction

action) { return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); } } //private class, not exposed private static class GenericBootstrapImpl implements GenericBootstrap, BootstrapState { private ValidationProviderResolver resolver; private ValidationProviderResolver defaultResolver; @Override public GenericBootstrap providerResolver(ValidationProviderResolver resolver) { this.resolver = resolver; return this; } @Override public ValidationProviderResolver getValidationProviderResolver() { return resolver; } @Override public ValidationProviderResolver getDefaultValidationProviderResolver() { if ( defaultResolver == null ) { defaultResolver = new DefaultValidationProviderResolver(); } return defaultResolver; } @Override 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.isEmpty() ) { String msg = "Unable to create a Configuration, because no Jakarta Bean Validation provider could be found." + " Add a provider like Hibernate Validator (RI) to your classpath."; throw new NoProviderFoundException( msg ); } Configuration config; try { config = resolver.getValidationProviders().get( 0 ).createGenericConfiguration( this ); } catch ( RuntimeException re ) { throw new ValidationException( "Unable to instantiate Configuration.", re ); } return config; } } /** * Finds {@link ValidationProvider} according to the default {@link ValidationProviderResolver} defined in the * Jakarta 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 { @Override public List> getValidationProviders() { // class loading and ServiceLoader methods should happen in a PrivilegedAction return GetValidationProviderListAction.getValidationProviderList(); } } private static class GetValidationProviderListAction implements PrivilegedAction>> { private final static GetValidationProviderListAction INSTANCE = new GetValidationProviderListAction(); //cache per classloader for an appropriate discovery //keep them in a weak hash map to avoid memory leaks and allow proper hot redeployment private final WeakHashMap>>> providersPerClassloader = new WeakHashMap<>(); public static synchronized List> getValidationProviderList() { if ( System.getSecurityManager() != null ) { return AccessController.doPrivileged( INSTANCE ); } else { return INSTANCE.run(); } } public static synchronized void clearCache() { INSTANCE.providersPerClassloader.clear(); } @Override 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 ) ); } } private static class NewProviderInstance> implements PrivilegedAction { private final Class clazz; public static > NewProviderInstance action(Class clazz) { return new NewProviderInstance<>( clazz ); } private NewProviderInstance(Class clazz) { this.clazz = clazz; } @Override public T run() { try { return clazz.newInstance(); } catch (InstantiationException | IllegalAccessException | RuntimeException e) { throw new ValidationException( "Cannot instantiate provider type: " + clazz, e ); } } } }