| // $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(); |
| } |
| } |
| } |
| } |
| |