1 /*
2  * Jakarta Bean Validation API
3  *
4  * License: Apache License, Version 2.0
5  * See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
6  */

7 package javax.validation;
8
9 import java.lang.ref.SoftReference;
10 import java.security.AccessController;
11 import java.security.PrivilegedAction;
12 import java.util.ArrayList;
13 import java.util.Iterator;
14 import java.util.List;
15 import java.util.ServiceConfigurationError;
16 import java.util.ServiceLoader;
17 import java.util.WeakHashMap;
18
19 import javax.validation.bootstrap.GenericBootstrap;
20 import javax.validation.bootstrap.ProviderSpecificBootstrap;
21 import javax.validation.spi.BootstrapState;
22 import javax.validation.spi.ValidationProvider;
23
24 /**
25  * This class is the entry point for Jakarta Bean Validation.
26  * <p>
27  * There are three ways to bootstrap it:
28  * <ul>
29  *     <li>The easiest approach is to build the default {@link ValidatorFactory}.
30  *     <pre>
31  * ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
32  * </pre>
33  *     In this case, the default validation provider resolver
34  *     will be used to locate available providers.
35  *     <p>
36  *     The chosen provider is defined as followed:
37  *     <ul>
38  *         <li>if the XML configuration defines a provider, this provider is used</li>
39  *         <li>if the XML configuration does not define a provider or if no XML
40  *         configuration is present the first provider returned by the
41  *         {@link ValidationProviderResolver} instance is used.</li>
42  *     </ul>
43  *     </li>
44  *     <li>
45  *     The second bootstrap approach allows to choose a custom
46  *     {@code ValidationProviderResolver}. The chosen
47  *     {@link ValidationProvider} is then determined in the same way
48  *     as in the default bootstrapping case (see above).
49  *     <pre>
50  * Configuration&lt;?&gt; configuration = Validation
51  *    .byDefaultProvider()
52  *    .providerResolver( new MyResolverStrategy() )
53  *    .configure();
54  * ValidatorFactory factory = configuration.buildValidatorFactory();
55  * </pre>
56  *     </li>
57  *     <li>
58  *     The third approach allows you to specify explicitly and in
59  *     a type safe fashion the expected provider.
60  *     <p>
61  *     Optionally you can choose a custom {@code ValidationProviderResolver}.
62  *     <pre>
63  * ACMEConfiguration configuration = Validation
64  *    .byProvider(ACMEProvider.class)
65  *    .providerResolver( new MyResolverStrategy() )  // optionally set the provider resolver
66  *    .configure();
67  * ValidatorFactory factory = configuration.buildValidatorFactory();
68  * </pre>
69  *     </li>
70  * </ul>
71  * <p>
72  * Note:
73  * <ul>
74  *     <li>
75  *     The {@code ValidatorFactory} object built by the bootstrap process should be cached
76  *     and shared amongst {@code Validator} consumers.
77  *     </li>
78  *     <li>This class is thread-safe.</li>
79  * </ul>
80  *
81  * @author Emmanuel Bernard
82  * @author Hardy Ferentschik
83  */

