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<?> 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<?> 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