001    // $Id: Validation.java 17620 2009-10-04 19:19:28Z hardy.ferentschik $
002    /*
003    * JBoss, Home of Professional Open Source
004    * Copyright 2009, Red Hat, Inc. and/or its affiliates, and individual contributors
005    * by the @authors tag. See the copyright.txt in the distribution for a
006    * full listing of individual contributors.
007    *
008    * Licensed under the Apache License, Version 2.0 (the "License");
009    * you may not use this file except in compliance with the License.
010    * You may obtain a copy of the License at
011    * http://www.apache.org/licenses/LICENSE-2.0
012    * Unless required by applicable law or agreed to in writing, software
013    * distributed under the License is distributed on an "AS IS" BASIS,
014    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015    * See the License for the specific language governing permissions and
016    * limitations under the License.
017    */
018    package javax.validation;
019    
020    import java.io.BufferedReader;
021    import java.io.IOException;
022    import java.io.InputStream;
023    import java.io.InputStreamReader;
024    import java.net.URL;
025    import java.security.AccessController;
026    import java.security.PrivilegedAction;
027    import java.util.ArrayList;
028    import java.util.Enumeration;
029    import java.util.List;
030    import java.util.Map;
031    import java.util.WeakHashMap;
032    import javax.validation.bootstrap.GenericBootstrap;
033    import javax.validation.bootstrap.ProviderSpecificBootstrap;
034    import javax.validation.spi.BootstrapState;
035    import javax.validation.spi.ValidationProvider;
036    
037    /**
038     * This class is the entry point for Bean Validation. There are three ways
039     * to bootstrap it:
040     * <ul>
041     * <li>
042     * The easiest approach is to build the default <code>ValidatorFactory</code>.
043     * <pre>{@code ValidatorFactory factory = Validation.buildDefaultValidatorFactory();}</pre>
044     * In this case, the default validation provider resolver
045     * will be used to locate available providers.
046     * The chosen provider is defined as followed:
047     * <ul>
048     * <li>if the XML configuration defines a provider, this provider is used</li>
049     * <li>if the XML configuration does not define a provider or if no XML configuration
050     * is present the first provider returned by the
051     * <code>ValidationProviderResolver</code> instance is used.</li>
052     * </ul>
053     * </li>
054     * <li>
055     * The second bootstrap approach allows to choose a custom
056     * <code>ValidationProviderResolver</code>. The chosen
057     * <code>ValidationProvider</code> is then determined in the same way
058     * as in the default bootstrapping case (see above).
059     * <pre>{@code
060     * Configuration<?> configuration = Validation
061     *    .byDefaultProvider()
062     *    .providerResolver( new MyResolverStrategy() )
063     *    .configure();
064     * ValidatorFactory factory = configuration.buildValidatorFactory();}
065     * </pre>
066     * </li>
067     * <li>
068     * The third approach allows you to specify explicitly and in
069     * a type safe fashion the expected provider.
070     * <p/>
071     * Optionally you can choose a custom <code>ValidationProviderResolver</code>.
072     * <pre>{@code
073     * ACMEConfiguration configuration = Validation
074     *    .byProvider(ACMEProvider.class)
075     *    .providerResolver( new MyResolverStrategy() )  // optionally set the provider resolver
076     *    .configure();
077     * ValidatorFactory factory = configuration.buildValidatorFactory();}
078     * </pre>
079     * </li>
080     * </ul>
081     * Note:<br/>
082     * <ul>
083     * <li>
084     * The <code>ValidatorFactory</code> object built by the bootstrap process should be cached
085     * and shared amongst <code>Validator</code> consumers.
086     * </li>
087     * <li>
088     * This class is thread-safe.
089     * </li>
090     * </ul>
091     *
092     * @author Emmanuel Bernard
093     * @author Hardy Ferentschik
094     */
095    public class Validation {
096    
097            /**
098             * Build and return a <code>ValidatorFactory</code> instance based on the
099             * default Bean Validation provider and following the XML configuration.
100             * <p/>
101             * The provider list is resolved using the default validation provider resolver
102             * logic.
103             * <p/> The code is semantically equivalent to
104             * <code>Validation.byDefaultProvider().configure().buildValidatorFactory()</code>
105             *
106             * @return <code>ValidatorFactory</code> instance.
107             *
108             * @throws ValidationException if the ValidatorFactory cannot be built
109             */
110            public static ValidatorFactory buildDefaultValidatorFactory() {
111                    return byDefaultProvider().configure().buildValidatorFactory();
112            }
113    
114            /**
115             * Build a <code>Configuration</code>. The provider list is resolved
116             * using the strategy provided to the bootstrap state.
117             * <pre>
118             * Configuration&lt?&gt; configuration = Validation
119             *    .byDefaultProvider()
120             *    .providerResolver( new MyResolverStrategy() )
121             *    .configure();
122             * ValidatorFactory factory = configuration.buildValidatorFactory();
123             * </pre>
124             * The provider can be specified in the XML configuration. If the XML
125             * configuration does not exsist or if no provider is specified,
126             * the first available provider will be returned.
127             *
128             * @return instance building a generic <code>Configuration</code>
129             *         compliant with the bootstrap state provided.
130             */
131            public static GenericBootstrap byDefaultProvider() {
132                    return new GenericBootstrapImpl();
133            }
134    
135            /**
136             * Build a <code>Configuration</code> for a particular provider implementation.
137             * Optionally overrides the provider resolution strategy used to determine the provider.
138             * <p/>
139             * Used by applications targeting a specific provider programmatically.
140             * <p/>
141             * <pre>
142             * ACMEConfiguration configuration =
143             *     Validation.byProvider(ACMEProvider.class)
144             *             .providerResolver( new MyResolverStrategy() )
145             *             .configure();
146             * </pre>,
147             * where <code>ACMEConfiguration</code> is the
148             * <code>Configuration</code> sub interface uniquely identifying the
149             * ACME Bean Validation provider. and <code>ACMEProvider</code> is the
150             * <code>ValidationProvider</code> implementation of the ACME provider.
151             *
152             * @param providerType the <code>ValidationProvider</code> implementation type
153             *
154             * @return instance building a provider specific <code>Configuration</code>
155             *         sub interface implementation.
156             */
157            public static <T extends Configuration<T>, U extends ValidationProvider<T>>
158            ProviderSpecificBootstrap<T> byProvider(Class<U> providerType) {
159                    return new ProviderSpecificBootstrapImpl<T, U>( providerType );
160            }
161    
162            //private class, not exposed
163            private static class ProviderSpecificBootstrapImpl<T extends Configuration<T>, U extends ValidationProvider<T>>
164                            implements ProviderSpecificBootstrap<T> {
165    
166                    private final Class<U> validationProviderClass;
167                    private ValidationProviderResolver resolver;
168    
169                    public ProviderSpecificBootstrapImpl(Class<U> validationProviderClass) {
170                            this.validationProviderClass = validationProviderClass;
171                    }
172    
173                    /**
174                     * Optionally define the provider resolver implementation used.
175                     * If not defined, use the default ValidationProviderResolver
176                     *
177                     * @param resolver ValidationProviderResolver implementation used
178                     *
179                     * @return self
180                     */
181                    public ProviderSpecificBootstrap<T> providerResolver(ValidationProviderResolver resolver) {
182                            this.resolver = resolver;
183                            return this;
184                    }
185    
186                    /**
187                     * Determine the provider implementation suitable for byProvider(Class)
188                     * and delegate the creation of this specific Configuration subclass to the provider.
189                     *
190                     * @return a Configuration sub interface implementation
191                     */
192                    public T configure() {
193                            if ( validationProviderClass == null ) {
194                                    throw new ValidationException(
195                                                    "builder is mandatory. Use Validation.byDefaultProvider() to use the generic provider discovery mechanism"
196                                    );
197                            }
198                            //used mostly as a BootstrapState
199                            GenericBootstrapImpl state = new GenericBootstrapImpl();
200                            if ( resolver == null ) {
201                                    resolver = state.getDefaultValidationProviderResolver();
202                            }
203                            else {
204                                    //stay null if no resolver is defined
205                                    state.providerResolver( resolver );
206                            }
207    
208                            List<ValidationProvider<?>> resolvers;
209                            try {
210                                    resolvers = resolver.getValidationProviders();
211                            }
212                            catch ( RuntimeException re ) {
213                                    throw new ValidationException( "Unable to get available provider resolvers.", re );
214                            }
215    
216                            for ( ValidationProvider provider : resolvers ) {
217                                    if ( validationProviderClass.isAssignableFrom( provider.getClass() ) ) {
218                                            ValidationProvider<T> specificProvider = validationProviderClass.cast( provider );
219                                            return specificProvider.createSpecializedConfiguration( state );
220    
221                                    }
222                            }
223                            throw new ValidationException( "Unable to find provider: " + validationProviderClass );
224                    }
225            }
226    
227            //private class, not exposed
228            private static class GenericBootstrapImpl implements GenericBootstrap, BootstrapState {
229    
230                    private ValidationProviderResolver resolver;
231                    private ValidationProviderResolver defaultResolver;
232    
233                    public GenericBootstrap providerResolver(ValidationProviderResolver resolver) {
234                            this.resolver = resolver;
235                            return this;
236                    }
237    
238                    public ValidationProviderResolver getValidationProviderResolver() {
239                            return resolver;
240                    }
241    
242                    public ValidationProviderResolver getDefaultValidationProviderResolver() {
243                            if ( defaultResolver == null ) {
244                                    defaultResolver = new DefaultValidationProviderResolver();
245                            }
246                            return defaultResolver;
247                    }
248    
249                    public Configuration<?> configure() {
250                            ValidationProviderResolver resolver = this.resolver == null ?
251                                            getDefaultValidationProviderResolver() :
252                                            this.resolver;
253    
254                            List<ValidationProvider<?>> resolvers;
255                            try {
256                                    resolvers = resolver.getValidationProviders();
257                            }
258                            catch ( RuntimeException re ) {
259                                    throw new ValidationException( "Unable to get available provider resolvers.", re );
260                            }
261    
262                            if ( resolvers.size() == 0 ) {
263                                    //FIXME looks like an assertion error almost
264                                    throw new ValidationException( "Unable to find a default provider" );
265                            }
266    
267                            Configuration<?> config;
268                            try {
269                                    config = resolver.getValidationProviders().get( 0 ).createGenericConfiguration( this );
270                            }
271                            catch ( RuntimeException re ) {
272                                    throw new ValidationException( "Unable to instantiate Configuration.", re );
273                            }
274    
275                            return config;
276                    }
277            }
278    
279            /**
280             * Find <code>ValidationProvider</code> according to the default <code>ValidationProviderResolver</code> defined in the
281             * Bean Validation specification. This implementation uses the current classloader or the classloader which has loaded
282             * the current class if the current class loader is unavailable. The classloader is used to retrieve the Service Provider files.
283             * <p>
284             * 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>.
285             * Since we cannot rely on Java 6 we have to reimplement the <code>Service</code> functionality.
286             * </p>
287             *
288             * @author Emmanuel Bernard
289             * @author Hardy Ferentschik
290             */
291            private static class DefaultValidationProviderResolver implements ValidationProviderResolver {
292    
293                    //cache per classloader for an appropriate discovery
294                    //keep them in a weak hashmap to avoid memory leaks and allow proper hot redeployment
295                    //TODO use a WeakConcurrentHashMap
296                    //FIXME The List<VP> does keep a strong reference to the key ClassLoader, use the same model as JPA CachingPersistenceProviderResolver
297                    private static final Map<ClassLoader, List<ValidationProvider<?>>> providersPerClassloader =
298                                    new WeakHashMap<ClassLoader, List<ValidationProvider<?>>>();
299    
300                    private static final String SERVICES_FILE = "META-INF/services/" + ValidationProvider.class.getName();
301    
302                    public List<ValidationProvider<?>> getValidationProviders() {
303                            ClassLoader classloader = GetClassLoader.fromContext();
304                            if ( classloader == null ) {
305                                    classloader = GetClassLoader.fromClass( DefaultValidationProviderResolver.class );
306                            }
307    
308                            List<ValidationProvider<?>> providers;
309                            synchronized ( providersPerClassloader ) {
310                                    providers = providersPerClassloader.get( classloader );
311                            }
312    
313                            if ( providers == null ) {
314                                    providers = new ArrayList<ValidationProvider<?>>();
315                                    try {
316                                            // If we are deployed into an OSGi environment, leverage it
317                                            Class providerClass = org.apache.servicemix.specs.locator.OsgiLocator.locate(ValidationProvider.class.getName());
318                                            if (providerClass != null) {
319                                                    providers.add(( ValidationProvider ) providerClass.newInstance());
320                                            }
321                                    } catch (Throwable e) {
322                                            // Do nothing here
323                                    } 
324                                    String name = null;
325                                    try {
326                                            Enumeration<URL> providerDefinitions = classloader.getResources( SERVICES_FILE );
327                                            while ( providerDefinitions.hasMoreElements() ) {
328                                                    URL url = providerDefinitions.nextElement();
329                                                    InputStream stream = url.openStream();
330                                                    try {
331                                                            BufferedReader reader = new BufferedReader( new InputStreamReader( stream ), 100 );
332                                                            name = reader.readLine();
333                                                            while ( name != null ) {
334                                                                    name = name.trim();
335                                                                    if ( !name.startsWith( "#" ) ) {
336                                                                            final Class<?> providerClass = loadClass(
337                                                                                            name,
338                                                                                            DefaultValidationProviderResolver.class
339                                                                            );
340    
341                                                                            providers.add(
342                                                                                            ( ValidationProvider ) providerClass.newInstance()
343                                                                            );
344                                                                    }
345                                                                    name = reader.readLine();
346                                                            }
347                                                    }
348                                                    finally {
349                                                            stream.close();
350                                                    }
351                                            }
352                                    }
353                                    catch ( IOException e ) {
354                                            throw new ValidationException( "Unable to read " + SERVICES_FILE, e );
355                                    }
356                                    catch ( ClassNotFoundException e ) {
357                                            //TODO is it better to not fail the whole loading because of a black sheep?
358                                            throw new ValidationException( "Unable to load Bean Validation provider " + name, e );
359                                    }
360                                    catch ( IllegalAccessException e ) {
361                                            throw new ValidationException( "Unable to instanciate Bean Validation provider" + name, e );
362                                    }
363                                    catch ( InstantiationException e ) {
364                                            throw new ValidationException( "Unable to instanciate Bean Validation provider" + name, e );
365                                    }
366    
367                                    synchronized ( providersPerClassloader ) {
368                                            providersPerClassloader.put( classloader, providers );
369                                    }
370                            }
371    
372                            return providers;
373                    }
374    
375                    private static Class<?> loadClass(String name, Class<?> caller) throws ClassNotFoundException {
376                            try {
377                                    //try context classloader, if fails try caller classloader
378                                    ClassLoader loader = GetClassLoader.fromContext();
379                                    if ( loader != null ) {
380                                            return loader.loadClass( name );
381                                    }
382                            }
383                            catch ( ClassNotFoundException e ) {
384                                    //trying caller classloader
385                                    if ( caller == null ) {
386                                            throw e;
387                                    }
388                            }
389                            return Class.forName( name, true, GetClassLoader.fromClass( caller ) );
390                    }
391            }
392    
393            private static class GetClassLoader implements PrivilegedAction<ClassLoader> {
394                    private final Class<?> clazz;
395    
396                    public static ClassLoader fromContext() {
397                            final GetClassLoader action = new GetClassLoader( null );
398                            if ( System.getSecurityManager() != null ) {
399                                    return AccessController.doPrivileged( action );
400                            }
401                            else {
402                                    return action.run();
403                            }
404                    }
405    
406                    public static ClassLoader fromClass(Class<?> clazz) {
407                            if ( clazz == null ) {
408                                    throw new IllegalArgumentException( "Class is null" );
409                            }
410                            final GetClassLoader action = new GetClassLoader( clazz );
411                            if ( System.getSecurityManager() != null ) {
412                                    return AccessController.doPrivileged( action );
413                            }
414                            else {
415                                    return action.run();
416                            }
417                    }
418    
419                    private GetClassLoader(Class<?> clazz) {
420                            this.clazz = clazz;
421                    }
422    
423                    public ClassLoader run() {
424                            if ( clazz != null ) {
425                                    return clazz.getClassLoader();
426                            }
427                            else {
428                                    return Thread.currentThread().getContextClassLoader();
429                            }
430                    }
431            }
432    }
433