84 public class Validation {
85
86     /**
87      * Builds and returns a {@link ValidatorFactory} instance based on the
88      * default Jakarta Bean Validation provider and following the XML configuration.
89      * <p>
90      * The provider list is resolved using the default validation provider resolver
91      * logic.
92      * <p>
93      * The code is semantically equivalent to
94      * {@code Validation.byDefaultProvider().configure().buildValidatorFactory()}.
95      *
96      * @return {@code ValidatorFactory} instance
97      *
98      * @throws NoProviderFoundException if no Jakarta Bean Validation provider was found
99      * @throws ValidationException if a Jakarta Bean Validation provider was found but the
100      * {@code ValidatorFactory} cannot be built
101      */

102     public static ValidatorFactory buildDefaultValidatorFactory() {
103         return byDefaultProvider().configure().buildValidatorFactory();
104     }
105
106     /**
107      * Builds a {@link Configuration}. The provider list is resolved
108      * using the strategy provided to the bootstrap state.
109      * <pre>
110      * Configuration&lt;?&gt; configuration = Validation
111      *    .byDefaultProvider()
112      *    .providerResolver( new MyResolverStrategy() )
113      *    .configure();
114      * ValidatorFactory factory = configuration.buildValidatorFactory();
115      * </pre>
116      * The provider can be specified in the XML configuration. If the XML
117      * configuration does not exist or if no provider is specified,
118      * the first available provider will be returned.
119      *
120      * @return instance building a generic {@code Configuration}
121      *         compliant with the bootstrap state provided
122      */

123     public static GenericBootstrap byDefaultProvider() {
124         return new GenericBootstrapImpl();
125     }
126
127     /**
128      * Builds a {@link Configuration} for a particular provider implementation.
129      * <p>
130      * Optionally overrides the provider resolution strategy used to determine the provider.
131      * <p>
132      * Used by applications targeting a specific provider programmatically.
133      * <pre>
134      * ACMEConfiguration configuration =
135      *     Validation.byProvider(ACMEProvider.class)
136      *             .providerResolver( new MyResolverStrategy() )
137      *             .configure();
138      * </pre>,
139      * where {@code ACMEConfiguration} is the
140      * {@code Configuration} sub interface uniquely identifying the
141      * ACME Jakarta Bean Validation provider. and {@code ACMEProvider} is the
142      * {@link ValidationProvider} implementation of the ACME provider.
143      *
144      * @param providerType the {@code ValidationProvider} implementation type
145      * @param <T> the type of the {@code Configuration} corresponding to this
146      *        {@code ValidationProvider}
147      * @param <U> the type of the {@code ValidationProvider} implementation
148      *
149      * @return instance building a provider specific {@code Configuration}
150      *         sub interface implementation
151      */

152     public static <T extends Configuration<T>, U extends ValidationProvider<T>>
153     ProviderSpecificBootstrap<T> byProvider(Class<U> providerType) {
154         return new ProviderSpecificBootstrapImpl<>( providerType );
155     }
156
157     /**
158      * Not a public API; it can be used reflectively by code that integrates with Jakarta Bean Validation, e.g. application
159      * servers, to clear the provider cache maintained by the default provider resolver.
160      * <p>
161      * This is a strictly unsupported API, its definition may be changed or removed at any time. Its purpose is to
162      * explore the addition of standardized methods for dealing with provider caching in a future revision of the spec.
163      */

164     @SuppressWarnings("unused")
165     private static void clearDefaultValidationProviderResolverCache() {
166         GetValidationProviderListAction.clearCache();
167     }
168
169     //private class, not exposed
170     private static class ProviderSpecificBootstrapImpl<T extends Configuration<T>, U extends ValidationProvider<T>>
171             implements ProviderSpecificBootstrap<T> {
172
173         private final Class<U> validationProviderClass;
174         private ValidationProviderResolver resolver;
175
176         public ProviderSpecificBootstrapImpl(Class<U> validationProviderClass) {
177             this.validationProviderClass = validationProviderClass;
178         }
179
180         /**
181          * Optionally defines the provider resolver implementation used.
182          * If not defined, use the default ValidationProviderResolver.
183          *
184          * @param resolver {@link ValidationProviderResolver} implementation used
185          *
186          * @return self
187          */

188         @Override
189         public ProviderSpecificBootstrap<T> providerResolver(ValidationProviderResolver resolver) {
190             this.resolver = resolver;
191             return this;
192         }
193
194         /**
195          * Determines the provider implementation suitable for {@link #byProvider(Class)}
196          * and delegates the creation of this specific {@link Configuration} subclass to the
197          * provider.
198          *
199          * @return a {@code Configuration} sub interface implementation
200          */

201         @Override
202         public T configure() {
203             if ( validationProviderClass == null ) {
204                 throw new ValidationException(
205                         "builder is mandatory. Use Validation.byDefaultProvider() to use the generic provider discovery mechanism"
206                 );
207             }
208             //used mostly as a BootstrapState
209             GenericBootstrapImpl state = new GenericBootstrapImpl();
210
211             // if no resolver is given, simply instantiate the given provider
212             if ( resolver == null ) {
213                 U provider = run( NewProviderInstance.action( validationProviderClass ) );
214                 return provider.createSpecializedConfiguration( state );
215             }
216             else {
217                 //stay null if no resolver is defined
218                 state.providerResolver( resolver );
219             }
220
221             List<ValidationProvider<?>> resolvers;
222             try {
223                 resolvers = resolver.getValidationProviders();
224             }
225             catch ( RuntimeException re ) {
226                 throw new ValidationException( "Unable to get available provider resolvers.", re );
227             }
228
229             for ( ValidationProvider<?> provider : resolvers ) {
230                 if ( validationProviderClass.isAssignableFrom( provider.getClass() ) ) {
231                     ValidationProvider<T> specificProvider = validationProviderClass.cast( provider );
232                     return specificProvider.createSpecializedConfiguration( state );
233
234                 }
235             }
236             throw new ValidationException( "Unable to find provider: " + validationProviderClass );
237         }
238
239         private <P> P run(PrivilegedAction<P> action) {
240             return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run();
241         }
242     }
243
244     //private class, not exposed
245     private static class GenericBootstrapImpl implements GenericBootstrap, BootstrapState {
246
247         private ValidationProviderResolver resolver;
248         private ValidationProviderResolver defaultResolver;
249
250         @Override
251         public GenericBootstrap providerResolver(ValidationProviderResolver resolver) {
252             this.resolver = resolver;
253             return this;
254         }
255
256         @Override
257         public ValidationProviderResolver getValidationProviderResolver() {
258             return resolver;
259         }
260
261         @Override
262         public ValidationProviderResolver getDefaultValidationProviderResolver() {
263             if ( defaultResolver == null ) {
264                 defaultResolver = new DefaultValidationProviderResolver();
265             }
266             return defaultResolver;
267         }
268
269         @Override
270         public Configuration<?> configure() {
271             ValidationProviderResolver resolver = this.resolver == null ?
272                     getDefaultValidationProviderResolver() :
273                     this.resolver;
274
275             List<ValidationProvider<?>> validationProviders;
276             try {
277                 validationProviders = resolver.getValidationProviders();
278             }
279             // don't wrap existing ValidationExceptions in another ValidationException
280             catch ( ValidationException e ) {
281                 throw e;
282             }
283             // if any other exception occurs wrap it in a ValidationException
284             catch ( RuntimeException re ) {
285                 throw new ValidationException( "Unable to get available provider resolvers.", re );
286             }
287
288             if ( validationProviders.isEmpty() ) {
289                 String msg = "Unable to create a Configuration, because no Jakarta Bean Validation provider could be found." +
290                         " Add a provider like Hibernate Validator (RI) to your classpath.";
291                 throw new NoProviderFoundException( msg );
292             }
293
294             Configuration<?> config;
295             try {
296                 config = resolver.getValidationProviders().get( 0 ).createGenericConfiguration( this );
297             }
298             catch ( RuntimeException re ) {
299                 throw new ValidationException( "Unable to instantiate Configuration.", re );
300             }
301
302             return config;
303         }
304     }
305
306     /**
307      * Finds {@link ValidationProvider} according to the default {@link ValidationProviderResolver} defined in the
308      * Jakarta Bean Validation specification. This implementation first uses thread's context classloader to locate providers.
309      * If no suitable provider is found using the aforementioned class loader, it uses current class loader.
310      * If it still does not find any suitable provider, it tries to locate the built-in provider using the current
311      * class loader.
312      *
313      * @author Emmanuel Bernard
314      * @author Hardy Ferentschik
315      */

316     private static class DefaultValidationProviderResolver implements ValidationProviderResolver {
317         @Override
318         public List<ValidationProvider<?>> getValidationProviders() {
319             // class loading and ServiceLoader methods should happen in a PrivilegedAction
320             return GetValidationProviderListAction.getValidationProviderList();
321         }
322     }
323
324     private static class GetValidationProviderListAction implements PrivilegedAction<List<ValidationProvider<?>>> {
325
326         private final static GetValidationProviderListAction INSTANCE = new GetValidationProviderListAction();
327
328         //cache per classloader for an appropriate discovery
329         //keep them in a weak hash map to avoid memory leaks and allow proper hot redeployment
330         private final WeakHashMap<ClassLoader, SoftReference<List<ValidationProvider<?>>>> providersPerClassloader =
331                 new WeakHashMap<>();
332
333         public static synchronized List<ValidationProvider<?>> getValidationProviderList() {
334             if ( System.getSecurityManager() != null ) {
335                 return AccessController.doPrivileged( INSTANCE );
336             }
337             else {
338                 return INSTANCE.run();
339             }
340         }
341
342         public static synchronized void clearCache() {
343             INSTANCE.providersPerClassloader.clear();
344         }
345
346         @Override
347         public List<ValidationProvider<?>> run() {
348             // Option #1: try first context class loader
349             ClassLoader classloader = Thread.currentThread().getContextClassLoader();
350             List<ValidationProvider<?>> cachedContextClassLoaderProviderList = getCachedValidationProviders( classloader );
351             if ( cachedContextClassLoaderProviderList != null ) {
352                 // if already processed return the cached provider list
353                 return cachedContextClassLoaderProviderList;
354             }
355
356             List<ValidationProvider<?>> validationProviderList = loadProviders( classloader );
357
358             // Option #2: if we cannot find any service files with the context class loader use the current class loader
359             if ( validationProviderList.isEmpty() ) {
360                 classloader = DefaultValidationProviderResolver.class.getClassLoader();
361                 List<ValidationProvider<?>> cachedCurrentClassLoaderProviderList = getCachedValidationProviders(
362                         classloader
363                 );
364                 if ( cachedCurrentClassLoaderProviderList != null ) {
365                     // if already processed return the cached provider list
366                     return cachedCurrentClassLoaderProviderList;
367                 }
368                 validationProviderList = loadProviders( classloader );
369             }
370
371             // cache the detected providers against the classloader in which they were found
372             cacheValidationProviders( classloader, validationProviderList );
373
374             return validationProviderList;
375         }
376
377         private List<ValidationProvider<?>> loadProviders(ClassLoader classloader) {
378             ServiceLoader<ValidationProvider> loader = ServiceLoader.load( ValidationProvider.class, classloader );
379             Iterator<ValidationProvider> providerIterator = loader.iterator();
380             List<ValidationProvider<?>> validationProviderList = new ArrayList<>();
381             while ( providerIterator.hasNext() ) {
382                 try {
383                     validationProviderList.add( providerIterator.next() );
384                 }
385                 catch ( ServiceConfigurationError e ) {
386                     // ignore, because it can happen when multiple
387                     // providers are present and some of them are not class loader
388                     // compatible with our API.
389                 }
390             }
391             return validationProviderList;
392         }
393
394         private synchronized List<ValidationProvider<?>> getCachedValidationProviders(ClassLoader classLoader) {
395             SoftReference<List<ValidationProvider<?>>> ref = providersPerClassloader.get( classLoader );
396             return ref != null ? ref.get() : null;
397         }
398
399         private synchronized void cacheValidationProviders(ClassLoader classLoader, List<ValidationProvider<?>> providers) {
400             providersPerClassloader.put( classLoader, new SoftReference<>( providers ) );
401         }
402     }
403
404     private static class NewProviderInstance<T extends ValidationProvider<?>> implements PrivilegedAction<T> {
405
406         private final Class<T> clazz;
407
408         public static <T extends ValidationProvider<?>> NewProviderInstance<T> action(Class<T> clazz) {
409             return new NewProviderInstance<>( clazz );
410         }
411
412         private NewProviderInstance(Class<T> clazz) {
413             this.clazz = clazz;
414         }
415
416         @Override
417         public T run() {
418             try {
419                 return clazz.newInstance();
420             }
421             catch (InstantiationException | IllegalAccessException | RuntimeException e) {
422                 throw new ValidationException( "Cannot instantiate provider type: " + clazz, e );
423             }
424         }
425     }
426 }
427