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

br.com.anteros.validation.api.Validation Maven / Gradle / Ivy

There is a newer version: 1.0.3
Show newest version
// $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 br.com.anteros.validation.api;

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 br.com.anteros.validation.api.bootstrap.GenericBootstrap;
import br.com.anteros.validation.api.bootstrap.ProviderSpecificBootstrap;
import br.com.anteros.validation.api.spi.BootstrapState;
import br.com.anteros.validation.api.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(); private static final String SCANNER_CLASSES_ANDROID = "br.com.anteros.android.util.AndroidClassPathScanner"; @SuppressWarnings("unchecked") 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); } /* * Tratamento especial para acrescentar compatibilidade com o * Android. */ if (providers.size() == 0) { try { Class> clValidationProvider = (Class>) Class.forName("br.com.anteros.bean.validation.AnterosValidationProvider"); providers.add(clValidationProvider.newInstance()); } catch (Exception 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(); } } } }