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