| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.bval.jsr; |
| |
| import java.io.Closeable; |
| import java.io.InputStream; |
| import java.time.Clock; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Optional; |
| import java.util.Set; |
| import java.util.concurrent.CopyOnWriteArrayList; |
| |
| import javax.validation.BootstrapConfiguration; |
| import javax.validation.ClockProvider; |
| import javax.validation.ConstraintValidatorFactory; |
| import javax.validation.MessageInterpolator; |
| import javax.validation.ParameterNameProvider; |
| 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; |
| import javax.validation.valueextraction.ValueExtractor; |
| |
| import org.apache.bval.cdi.BValExtension; |
| import org.apache.bval.jsr.parameter.DefaultParameterNameProvider; |
| 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.commons.weaver.privilizer.Privileged; |
| |
| /** |
| * Description: used to configure apache-validation for jsr. |
| * Implementation of Configuration that also implements ConfigurationState, |
| * hence this can be passed to buildValidatorFactory(ConfigurationState). |
| * <br/> |
| */ |
| public class ConfigurationImpl implements ApacheValidatorConfiguration, ConfigurationState { |
| /** |
| * Configured {@link ValidationProvider} |
| */ |
| //couldn't this be parameterized <ApacheValidatorConfiguration> or <? super ApacheValidatorConfiguration>? |
| protected final ValidationProvider<?> provider; |
| |
| /** |
| * Configured {@link ValidationProviderResolver} |
| */ |
| protected final ValidationProviderResolver providerResolver; |
| |
| /** |
| * Configured {@link ValidationProvider} class |
| */ |
| protected Class<? extends ValidationProvider<?>> providerClass; |
| |
| /** |
| * Configured {@link MessageInterpolator} |
| */ |
| protected MessageInterpolator defaultMessageInterpolator = new DefaultMessageInterpolator(); |
| protected volatile MessageInterpolator messageInterpolator = defaultMessageInterpolator; |
| protected Class<? extends MessageInterpolator> messageInterpolatorClass; |
| |
| /** |
| * Configured {@link ConstraintValidatorFactory} |
| */ |
| protected ConstraintValidatorFactory defaultConstraintValidatorFactory = new DefaultConstraintValidatorFactory(); |
| protected volatile ConstraintValidatorFactory constraintValidatorFactory = defaultConstraintValidatorFactory; |
| protected Class<? extends ConstraintValidatorFactory> constraintValidatorFactoryClass; |
| |
| protected TraversableResolver defaultTraversableResolver = new DefaultTraversableResolver(); |
| protected volatile TraversableResolver traversableResolver = defaultTraversableResolver; |
| protected Class<? extends TraversableResolver> traversableResolverClass; |
| |
| protected ParameterNameProvider defaultParameterNameProvider = new DefaultParameterNameProvider(); |
| protected volatile ParameterNameProvider parameterNameProvider = defaultParameterNameProvider; |
| protected Class<? extends ParameterNameProvider> parameterNameProviderClass; |
| |
| protected BootstrapConfiguration bootstrapConfiguration; |
| |
| protected Collection<ExecutableType> executableValidation; |
| |
| 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 boolean beforeCdi = false; |
| |
| // BEGIN DEFAULTS |
| /** |
| * false = dirty flag (to prevent from multiple parsing validation.xml) |
| */ |
| private boolean prepared = false; |
| // END DEFAULTS |
| |
| private Set<InputStream> mappingStreams = new HashSet<>(); |
| private 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) { |
| this.provider = null; |
| if (aState.getValidationProviderResolver() == null) { |
| providerResolver = aState.getDefaultValidationProviderResolver(); |
| } else { |
| providerResolver = aState.getValidationProviderResolver(); |
| } |
| } else { |
| throw new ValidationException("either provider or state are required"); |
| } |
| 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. |
| * |
| * @return this |
| */ |
| @Override |
| public ApacheValidatorConfiguration ignoreXmlConfiguration() { |
| ignoreXmlConfiguration = true; |
| return this; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public ConfigurationImpl messageInterpolator(MessageInterpolator resolver) { |
| if (resolver != null) { |
| this.messageInterpolatorClass = null; |
| this.messageInterpolator = resolver; |
| this.prepared = false; |
| } |
| return this; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public ConfigurationImpl constraintValidatorFactory(ConstraintValidatorFactory constraintFactory) { |
| if (constraintFactory != null) { |
| this.constraintValidatorFactoryClass = null; |
| this.constraintValidatorFactory = constraintFactory; |
| this.prepared = false; |
| } |
| return this; |
| } |
| |
| @Override |
| public ApacheValidatorConfiguration parameterNameProvider(ParameterNameProvider parameterNameProvider) { |
| if (parameterNameProvider != null) { |
| this.parameterNameProviderClass = null; |
| this.parameterNameProvider = parameterNameProvider; |
| } |
| return this; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * Add a stream describing constraint mapping in the Bean Validation |
| * XML format. |
| * |
| * @return this |
| */ |
| @Override |
| public ApacheValidatorConfiguration addMapping(InputStream stream) { |
| if (stream != null) { |
| mappingStreams.add(IOs.convertToMarkableInputStream(stream)); |
| } |
| return this; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * Add a provider specific property. This property is equivalent to |
| * XML configuration properties. |
| * If we do not know how to handle the property, we silently ignore it. |
| * |
| * @return this |
| */ |
| @Override |
| public ApacheValidatorConfiguration addProperty(String name, String value) { |
| if ("bval.before.cdi".equals(name)) { |
| beforeCdi = Boolean.parseBoolean(value); |
| } else { |
| properties.put(name, value); |
| } |
| return this; |
| } |
| |
| @Override |
| public MessageInterpolator getDefaultMessageInterpolator() { |
| return defaultMessageInterpolator; |
| } |
| |
| @Override |
| public TraversableResolver getDefaultTraversableResolver() { |
| return defaultTraversableResolver; |
| } |
| |
| @Override |
| public ConstraintValidatorFactory getDefaultConstraintValidatorFactory() { |
| return defaultConstraintValidatorFactory; |
| } |
| |
| @Override |
| public ParameterNameProvider getDefaultParameterNameProvider() { |
| return defaultParameterNameProvider; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * Return a map of non type-safe custom properties. |
| * |
| * @return null |
| */ |
| @Override |
| public Map<String, String> getProperties() { |
| return properties; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * Returns true if Configuration.ignoreXMLConfiguration() has been called. |
| * In this case, we ignore META-INF/validation.xml |
| * |
| * @return true |
| */ |
| @Override |
| public boolean isIgnoreXmlConfiguration() { |
| return ignoreXmlConfiguration; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public Set<InputStream> getMappingStreams() { |
| return mappingStreams; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @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; |
| } |
| |
| @Override |
| public BootstrapConfiguration getBootstrapConfiguration() { |
| return createBootstrapConfiguration(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * main factory method to build a ValidatorFactory |
| * |
| * @throws ValidationException if the ValidatorFactory cannot be built |
| */ |
| @Override |
| public ValidatorFactory buildValidatorFactory() { |
| 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; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @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; |
| } |
| |
| @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; |
| } |
| |
| /** |
| * Get the configured {@link ValidationProvider}. |
| * @return {@link ValidationProvider} |
| */ |
| public ValidationProvider<?> getProvider() { |
| return provider; |
| } |
| |
| 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); |
| } |
| } |
| |
| /** |
| * Set {@link ValidationProvider} class. |
| * @param providerClass the provider type |
| */ |
| public void setProviderClass(Class<? extends ValidationProvider<?>> providerClass) { |
| this.providerClass = providerClass; |
| } |
| |
| public void setExecutableValidation(final Collection<ExecutableType> executableValidation) { |
| this.executableValidation = executableValidation; |
| } |
| |
| public Collection<ExecutableType> getExecutableValidation() { |
| return executableValidation; |
| } |
| |
| public Closeable getClosable() { |
| return () -> { |
| for (final BValExtension.Releasable<?> releasable : releasables) { |
| releasable.release(); |
| } |
| releasables.clear(); |
| }; |
| } |
| |
| @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); |
| } |
| } |
| |
| public void traversableResolverClass(final Class<TraversableResolver> clazz) { |
| traversableResolverClass = clazz; |
| } |
| |
| public void constraintValidatorFactoryClass(final Class<ConstraintValidatorFactory> clazz) { |
| constraintValidatorFactoryClass = clazz; |
| } |
| |
| public void messageInterpolatorClass(final Class<MessageInterpolator> clazz) { |
| messageInterpolatorClass = clazz; |
| } |
| |
| public void parameterNameProviderClass(final Class<? extends ParameterNameProvider> clazz) { |
| parameterNameProviderClass = clazz; |
| } |
| |
| @Override |
| public ApacheValidatorConfiguration clockProvider(ClockProvider clockProvider) { |
| this.clockProvider = clockProvider; |
| return this; |
| } |
| |
| @Override |
| public ApacheValidatorConfiguration addValueExtractor(ValueExtractor<?> extractor) { |
| valueExtractors.add(extractor); |
| return this; |
| } |
| |
| @Override |
| public ClockProvider getDefaultClockProvider() { |
| return defaultClockProvider; |
| } |
| |
| @Override |
| public Set<ValueExtractor<?>> getValueExtractors() { |
| return Collections.unmodifiableSet(valueExtractors); |
| } |
| |
| @Override |
| public ClockProvider getClockProvider() { |
| if (beforeCdi) { |
| return defaultClockProvider; |
| } |
| if (clockProvider == defaultClockProvider && clockProviderClass != null) { |
| synchronized (this) { |
| if (clockProvider == defaultClockProvider && clockProviderClass != null) { |
| clockProvider = newInstance(clockProviderClass); |
| } |
| } |
| } |
| return clockProvider; |
| } |
| |
| protected void initializePropertyDefaults() { |
| properties.put(Properties.CONSTRAINTS_CACHE_SIZE, Integer.toString(50)); |
| } |
| } |