Repository: bval Updated Branches: refs/heads/bv2 b7b7a1032 -> 36920e87f
configuration refactoring Project: http://git-wip-us.apache.org/repos/asf/bval/repo Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/36920e87 Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/36920e87 Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/36920e87 Branch: refs/heads/bv2 Commit: 36920e87f316b24231da4492c023da3feeef7ec9 Parents: b7b7a10 Author: Matt Benson <mben...@apache.org> Authored: Tue Mar 13 18:30:07 2018 -0500 Committer: Matt Benson <mben...@apache.org> Committed: Tue Mar 13 18:30:07 2018 -0500 ---------------------------------------------------------------------- .../java/org/apache/bval/cdi/BValExtension.java | 26 +- .../bval/jsr/BootstrapConfigurationImpl.java | 59 ++- .../org/apache/bval/jsr/ConfigurationImpl.java | 422 +++++++++---------- .../apache/bval/jsr/xml/ValidationParser.java | 257 +++-------- .../bval/jsr/xml/ValidationParserTest.java | 6 +- 5 files changed, 295 insertions(+), 475 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/bval/blob/36920e87/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java b/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java index 75bf195..39823a5 100644 --- a/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java +++ b/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java @@ -51,6 +51,7 @@ import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import org.apache.bval.jsr.ConfigurationImpl; import org.apache.bval.util.Validate; /** @@ -110,7 +111,10 @@ public class BValExtension implements Extension { if (validator != null) { return; } - config.addProperty("bval.before.cdi", "true"); // ignore parts of the config relying on CDI since we didn't start yet + if (config instanceof ConfigurationImpl) { + // ignore parts of the config relying on CDI since we didn't start yet + ((ConfigurationImpl) config).deferBootstrapOverrides(); + } if (factory == null) { factory = config.buildValidatorFactory(); } @@ -218,17 +222,9 @@ public class BValExtension implements Extension { if (factory != null) { // cleanup cache used to discover ValidateOnException before factory is recreated factory.close(); } - - cdiIntegration(afterBeanDiscovery, beanManager); - } - - private void cdiIntegration(final AfterBeanDiscovery afterBeanDiscovery, final BeanManager beanManager) { - try { - config.addProperty("bval.before.cdi", "false"); // now take into account all the config - } catch (final Exception e) { - // no-op: sadly tck does it + if (config instanceof ConfigurationImpl) { + ((ConfigurationImpl) config).releaseDeferredBootstrapOverrides(); } - if (!validatorFactoryFound) { try { // recreate the factory afterBeanDiscovery.addBean(new ValidatorFactoryBean(factory = config.buildValidatorFactory())); @@ -272,9 +268,7 @@ public class BValExtension implements Extension { it.postConstruct(instance); return new Releasable<T>(context, it, instance); - } catch (final Exception e) { - // no-op - } catch (final NoClassDefFoundError error) { + } catch (final Exception | NoClassDefFoundError error) { // no-op } return null; @@ -305,9 +299,7 @@ public class BValExtension implements Extension { injectionTarget.preDestroy(instance); injectionTarget.dispose(instance); context.release(); - } catch (final Exception e) { - // no-op - } catch (final NoClassDefFoundError e) { + } catch (final Exception | NoClassDefFoundError e) { // no-op } } http://git-wip-us.apache.org/repos/asf/bval/blob/36920e87/bval-jsr/src/main/java/org/apache/bval/jsr/BootstrapConfigurationImpl.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/BootstrapConfigurationImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/BootstrapConfigurationImpl.java index d85ab51..f7a11a3 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/BootstrapConfigurationImpl.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/BootstrapConfigurationImpl.java @@ -18,24 +18,50 @@ */ package org.apache.bval.jsr; -import javax.validation.BootstrapConfiguration; -import javax.validation.executable.ExecutableType; import java.util.Collections; +import java.util.EnumSet; import java.util.Map; import java.util.Set; +import javax.validation.BootstrapConfiguration; +import javax.validation.executable.ExecutableType; + public class BootstrapConfigurationImpl implements BootstrapConfiguration { - private Map<String, String> properties; - private Set<ExecutableType> defaultValidatedExecutableTypes; - private boolean executableValidationEnabled; - private Set<String> constraintMappingResourcePaths; + public static final Set<ExecutableType> DEFAULT_DEFAULT_VALIDATED_EXECUTABLE_TYPES = + Collections.unmodifiableSet(EnumSet.of(ExecutableType.CONSTRUCTORS, ExecutableType.NON_GETTER_METHODS)); + + public static final BootstrapConfigurationImpl DEFAULT = new BootstrapConfigurationImpl(Collections.emptySet(), + true, BootstrapConfigurationImpl.DEFAULT_DEFAULT_VALIDATED_EXECUTABLE_TYPES, Collections.emptyMap(), + Collections.emptySet()); + + private static Set<ExecutableType> expandExecutableValidation(Set<ExecutableType> executableTypes) { + if (executableTypes == DEFAULT_DEFAULT_VALIDATED_EXECUTABLE_TYPES) { + return executableTypes; + } + executableTypes = EnumSet.copyOf(executableTypes); + if (executableTypes.contains(ExecutableType.ALL)) { + executableTypes.clear(); + executableTypes.add(ExecutableType.CONSTRUCTORS); + executableTypes.add(ExecutableType.NON_GETTER_METHODS); + executableTypes.add(ExecutableType.GETTER_METHODS); + } else if (executableTypes.contains(ExecutableType.NONE)) { // if both are present ALL trumps NONE + executableTypes.clear(); + } + return Collections.unmodifiableSet(executableTypes); + } + + private final Set<String> constraintMappingResourcePaths; + private final boolean executableValidationEnabled; + private final Set<ExecutableType> defaultValidatedExecutableTypes; + private final Map<String, String> properties; + private final Set<String> valueExtractorClassNames; + private String parameterNameProviderClassName; private String traversableResolverClassName; private String messageInterpolatorClassName; private String constraintValidatorFactoryClassName; private String defaultProviderClassName; private String clockProviderClassName; - private Set<String> valueExtractorClassNames; public BootstrapConfigurationImpl(final String defaultProviderClassName, final String constraintValidatorFactoryClassName, final String messageInterpolatorClassName, @@ -43,16 +69,27 @@ public class BootstrapConfigurationImpl implements BootstrapConfiguration { final Set<String> constraintMappingResourcePaths, final boolean executableValidationEnabled, final Set<ExecutableType> defaultValidatedExecutableTypes, final Map<String, String> properties, final String clockProviderClassName, final Set<String> valueExtractorClassNames) { - this.properties = Collections.unmodifiableMap(properties); - this.defaultValidatedExecutableTypes = Collections.unmodifiableSet(defaultValidatedExecutableTypes); - this.executableValidationEnabled = executableValidationEnabled; - this.constraintMappingResourcePaths = Collections.unmodifiableSet(constraintMappingResourcePaths); + + this(Collections.unmodifiableSet(constraintMappingResourcePaths), executableValidationEnabled, + expandExecutableValidation(defaultValidatedExecutableTypes), Collections.unmodifiableMap(properties), + Collections.unmodifiableSet(valueExtractorClassNames)); + this.parameterNameProviderClassName = parameterNameProviderClassName; this.traversableResolverClassName = traversableResolverClassName; this.messageInterpolatorClassName = messageInterpolatorClassName; this.constraintValidatorFactoryClassName = constraintValidatorFactoryClassName; this.defaultProviderClassName = defaultProviderClassName; this.clockProviderClassName = clockProviderClassName; + } + + private BootstrapConfigurationImpl(final Set<String> constraintMappingResourcePaths, + final boolean executableValidationEnabled, final Set<ExecutableType> defaultValidatedExecutableTypes, + final Map<String, String> properties, final Set<String> valueExtractorClassNames) { + + this.constraintMappingResourcePaths = constraintMappingResourcePaths; + this.executableValidationEnabled = executableValidationEnabled; + this.defaultValidatedExecutableTypes = defaultValidatedExecutableTypes; + this.properties = properties; this.valueExtractorClassNames = valueExtractorClassNames; } http://git-wip-us.apache.org/repos/asf/bval/blob/36920e87/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java index 658273c..3e2f678 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java @@ -29,6 +29,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Supplier; import javax.validation.BootstrapConfiguration; import javax.validation.ClockProvider; @@ -39,7 +40,6 @@ import javax.validation.TraversableResolver; import javax.validation.ValidationException; import javax.validation.ValidationProviderResolver; import javax.validation.ValidatorFactory; -import javax.validation.executable.ExecutableType; import javax.validation.spi.BootstrapState; import javax.validation.spi.ConfigurationState; import javax.validation.spi.ValidationProvider; @@ -51,6 +51,7 @@ import org.apache.bval.jsr.resolver.DefaultTraversableResolver; import org.apache.bval.jsr.util.IOs; import org.apache.bval.jsr.xml.ValidationParser; import org.apache.bval.util.Exceptions; +import org.apache.bval.util.Lazy; import org.apache.commons.weaver.privilizer.Privileged; /** @@ -60,56 +61,79 @@ import org.apache.commons.weaver.privilizer.Privileged; * <br/> */ public class ConfigurationImpl implements ApacheValidatorConfiguration, ConfigurationState { + + private class LazyParticipant<T> extends Lazy<T> { + + private LazyParticipant(Supplier<T> init) { + super(init); + } + + @Override + public Lazy<T> reset(Supplier<T> init) { + try { + return super.reset(init); + } finally { + ConfigurationImpl.this.prepared = false; + } + } + + ConfigurationImpl override(T value) { + if (value != null) { + reset(() -> value); + } + return ConfigurationImpl.this; + } + } + /** * Configured {@link ValidationProvider} */ //couldn't this be parameterized <ApacheValidatorConfiguration> or <? super ApacheValidatorConfiguration>? - protected final ValidationProvider<?> provider; + private final ValidationProvider<ApacheValidatorConfiguration> provider; /** * Configured {@link ValidationProviderResolver} */ - protected final ValidationProviderResolver providerResolver; + private final ValidationProviderResolver providerResolver; /** * Configured {@link ValidationProvider} class */ - protected Class<? extends ValidationProvider<?>> providerClass; + private Class<? extends ValidationProvider<?>> providerClass; - /** - * Configured {@link MessageInterpolator} - */ - protected MessageInterpolator defaultMessageInterpolator = new DefaultMessageInterpolator(); - protected volatile MessageInterpolator messageInterpolator = defaultMessageInterpolator; - protected Class<? extends MessageInterpolator> messageInterpolatorClass; + private final MessageInterpolator defaultMessageInterpolator = new DefaultMessageInterpolator(); - /** - * Configured {@link ConstraintValidatorFactory} - */ - protected ConstraintValidatorFactory defaultConstraintValidatorFactory = new DefaultConstraintValidatorFactory(); - protected volatile ConstraintValidatorFactory constraintValidatorFactory = defaultConstraintValidatorFactory; - protected Class<? extends ConstraintValidatorFactory> constraintValidatorFactoryClass; + private final LazyParticipant<MessageInterpolator> messageInterpolator = + new LazyParticipant<>(this::getDefaultMessageInterpolator); + + private final ConstraintValidatorFactory defaultConstraintValidatorFactory = + new DefaultConstraintValidatorFactory(); + + private final LazyParticipant<ConstraintValidatorFactory> constraintValidatorFactory = + new LazyParticipant<>(this::getDefaultConstraintValidatorFactory); + + private final TraversableResolver defaultTraversableResolver = new DefaultTraversableResolver(); + + private final LazyParticipant<TraversableResolver> traversableResolver = + new LazyParticipant<>(this::getDefaultTraversableResolver); - protected TraversableResolver defaultTraversableResolver = new DefaultTraversableResolver(); - protected volatile TraversableResolver traversableResolver = defaultTraversableResolver; - protected Class<? extends TraversableResolver> traversableResolverClass; + private final ParameterNameProvider defaultParameterNameProvider = new DefaultParameterNameProvider(); - protected ParameterNameProvider defaultParameterNameProvider = new DefaultParameterNameProvider(); - protected volatile ParameterNameProvider parameterNameProvider = defaultParameterNameProvider; - protected Class<? extends ParameterNameProvider> parameterNameProviderClass; + private final LazyParticipant<ParameterNameProvider> parameterNameProvider = + new LazyParticipant<>(this::getDefaultParameterNameProvider); - protected BootstrapConfiguration bootstrapConfiguration; + private final ClockProvider defaultClockProvider = Clock::systemDefaultZone; - protected Collection<ExecutableType> executableValidation; + private final LazyParticipant<ClockProvider> clockProvider = new LazyParticipant<>(this::getDefaultClockProvider); + + private Lazy<BootstrapConfiguration> bootstrapConfiguration = new Lazy<>(this::createBootstrapConfiguration); private Collection<BValExtension.Releasable<?>> releasables = new CopyOnWriteArrayList<>(); - protected ClockProvider defaultClockProvider = Clock::systemDefaultZone; - protected volatile ClockProvider clockProvider = defaultClockProvider; - protected Class<? extends ClockProvider> clockProviderClass; - protected Set<ValueExtractor<?>> valueExtractors = new HashSet<>(); + private Set<ValueExtractor<?>> valueExtractors = new HashSet<>(); private boolean beforeCdi = false; + private ClassLoader loader; // BEGIN DEFAULTS /** @@ -118,22 +142,20 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur private boolean prepared = false; // END DEFAULTS - private Set<InputStream> mappingStreams = new HashSet<>(); - private Map<String, String> properties = new HashMap<>(); + private final Set<InputStream> mappingStreams = new HashSet<>(); + private final Map<String, String> properties = new HashMap<>(); private boolean ignoreXmlConfiguration = false; - private volatile ValidationParser parser; - /** * Create a new ConfigurationImpl instance. * @param aState bootstrap state * @param aProvider provider */ - public ConfigurationImpl(BootstrapState aState, ValidationProvider<?> aProvider) { - if (aProvider != null) { - this.provider = aProvider; - this.providerResolver = null; - } else if (aState != null) { + public ConfigurationImpl(BootstrapState aState, ValidationProvider<ApacheValidatorConfiguration> aProvider) { + Exceptions.raiseIf(aProvider == null && aState == null, ValidationException::new, + "one of provider or state is required"); + + if (aProvider == null) { this.provider = null; if (aState.getValidationProviderResolver() == null) { providerResolver = aState.getDefaultValidationProviderResolver(); @@ -141,26 +163,14 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur providerResolver = aState.getValidationProviderResolver(); } } else { - throw new ValidationException("either provider or state are required"); + this.provider = aProvider; + this.providerResolver = null; } initializePropertyDefaults(); } /** * {@inheritDoc} - */ - @Override - public ApacheValidatorConfiguration traversableResolver(TraversableResolver resolver) { - if (resolver != null) { - this.traversableResolverClass = null; - this.traversableResolver = resolver; - this.prepared = false; - } - return this; - } - - /** - * {@inheritDoc} * Ignore data from the <i>META-INF/validation.xml</i> file if this * method is called. * @@ -177,34 +187,33 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur */ @Override public ConfigurationImpl messageInterpolator(MessageInterpolator resolver) { - if (resolver != null) { - this.messageInterpolatorClass = null; - this.messageInterpolator = resolver; - this.prepared = false; - } - return this; + return messageInterpolator.override(resolver); } /** * {@inheritDoc} */ @Override - public ConfigurationImpl constraintValidatorFactory(ConstraintValidatorFactory constraintFactory) { - if (constraintFactory != null) { - this.constraintValidatorFactoryClass = null; - this.constraintValidatorFactory = constraintFactory; - this.prepared = false; - } - return this; + public ApacheValidatorConfiguration traversableResolver(TraversableResolver resolver) { + return traversableResolver.override(resolver); + } + + /** + * {@inheritDoc} + */ + @Override + public ConfigurationImpl constraintValidatorFactory(ConstraintValidatorFactory constraintValidatorFactory) { + return this.constraintValidatorFactory.override(constraintValidatorFactory); } @Override public ApacheValidatorConfiguration parameterNameProvider(ParameterNameProvider parameterNameProvider) { - if (parameterNameProvider != null) { - this.parameterNameProviderClass = null; - this.parameterNameProvider = parameterNameProvider; - } - return this; + return this.parameterNameProvider.override(parameterNameProvider); + } + + @Override + public ApacheValidatorConfiguration clockProvider(ClockProvider clockProvider) { + return this.clockProvider.override(clockProvider); } /** @@ -232,11 +241,7 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur */ @Override public ApacheValidatorConfiguration addProperty(String name, String value) { - if ("bval.before.cdi".equals(name)) { - beforeCdi = Boolean.parseBoolean(value); - } else { - properties.put(name, value); - } + properties.put(name, value); return this; } @@ -260,6 +265,11 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur return defaultParameterNameProvider; } + @Override + public ClockProvider getDefaultClockProvider() { + return defaultClockProvider; + } + /** * {@inheritDoc} * Return a map of non type-safe custom properties. @@ -296,22 +306,12 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur */ @Override public MessageInterpolator getMessageInterpolator() { - if (beforeCdi) { - return defaultMessageInterpolator; - } - if (messageInterpolator == defaultMessageInterpolator && messageInterpolatorClass != null) { - synchronized (this) { - if (messageInterpolator == defaultMessageInterpolator && messageInterpolatorClass != null) { - messageInterpolator = newInstance(messageInterpolatorClass); - } - } - } - return messageInterpolator; + return messageInterpolator.get(); } @Override public BootstrapConfiguration getBootstrapConfiguration() { - return createBootstrapConfiguration(); + return bootstrapConfiguration.get(); } /** @@ -325,58 +325,13 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur return doBuildValidatorFactory(); } - @Privileged - private ValidatorFactory doBuildValidatorFactory() { - prepare(); - parser.ensureValidatorFactoryCanBeBuilt(); - final ValidationProvider<?> useProvider = provider == null ? findProvider() : provider; - return useProvider.buildValidatorFactory(this); - } - - private ConfigurationImpl prepare() { - if (prepared) { - return this; - } - createBootstrapConfiguration(); - parser.applyConfigWithInstantiation(this); // instantiate the config if needed - - prepared = true; - return this; - } - - private BootstrapConfiguration createBootstrapConfiguration() { - if (parser == null) { - parser = parseValidationXml(); // already done if BootstrapConfiguration already looked up - bootstrapConfiguration = parser.getBootstrap(); - } - return bootstrapConfiguration; - } - - /** Check whether a validation.xml file exists and parses it with JAXB */ - private ValidationParser parseValidationXml() { - return ValidationParser.processValidationConfig(getProperties().get(Properties.VALIDATION_XML_PATH), this, - ignoreXmlConfiguration); - } - /** * {@inheritDoc} * @return the constraint validator factory of this configuration. */ @Override public ConstraintValidatorFactory getConstraintValidatorFactory() { - if (beforeCdi) { - return constraintValidatorFactory; - } - if (constraintValidatorFactory == defaultConstraintValidatorFactory - && constraintValidatorFactoryClass != null) { - synchronized (this) { - if (constraintValidatorFactory == defaultConstraintValidatorFactory - && constraintValidatorFactoryClass != null) { - constraintValidatorFactory = newInstance(constraintValidatorFactoryClass); - } - } - } - return constraintValidatorFactory; + return constraintValidatorFactory.get(); } /** @@ -384,73 +339,39 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur */ @Override public TraversableResolver getTraversableResolver() { - if (beforeCdi) { - return defaultTraversableResolver; - } - if (traversableResolver == defaultTraversableResolver && traversableResolverClass != null) { - synchronized (this) { - if (traversableResolver == defaultTraversableResolver && traversableResolverClass != null) { - traversableResolver = newInstance(traversableResolverClass); - } - } - } - return traversableResolver; + return traversableResolver.get(); } @Override public ParameterNameProvider getParameterNameProvider() { - if (beforeCdi) { - return defaultParameterNameProvider; - } - if (parameterNameProvider == defaultParameterNameProvider && parameterNameProviderClass != null) { - synchronized (this) { - if (parameterNameProvider == defaultParameterNameProvider && parameterNameProviderClass != null) { - parameterNameProvider = newInstance(parameterNameProviderClass); - } - } - } - return parameterNameProvider; + return parameterNameProvider.get(); } - /** - * Get the configured {@link ValidationProvider}. - * @return {@link ValidationProvider} - */ - public ValidationProvider<?> getProvider() { - return provider; + @Override + public ClockProvider getClockProvider() { + return clockProvider.get(); } - private ValidationProvider<?> findProvider() { - if (providerClass == null) { - return providerResolver.getValidationProviders().get(0); - } - final Optional<ValidationProvider<?>> knownProvider = - providerResolver.getValidationProviders().stream().filter(providerClass::isInstance).findFirst(); - if (knownProvider.isPresent()) { - return knownProvider.get(); - } - try { - return providerClass.getConstructor().newInstance(); - } catch (Exception e) { - throw Exceptions.create(ValidationException::new, "Unable to find/create %s of type %s", - ValidationProvider.class.getSimpleName(), providerClass); - } + @Override + public ApacheValidatorConfiguration addValueExtractor(ValueExtractor<?> extractor) { + valueExtractors.add(extractor); + return this; } - /** - * Set {@link ValidationProvider} class. - * @param providerClass the provider type - */ - public void setProviderClass(Class<? extends ValidationProvider<?>> providerClass) { - this.providerClass = providerClass; + @Override + public Set<ValueExtractor<?>> getValueExtractors() { + return Collections.unmodifiableSet(valueExtractors); } - public void setExecutableValidation(final Collection<ExecutableType> executableValidation) { - this.executableValidation = executableValidation; + public void deferBootstrapOverrides() { + beforeCdi = true; } - public Collection<ExecutableType> getExecutableValidation() { - return executableValidation; + public void releaseDeferredBootstrapOverrides() { + if (beforeCdi) { + beforeCdi = false; + performBootstrapOverrides(); + } } public Closeable getClosable() { @@ -463,74 +384,107 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur } @Privileged - private <T> T newInstance(final Class<T> cls) { - try { - final BValExtension.Releasable<T> releasable = BValExtension.inject(cls); - releasables.add(releasable); - return releasable.getInstance(); - } catch (Exception | NoClassDefFoundError e) { - } - try { - return cls.getConstructor().newInstance(); - } catch (final Exception e) { - throw new ValidationException(e.getMessage(), e); - } + private ValidatorFactory doBuildValidatorFactory() { + prepare(); + return Optional.<ValidationProvider<?>> ofNullable(provider).orElseGet(this::findProvider) + .buildValidatorFactory(this); } - public void traversableResolverClass(final Class<TraversableResolver> clazz) { - traversableResolverClass = clazz; + private void prepare() { + if (!prepared) { + applyBootstrapConfiguration(); + prepared = true; + } } - public void constraintValidatorFactoryClass(final Class<ConstraintValidatorFactory> clazz) { - constraintValidatorFactoryClass = clazz; + private BootstrapConfiguration createBootstrapConfiguration() { + if (!ignoreXmlConfiguration) { + loader = ValidationParser.class.getClassLoader(); + final BootstrapConfiguration xmlBootstrap = + ValidationParser.processValidationConfig(getProperties().get(Properties.VALIDATION_XML_PATH), this); + if (xmlBootstrap != null) { + return xmlBootstrap; + } + } + loader = ApacheValidatorFactory.class.getClassLoader(); + return BootstrapConfigurationImpl.DEFAULT; } - public void messageInterpolatorClass(final Class<MessageInterpolator> clazz) { - messageInterpolatorClass = clazz; - } + private void applyBootstrapConfiguration() { + final BootstrapConfiguration bootstrapConfig = bootstrapConfiguration.get(); - public void parameterNameProviderClass(final Class<? extends ParameterNameProvider> clazz) { - parameterNameProviderClass = clazz; - } + if (bootstrapConfig.getDefaultProviderClassName() != null) { + this.providerClass = loadClass(bootstrapConfig.getDefaultProviderClassName()); + } - @Override - public ApacheValidatorConfiguration clockProvider(ClockProvider clockProvider) { - this.clockProvider = clockProvider; - return this; + bootstrapConfig.getProperties().forEach(this::addProperty); + bootstrapConfig.getConstraintMappingResourcePaths().stream().map(ValidationParser::open) + .forEach(this::addMapping); + + if (!beforeCdi) { + performBootstrapOverrides(); + } } - @Override - public ApacheValidatorConfiguration addValueExtractor(ValueExtractor<?> extractor) { - valueExtractors.add(extractor); - return this; + private void performBootstrapOverrides() { + final BootstrapConfiguration bootstrapConfig = bootstrapConfiguration.get(); + override(messageInterpolator, bootstrapConfig::getMessageInterpolatorClassName); + override(traversableResolver, bootstrapConfig::getTraversableResolverClassName); + override(constraintValidatorFactory, bootstrapConfig::getConstraintValidatorFactoryClassName); + override(parameterNameProvider, bootstrapConfig::getParameterNameProviderClassName); + override(clockProvider, bootstrapConfig::getClockProviderClassName); } - @Override - public ClockProvider getDefaultClockProvider() { - return defaultClockProvider; + @SuppressWarnings("unchecked") + private <T> Class<T> loadClass(final String className) { + try { + return (Class<T>) Class.forName(className, true, loader); + } catch (final ClassNotFoundException ex) { + throw new ValidationException(ex); + } } - @Override - public Set<ValueExtractor<?>> getValueExtractors() { - return Collections.unmodifiableSet(valueExtractors); + private void initializePropertyDefaults() { + properties.put(Properties.CONSTRAINTS_CACHE_SIZE, Integer.toString(50)); } - @Override - public ClockProvider getClockProvider() { - if (beforeCdi) { - return defaultClockProvider; + private ValidationProvider<?> findProvider() { + if (providerClass == null) { + return providerResolver.getValidationProviders().get(0); } - if (clockProvider == defaultClockProvider && clockProviderClass != null) { - synchronized (this) { - if (clockProvider == defaultClockProvider && clockProviderClass != null) { - clockProvider = newInstance(clockProviderClass); - } - } + final Optional<ValidationProvider<?>> knownProvider = + providerResolver.getValidationProviders().stream().filter(providerClass::isInstance).findFirst(); + if (knownProvider.isPresent()) { + return knownProvider.get(); + } + try { + return providerClass.getConstructor().newInstance(); + } catch (Exception e) { + throw Exceptions.create(ValidationException::new, "Unable to find/create %s of type %s", + ValidationProvider.class.getSimpleName(), providerClass); } - return clockProvider; } - protected void initializePropertyDefaults() { - properties.put(Properties.CONSTRAINTS_CACHE_SIZE, Integer.toString(50)); + private <T> void override(LazyParticipant<T> participant, Supplier<String> getClassName) { + final String className = getClassName.get(); + if (className != null) { + final Class<T> t = loadClass(className); + participant.reset(() -> newInstance(t)); + } + } + + @Privileged + private <T> T newInstance(final Class<T> cls) { + try { + final BValExtension.Releasable<T> releasable = BValExtension.inject(cls); + releasables.add(releasable); + return releasable.getInstance(); + } catch (Exception | NoClassDefFoundError e) { + } + try { + return cls.getConstructor().newInstance(); + } catch (final Exception e) { + throw new ValidationException(e.getMessage(), e); + } } } http://git-wip-us.apache.org/repos/asf/bval/blob/36920e87/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationParser.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationParser.java b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationParser.java index bed2e10..35d510b 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationParser.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/xml/ValidationParser.java @@ -21,27 +21,21 @@ package org.apache.bval.jsr.xml; import java.io.IOException; import java.io.InputStream; import java.net.URL; -import java.util.Collection; import java.util.Collections; import java.util.EnumSet; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.CopyOnWriteArraySet; +import java.util.function.Predicate; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; -import javax.validation.ConstraintValidatorFactory; -import javax.validation.MessageInterpolator; -import javax.validation.ParameterNameProvider; -import javax.validation.TraversableResolver; +import javax.validation.BootstrapConfiguration; import javax.validation.ValidationException; import javax.validation.executable.ExecutableType; -import javax.validation.spi.ValidationProvider; import org.apache.bval.jsr.BootstrapConfigurationImpl; import org.apache.bval.jsr.ConfigurationImpl; @@ -72,64 +66,42 @@ public class ValidationParser { .build(); public static String getValidationXmlFile(String file) { - if (file == null) { - return DEFAULT_VALIDATION_XML_FILE; - } - return file; + return file == null ? DEFAULT_VALIDATION_XML_FILE : file; } - public static ValidationParser processValidationConfig(final String file, final ConfigurationImpl targetConfig, - final boolean ignoreXml) { - final ValidationParser parser = new ValidationParser(); - - if (!ignoreXml) { - parser.xmlConfig = parseXmlConfig(file); + public static BootstrapConfiguration processValidationConfig(final String file, + final ConfigurationImpl targetConfig) { + final ValidationConfigType xmlConfig = parseXmlConfig(file); + if (xmlConfig == null) { + return null; } + final boolean executableValidationEnabled; + final Set<ExecutableType> defaultValidatedExecutableTypes; - if (parser.xmlConfig == null) { // default config - final CopyOnWriteArraySet<ExecutableType> executableTypes = new CopyOnWriteArraySet<>(); - executableTypes.add(ExecutableType.CONSTRUCTORS); - executableTypes.add(ExecutableType.NON_GETTER_METHODS); - - parser.bootstrap = new BootstrapConfigurationImpl(null, null, null, null, null, Collections.emptySet(), - true, executableTypes, Collections.emptyMap(), null, Collections.emptySet()); - - targetConfig.setExecutableValidation(executableTypes); + if (xmlConfig.getExecutableValidation() == null) { + defaultValidatedExecutableTypes = BootstrapConfigurationImpl.DEFAULT_DEFAULT_VALIDATED_EXECUTABLE_TYPES; + executableValidationEnabled = true; } else { - if (parser.xmlConfig.getExecutableValidation() == null) { - final ExecutableValidationType value = new ExecutableValidationType(); - value.setEnabled(true); - - final DefaultValidatedExecutableTypesType defaultValidatedExecutableTypes = - new DefaultValidatedExecutableTypesType(); - value.setDefaultValidatedExecutableTypes(defaultValidatedExecutableTypes); - defaultValidatedExecutableTypes.getExecutableType().add(ExecutableType.CONSTRUCTORS); - defaultValidatedExecutableTypes.getExecutableType().add(ExecutableType.NON_GETTER_METHODS); - - parser.xmlConfig.setExecutableValidation(value); - } - - applySimpleConfig(parser.xmlConfig, targetConfig); + final Optional<ExecutableValidationType> executableValidation = + Optional.of(xmlConfig).map(ValidationConfigType::getExecutableValidation); + executableValidationEnabled = executableValidation.map(ExecutableValidationType::getEnabled) + .filter(Predicate.isEqual(Boolean.TRUE)).isPresent(); - parser.bootstrap = new BootstrapConfigurationImpl(parser.xmlConfig.getDefaultProvider(), - parser.xmlConfig.getConstraintValidatorFactory(), parser.xmlConfig.getMessageInterpolator(), - parser.xmlConfig.getTraversableResolver(), parser.xmlConfig.getParameterNameProvider(), - new HashSet<>(parser.xmlConfig.getConstraintMapping()), - parser.xmlConfig.getExecutableValidation().getEnabled(), - new HashSet<>(targetConfig.getExecutableValidation()), toMap(parser.xmlConfig.getProperty()), - parser.xmlConfig.getClockProvider(), new HashSet<>(parser.xmlConfig.getValueExtractor())); + defaultValidatedExecutableTypes = executableValidation.filter(x -> executableValidationEnabled) + .map(ExecutableValidationType::getDefaultValidatedExecutableTypes) + .map(DefaultValidatedExecutableTypesType::getExecutableType).map(EnumSet::copyOf) + .orElse(EnumSet.noneOf(ExecutableType.class)); } - return parser; + return new BootstrapConfigurationImpl(xmlConfig.getDefaultProvider(), xmlConfig.getConstraintValidatorFactory(), + xmlConfig.getMessageInterpolator(), xmlConfig.getTraversableResolver(), + xmlConfig.getParameterNameProvider(), new HashSet<>(xmlConfig.getConstraintMapping()), + executableValidationEnabled, defaultValidatedExecutableTypes, toMap(xmlConfig.getProperty()), + xmlConfig.getClockProvider(), new HashSet<>(xmlConfig.getValueExtractor())); } private static Map<String, String> toMap(final List<PropertyType> property) { - final Map<String, String> map = new HashMap<>(); - if (property != null) { - for (final PropertyType p : property) { - map.put(p.getName(), p.getValue()); - } - } - return map; + return property == null || property.isEmpty() ? Collections.emptyMap() + : property.stream().collect(Collectors.toMap(PropertyType::getName, PropertyType::getValue)); } @Privileged @@ -144,11 +116,27 @@ public class ValidationParser { return SCHEMA_MANAGER.unmarshal(new InputSource(inputStream), ValidationConfigType.class); } catch (Exception e) { - throw new ValidationException("Unable to parse " + validationXmlFile, e); + throw Exceptions.create(ValidationException::new, e, "Unable to parse %s", validationXmlFile); } } - protected static InputStream getInputStream(final String path) throws IOException { + public static InputStream open(String mappingFileName) { + if (mappingFileName.charAt(0) == '/') { + // Classloader needs a path without a starting / + mappingFileName = mappingFileName.substring(1); + } + try { + final InputStream in = getInputStream(mappingFileName); + Exceptions.raiseIf(in == null, ValidationException::new, + "Unable to open input stream for mapping file %s", mappingFileName); + return(in); + } catch (IOException e) { + throw Exceptions.create(ValidationException::new, e, "Unable to open input stream for mapping file %s", + mappingFileName); + } + } + + static InputStream getInputStream(final String path) throws IOException { final ClassLoader loader = Reflection.getClassLoader(ValidationParser.class); final List<URL> urls = Collections.list(loader.getResources(path)); Exceptions.raiseIf(urls.stream().distinct().count() > 1, ValidationException::new, @@ -156,158 +144,7 @@ public class ValidationParser { return urls.isEmpty() ? null : urls.get(0).openStream(); } - public static void applySimpleConfig(ValidationConfigType xmlConfig, ConfigurationImpl targetConfig) { - applyExecutableValidation(xmlConfig, targetConfig); - } - - private static void applyProperties(ValidationConfigType xmlConfig, ConfigurationImpl target) { - for (final PropertyType property : xmlConfig.getProperty()) { - target.addProperty(property.getName(), property.getValue()); - } - } - - private static void applyExecutableValidation(final ValidationConfigType xmlConfig, - final ConfigurationImpl targetConfig) { - - final Set<ExecutableType> executableTypes = Optional.of(xmlConfig) - .map(ValidationConfigType::getExecutableValidation).filter(vc -> Boolean.TRUE.equals(vc.getEnabled())) - .map(ExecutableValidationType::getDefaultValidatedExecutableTypes) - .map(DefaultValidatedExecutableTypesType::getExecutableType).map(EnumSet::copyOf) - .orElseGet(() -> EnumSet.noneOf(ExecutableType.class)); - - if (executableTypes.contains(ExecutableType.ALL)) { - executableTypes.clear(); - executableTypes.add(ExecutableType.CONSTRUCTORS); - executableTypes.add(ExecutableType.NON_GETTER_METHODS); - executableTypes.add(ExecutableType.GETTER_METHODS); - } else if (executableTypes.contains(ExecutableType.NONE)) { // if both are present ALL trumps NONE - executableTypes.clear(); - } - targetConfig.setExecutableValidation(Collections.unmodifiableSet(executableTypes)); - } - - private static void applyMappingStreams(ValidationConfigType xmlConfig, ConfigurationImpl target) { - for (String rawMappingFileName : xmlConfig.getConstraintMapping()) { - String mappingFileName = rawMappingFileName; - if (mappingFileName.charAt(0) == '/') { - // Classloader needs a path without a starting / - mappingFileName = mappingFileName.substring(1); - } - log.log(Level.FINEST, String.format("Trying to open input stream for %s", mappingFileName)); - try { - final InputStream in = getInputStream(mappingFileName); - Exceptions.raiseIf(in == null, ValidationException::new, - "Unable to open input stream for mapping file %s", mappingFileName); - target.addMapping(in); - } catch (IOException e) { - Exceptions.raise(ValidationException::new, e, "Unable to open input stream for mapping file %s", - mappingFileName); - } - } - } - - private ValidationConfigType xmlConfig; - private BootstrapConfigurationImpl bootstrap; - private Collection<ValidationException> exceptions = new CopyOnWriteArrayList<ValidationException>(); - private ValidationParser() { // no-op } - - public void applyConfigWithInstantiation(ConfigurationImpl targetConfig) { - if (xmlConfig == null) { - return; - } - - applyProviderClass(xmlConfig, targetConfig); - applyMessageInterpolator(xmlConfig, targetConfig); - applyTraversableResolver(xmlConfig, targetConfig); - applyConstraintFactory(xmlConfig, targetConfig); - applyParameterNameProvider(xmlConfig, targetConfig); - applyMappingStreams(xmlConfig, targetConfig); - applyProperties(xmlConfig, targetConfig); - } - - public BootstrapConfigurationImpl getBootstrap() { - return bootstrap; - } - - private void applyParameterNameProvider(final ValidationConfigType xmlConfig, - final ConfigurationImpl targetConfig) { - final String parameterNameProvider = xmlConfig.getParameterNameProvider(); - if (targetConfig.getParameterNameProvider() == targetConfig.getDefaultParameterNameProvider() - && parameterNameProvider != null) { - final Class<?> loaded = loadClass(parameterNameProvider); - if (loaded == null) { - log.log(Level.SEVERE, "Can't load " + parameterNameProvider); - } else { - final Class<? extends ParameterNameProvider> clazz = loaded.asSubclass(ParameterNameProvider.class); - targetConfig.parameterNameProviderClass(clazz); - log.log(Level.INFO, String.format("Using %s as validation provider.", parameterNameProvider)); - } - } - } - - @SuppressWarnings("unchecked") - private void applyProviderClass(ValidationConfigType xmlConfig, ConfigurationImpl target) { - String providerClassName = xmlConfig.getDefaultProvider(); - if (providerClassName != null) { - Class<? extends ValidationProvider<?>> clazz = - (Class<? extends ValidationProvider<?>>) loadClass(providerClassName); - target.setProviderClass(clazz); - log.log(Level.INFO, String.format("Using %s as validation provider.", providerClassName)); - } - } - - @SuppressWarnings("unchecked") - private void applyMessageInterpolator(ValidationConfigType xmlConfig, ConfigurationImpl target) { - String messageInterpolatorClass = xmlConfig.getMessageInterpolator(); - if (target.getMessageInterpolator() == target.getDefaultMessageInterpolator() - && messageInterpolatorClass != null) { - Class<MessageInterpolator> clazz = (Class<MessageInterpolator>) loadClass(messageInterpolatorClass); - target.messageInterpolatorClass(clazz); - log.log(Level.INFO, String.format("Using %s as message interpolator.", messageInterpolatorClass)); - } - } - - @SuppressWarnings("unchecked") - private void applyTraversableResolver(ValidationConfigType xmlConfig, ConfigurationImpl target) { - String traversableResolverClass = xmlConfig.getTraversableResolver(); - if (target.getTraversableResolver() == target.getDefaultTraversableResolver() - && traversableResolverClass != null) { - Class<TraversableResolver> clazz = (Class<TraversableResolver>) loadClass(traversableResolverClass); - target.traversableResolverClass(clazz); - log.log(Level.INFO, String.format("Using %s as traversable resolver.", traversableResolverClass)); - } - } - - @SuppressWarnings("unchecked") - private void applyConstraintFactory(ValidationConfigType xmlConfig, ConfigurationImpl target) { - String constraintFactoryClass = xmlConfig.getConstraintValidatorFactory(); - if (target.getConstraintValidatorFactory() == target.getDefaultConstraintValidatorFactory() - && constraintFactoryClass != null) { - Class<ConstraintValidatorFactory> clazz = - (Class<ConstraintValidatorFactory>) loadClass(constraintFactoryClass); - target.constraintValidatorFactoryClass(clazz); - log.log(Level.INFO, String.format("Using %s as constraint factory.", constraintFactoryClass)); - } - } - - private Class<?> loadClass(final String className) { - final ClassLoader loader = Reflection.getClassLoader(ValidationParser.class); - try { - return Class.forName(className, true, loader); - } catch (final ClassNotFoundException ex) { - // TCK check BootstrapConfig is present in all cases - // so throw next exception later - exceptions.add(new ValidationException("Unable to load class: " + className, ex)); - return null; - } - } - - public void ensureValidatorFactoryCanBeBuilt() { - if (!exceptions.isEmpty()) { - throw exceptions.iterator().next(); - } - } } http://git-wip-us.apache.org/repos/asf/bval/blob/36920e87/bval-jsr/src/test/java/org/apache/bval/jsr/xml/ValidationParserTest.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/test/java/org/apache/bval/jsr/xml/ValidationParserTest.java b/bval-jsr/src/test/java/org/apache/bval/jsr/xml/ValidationParserTest.java index 41d4f13..a2a6faa 100644 --- a/bval-jsr/src/test/java/org/apache/bval/jsr/xml/ValidationParserTest.java +++ b/bval-jsr/src/test/java/org/apache/bval/jsr/xml/ValidationParserTest.java @@ -82,19 +82,19 @@ public class ValidationParserTest implements ApacheValidatorConfiguration.Proper @Test public void testParse() { ConfigurationImpl config = new ConfigurationImpl(null, new ApacheValidationProvider()); - ValidationParser.processValidationConfig("sample-validation.xml", config, false); + ValidationParser.processValidationConfig("sample-validation.xml", config); } @Test public void testParseV11() { ConfigurationImpl config = new ConfigurationImpl(null, new ApacheValidationProvider()); - ValidationParser.processValidationConfig("sample-validation11.xml", config, false); + ValidationParser.processValidationConfig("sample-validation11.xml", config); } @Test public void testParseV20() { ConfigurationImpl config = new ConfigurationImpl(null, new ApacheValidationProvider()); - ValidationParser.processValidationConfig("sample-validation2.xml", config, false); + ValidationParser.processValidationConfig("sample-validation2.xml", config); } @Test