// $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: * <ul> * <li> * The easiest approach is to build the default <code>ValidatorFactory</code>. * <pre>{@code ValidatorFactory factory = Validation.buildDefaultValidatorFactory();}</pre> * In this case, the default validation provider resolver * will be used to locate available providers. * The chosen provider is defined as followed: * <ul> * <li>if the XML configuration defines a provider, this provider is used</li> * <li>if the XML configuration does not define a provider or if no XML configuration * is present the first provider returned by the * <code>ValidationProviderResolver</code> instance is used.</li> * </ul> * </li> * <li> * The second bootstrap approach allows to choose a custom * <code>ValidationProviderResolver</code>. The chosen * <code>ValidationProvider</code> is then determined in the same way * as in the default bootstrapping case (see above). * <pre>{@code * Configuration<?> configuration = Validation * .byDefaultProvider() * .providerResolver( new MyResolverStrategy() ) * .configure(); * ValidatorFactory factory = configuration.buildValidatorFactory();} * </pre> * </li> * <li> * The third approach allows you to specify explicitly and in * a type safe fashion the expected provider. * <p/> * Optionally you can choose a custom <code>ValidationProviderResolver</code>. * <pre>{@code * ACMEConfiguration configuration = Validation * .byProvider(ACMEProvider.class) * .providerResolver( new MyResolverStrategy() ) // optionally set the provider resolver * .configure(); * ValidatorFactory factory = configuration.buildValidatorFactory();} * </pre> * </li> * </ul> * Note:<br/> * <ul> * <li> * The <code>ValidatorFactory</code> object built by the bootstrap process should be cached * and shared amongst <code>Validator</code> consumers. * </li> * <li> * This class is thread-safe. * </li> * </ul> * * @author Emmanuel Bernard * @author Hardy Ferentschik */ public class Validation { /** * Build and return a <code>ValidatorFactory</code> instance based on the * default Bean Validation provider and following the XML configuration. * <p/> * The provider list is resolved using the default validation provider resolver * logic. * <p/> The code is semantically equivalent to * <code>Validation.byDefaultProvider().configure().buildValidatorFactory()</code> * * @return <code>ValidatorFactory</code> instance. * * @throws ValidationException if the ValidatorFactory cannot be built */ public static ValidatorFactory buildDefaultValidatorFactory() { return byDefaultProvider().configure().buildValidatorFactory(); } /** * Build a <code>Configuration</code>. The provider list is resolved * using the strategy provided to the bootstrap state. * <pre> * Configuration<?> configuration = Validation * .byDefaultProvider() * .providerResolver( new MyResolverStrategy() ) * .configure(); * ValidatorFactory factory = configuration.buildValidatorFactory(); * </pre> * 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 <code>Configuration</code> * compliant with the bootstrap state provided. */ public static GenericBootstrap byDefaultProvider() { return new GenericBootstrapImpl(); } /** * Build a <code>Configuration</code> for a particular provider implementation. * Optionally overrides the provider resolution strategy used to determine the provider. * <p/> * Used by applications targeting a specific provider programmatically. * <p/> * <pre> * ACMEConfiguration configuration = * Validation.byProvider(ACMEProvider.class) * .providerResolver( new MyResolverStrategy() ) * .configure(); * </pre>, * where <code>ACMEConfiguration</code> is the * <code>Configuration</code> sub interface uniquely identifying the * ACME Bean Validation provider. and <code>ACMEProvider</code> is the * <code>ValidationProvider</code> implementation of the ACME provider. * * @param providerType the <code>ValidationProvider</code> implementation type * * @return instance building a provider specific <code>Configuration</code> * sub interface implementation. */ public static <T extends Configuration<T>, U extends ValidationProvider<T>> ProviderSpecificBootstrap<T> byProvider(Class<U> providerType) { return new ProviderSpecificBootstrapImpl<T, U>( providerType ); } //private class, not exposed private static class ProviderSpecificBootstrapImpl<T extends Configuration<T>, U extends ValidationProvider<T>> implements ProviderSpecificBootstrap<T> { private final Class<U> validationProviderClass; private ValidationProviderResolver resolver; public ProviderSpecificBootstrapImpl(Class<U> 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<T> 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<ValidationProvider<?>> 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<T> 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<ValidationProvider<?>> 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 <code>ValidationProvider</code> according to the default <code>ValidationProviderResolver</code> 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. * <p> * This class implements the Service Provider pattern described <a href="http://java.sun.com/j2se/1.5.0/docs/guide/jar/jar.html#Service%20Provider">here</a>. * Since we cannot rely on Java 6 we have to reimplement the <code>Service</code> functionality. * </p> * * @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<VP> does keep a strong reference to the key ClassLoader, use the same model as JPA CachingPersistenceProviderResolver private static final Map<ClassLoader, List<ValidationProvider<?>>> providersPerClassloader = new WeakHashMap<ClassLoader, List<ValidationProvider<?>>>(); private static final String SERVICES_FILE = "META-INF/services/" + ValidationProvider.class.getName(); public List<ValidationProvider<?>> getValidationProviders() { ClassLoader classloader = GetClassLoader.fromContext(); if ( classloader == null ) { classloader = GetClassLoader.fromClass( DefaultValidationProviderResolver.class ); } List<ValidationProvider<?>> providers; synchronized ( providersPerClassloader ) { providers = providersPerClassloader.get( classloader ); } if ( providers == null ) { providers = new ArrayList<ValidationProvider<?>>(); try { // If we are deployed into an OSGi environment, leverage it Class<? extends ValidationProvider> providerClass = org.apache.servicemix.specs.locator.OsgiLocator.locate(ValidationProvider.class); if (providerClass != null) { providers.add(providerClass.newInstance()); } } catch (Throwable e) { // Do nothing here } String name = null; try { Enumeration<URL> 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<ClassLoader> { 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(); } } } }