Added: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/CascadingPropertyValidator.java URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/CascadingPropertyValidator.java?rev=1843674&view=auto ============================================================================== --- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/CascadingPropertyValidator.java (added) +++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/CascadingPropertyValidator.java Fri Oct 12 15:00:48 2018 @@ -0,0 +1,122 @@ +/** + * 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 javax.validation.ConstraintViolation; +import javax.validation.Valid; +import javax.validation.ValidationException; +import javax.validation.Validator; + +import java.util.Set; + +/** + * Per the bean validation spec, {@link Valid} is not honored by the + * {@link #validateProperty(Object, String, Class...)} and + * {@link #validateValue(Class, String, Object, Class...)} methods. The + * {@link CascadingPropertyValidator} interface thus defines a {@link Validator} that + * provides corresponding methods that <em>may</em> honor {@link Valid}. + * It should be noted that {@link Validator#validateProperty(Object, String, Class...)} + * and {@link Validator#validateValue(Class, String, Object, Class...)} are assumed + * semantically equivalent to calling the {@link CascadingPropertyValidator}-defined + * methods with {@code cascade == false}. + * + * @version $Rev: 993539 $ $Date: 2010-09-07 16:27:50 -0500 (Tue, 07 Sep 2010) $ + */ +public interface CascadingPropertyValidator extends Validator { + + /** + * {@inheritDoc} Validates all constraints placed on the property of {@code object} named {@code propertyName}. + * + * @param object object to validate + * @param propertyName property to validate (i.e. field and getter constraints). Nested + * properties may be referenced (e.g. prop[2].subpropA.subpropB) + * @param groups group or list of groups targeted for validation (default to + * {@link javax.validation.groups.Default}) + * @return constraint violations or an empty {@link Set} if none + * @throws IllegalArgumentException if {@code object} is {@code null}, if {@code propertyName null}, + * empty or not a valid object property or if {@code null} is + * passed to the varargs {@code groups} + * @throws ValidationException if a non recoverable error happens during the validation process + */ + @Override + default <T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, Class<?>... groups) { + return validateProperty(object, propertyName, false, groups); + } + + /** + * Validates all constraints placed on the property of {@code object} named {@code propertyName}. + * + * @param object object to validate + * @param propertyName property to validate (i.e. field and getter constraints). Nested + * properties may be referenced (e.g. prop[2].subpropA.subpropB) + * @param cascade whether to cascade along {@link Valid} properties + * @param groups group or list of groups targeted for validation (default to + * {@link javax.validation.groups.Default}) + * @return constraint violations or an empty {@link Set} if none + * @throws IllegalArgumentException if {@code object} is {@code null}, if {@code propertyName null}, + * empty or not a valid object property or if {@code null} is + * passed to the varargs {@code groups} + * @throws ValidationException if a non recoverable error happens during the validation process + */ + <T> Set<javax.validation.ConstraintViolation<T>> validateProperty(T object, String propertyName, boolean cascade, + Class<?>... groups); + + /** + * {@inheritDoc} Validates all constraints placed on the property named {@code propertyName} of the class + * {@code beanType} would the property value be {@code value}. + * <p/> + * {@link ConstraintViolation} objects return {@code null} for {@link ConstraintViolation#getRootBean()} and + * {@link ConstraintViolation#getLeafBean()}. + * + * @param beanType the bean type + * @param propertyName property to validate + * @param value property value to validate + * @param groups group or list of groups targeted for validation (default to + * {@link javax.validation.groups.Default}) + * @return constraint violations or an empty {@link Set} if none + * @throws IllegalArgumentException if {@code beanType} is {@code null}, if + * {@code propertyName null}, empty or not a valid object + * property or if {@code null} is passed to the varargs {@code groups} + * @throws ValidationException if a non recoverable error happens during the validation process + */ + @Override + default <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value, + Class<?>... groups) { + return validateValue(beanType, propertyName, value, false, groups); + } + + /** + * {@inheritDoc} Validates all constraints placed on the property named {@code propertyName} of the class + * {@code beanType} would the property value be {@code value}. + * <p/> + * {@link ConstraintViolation} objects return {@code null} for {@link ConstraintViolation#getRootBean()} and + * {@link ConstraintViolation#getLeafBean()}. + * + * @param beanType the bean type + * @param propertyName property to validate + * @param value property value to validate + * @param groups group or list of groups targeted for validation (default to + * {@link javax.validation.groups.Default}) + * @return constraint violations or an empty {@link Set} if none + * @throws IllegalArgumentException if {@code beanType} is {@code null}, if + * {@code propertyName null}, empty or not a valid object + * property or if {@code null} is passed to the varargs {@code groups} + * @throws ValidationException if a non recoverable error happens during the validation process + */ + <T> Set<javax.validation.ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value, + boolean cascade, Class<?>... groups); +}
Added: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java?rev=1843674&view=auto ============================================================================== --- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java (added) +++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java Fri Oct 12 15:00:48 2018 @@ -0,0 +1,494 @@ +/* + * 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.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Supplier; + +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.spi.BootstrapState; +import javax.validation.spi.ConfigurationState; +import javax.validation.spi.ValidationProvider; +import javax.validation.valueextraction.ValueExtractor; + +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.valueextraction.ValueExtractors; +import org.apache.bval.jsr.xml.ValidationParser; +import org.apache.bval.util.CloseableAble; +import org.apache.bval.util.Exceptions; +import org.apache.bval.util.Lazy; +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, CloseableAble { + + private class LazyParticipant<T> extends Lazy<T> { + private boolean locked; + + private LazyParticipant(Supplier<T> init) { + super(init); + } + + ConfigurationImpl override(T value) { + if (value != null) { + synchronized (this) { + if (!locked) { + try { + reset(() -> value); + } finally { + ConfigurationImpl.this.prepared = false; + } + } + } + } + return ConfigurationImpl.this; + } + + synchronized ConfigurationImpl externalOverride(T value) { + locked = false; + try { + return override(value); + } finally { + locked = true; + } + } + } + + /** + * Configured {@link ValidationProvider} + */ + //couldn't this be parameterized <ApacheValidatorConfiguration> or <? super ApacheValidatorConfiguration>? + private final ValidationProvider<ApacheValidatorConfiguration> provider; + + /** + * Configured {@link ValidationProviderResolver} + */ + private final ValidationProviderResolver providerResolver; + + /** + * Configured {@link ValidationProvider} class + */ + private Class<? extends ValidationProvider<?>> providerClass; + + private final MessageInterpolator defaultMessageInterpolator = new DefaultMessageInterpolator(); + + 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); + + private final ParameterNameProvider defaultParameterNameProvider = new DefaultParameterNameProvider(); + + private final LazyParticipant<ParameterNameProvider> parameterNameProvider = + new LazyParticipant<>(this::getDefaultParameterNameProvider); + + private final ClockProvider defaultClockProvider = Clock::systemDefaultZone; + + private final LazyParticipant<ClockProvider> clockProvider = new LazyParticipant<>(this::getDefaultClockProvider); + + private final ValueExtractors bootstrapValueExtractors = ValueExtractors.EMPTY.createChild(); + private final ValueExtractors valueExtractors = bootstrapValueExtractors.createChild(); + + private final Lazy<BootstrapConfiguration> bootstrapConfiguration = new Lazy<>(this::createBootstrapConfiguration); + + private final Set<InputStream> mappingStreams = new HashSet<>(); + private final Map<String, String> properties = new HashMap<>(); + + private boolean beforeCdi = false; + private ClassLoader loader; + + // BEGIN DEFAULTS + /** + * false = dirty flag (to prevent from multiple parsing validation.xml) + */ + private boolean prepared = false; + // END DEFAULTS + + private boolean ignoreXmlConfiguration = false; + + private ParticipantFactory participantFactory; + + /** + * Create a new ConfigurationImpl instance. + * @param aState bootstrap state + * @param aProvider provider + */ + 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(); + } else { + providerResolver = aState.getValidationProviderResolver(); + } + } else { + this.provider = aProvider; + this.providerResolver = null; + } + initializePropertyDefaults(); + } + + /** + * {@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) { + return messageInterpolator.externalOverride(resolver); + } + + /** + * {@inheritDoc} + */ + @Override + public ApacheValidatorConfiguration traversableResolver(TraversableResolver resolver) { + return traversableResolver.externalOverride(resolver); + } + + /** + * {@inheritDoc} + */ + @Override + public ConfigurationImpl constraintValidatorFactory(ConstraintValidatorFactory constraintValidatorFactory) { + return this.constraintValidatorFactory.externalOverride(constraintValidatorFactory); + } + + @Override + public ApacheValidatorConfiguration parameterNameProvider(ParameterNameProvider parameterNameProvider) { + return this.parameterNameProvider.externalOverride(parameterNameProvider); + } + + @Override + public ApacheValidatorConfiguration clockProvider(ClockProvider clockProvider) { + return this.clockProvider.externalOverride(clockProvider); + } + + /** + * {@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) { + 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; + } + + @Override + public ClockProvider getDefaultClockProvider() { + return defaultClockProvider; + } + + /** + * {@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() { + return messageInterpolator.get(); + } + + @Override + public BootstrapConfiguration getBootstrapConfiguration() { + return bootstrapConfiguration.get(); + } + + /** + * {@inheritDoc} + * main factory method to build a ValidatorFactory + * + * @throws ValidationException if the ValidatorFactory cannot be built + */ + @Override + public ValidatorFactory buildValidatorFactory() { + return doBuildValidatorFactory(); + } + + /** + * {@inheritDoc} + * @return the constraint validator factory of this configuration. + */ + @Override + public ConstraintValidatorFactory getConstraintValidatorFactory() { + return constraintValidatorFactory.get(); + } + + /** + * {@inheritDoc} + */ + @Override + public TraversableResolver getTraversableResolver() { + return traversableResolver.get(); + } + + @Override + public ParameterNameProvider getParameterNameProvider() { + return parameterNameProvider.get(); + } + + @Override + public ClockProvider getClockProvider() { + return clockProvider.get(); + } + + @Override + public ApacheValidatorConfiguration addValueExtractor(ValueExtractor<?> extractor) { + valueExtractors.add(extractor); + return this; + } + + @Override + public Set<ValueExtractor<?>> getValueExtractors() { + return Collections.unmodifiableSet(new LinkedHashSet<>(valueExtractors.getValueExtractors().values())); + } + + public void deferBootstrapOverrides() { + beforeCdi = true; + } + + public void releaseDeferredBootstrapOverrides() { + if (beforeCdi) { + beforeCdi = false; + performBootstrapOverrides(); + } + } + + @Override + public Closeable getCloseable() { + if (participantFactory == null) { + return () -> { + }; + } + return participantFactory; + } + + @Privileged + private ValidatorFactory doBuildValidatorFactory() { + prepare(); + return Optional.<ValidationProvider<?>> ofNullable(provider).orElseGet(this::findProvider) + .buildValidatorFactory(this); + } + + private void prepare() { + if (!prepared) { + applyBootstrapConfiguration(); + prepared = true; + } + } + + private BootstrapConfiguration createBootstrapConfiguration() { + try { + if (!ignoreXmlConfiguration) { + loader = Thread.currentThread().getContextClassLoader(); + if (loader == null) { + loader = ValidationParser.class.getClassLoader(); + } + final BootstrapConfiguration xmlBootstrap = + ValidationParser.processValidationConfig(getProperties().get(Properties.VALIDATION_XML_PATH), this); + if (xmlBootstrap != null) { + return xmlBootstrap; + } + } + loader = Thread.currentThread().getContextClassLoader(); + if (loader == null) { + loader = ApacheValidatorFactory.class.getClassLoader(); + } + return BootstrapConfigurationImpl.DEFAULT; + } finally { + participantFactory = new ParticipantFactory(loader); + } + } + + private void applyBootstrapConfiguration() { + final BootstrapConfiguration bootstrapConfig = bootstrapConfiguration.get(); + + if (bootstrapConfig.getDefaultProviderClassName() != null) { + this.providerClass = loadClass(bootstrapConfig.getDefaultProviderClassName()); + } + bootstrapConfig.getProperties().forEach(this::addProperty); + bootstrapConfig.getConstraintMappingResourcePaths().stream().map(ValidationParser::open) + .forEach(this::addMapping); + + if (!beforeCdi) { + performBootstrapOverrides(); + } + } + + 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); + + bootstrapConfig.getValueExtractorClassNames().stream().<ValueExtractor<?>> map(participantFactory::create) + .forEach(bootstrapValueExtractors::add); + } + + @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); + } + } + + private void initializePropertyDefaults() { + properties.put(Properties.CONSTRAINTS_CACHE_SIZE, Integer.toString(50)); + } + + 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); + } + } + + private <T> void override(LazyParticipant<T> participant, Supplier<String> getClassName) { + Optional.ofNullable(getClassName.get()).<T> map(participantFactory::create).ifPresent(participant::override); + } +} Added: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintAnnotationAttributes.java URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintAnnotationAttributes.java?rev=1843674&view=auto ============================================================================== --- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintAnnotationAttributes.java (added) +++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintAnnotationAttributes.java Fri Oct 12 15:00:48 2018 @@ -0,0 +1,242 @@ +/** + * 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.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.Predicate; + +import javax.validation.Constraint; +import javax.validation.ConstraintTarget; +import javax.validation.Payload; + +import org.apache.bval.util.Exceptions; +import org.apache.bval.util.ObjectUtils; +import org.apache.bval.util.Validate; +import org.apache.bval.util.reflection.Reflection; +import org.apache.bval.util.reflection.TypeUtils; +import org.apache.commons.weaver.privilizer.Privilizing; +import org.apache.commons.weaver.privilizer.Privilizing.CallTo; + +/** + * Defines the well-known attributes of {@link Constraint} annotations. + * + * @version $Rev: 1165923 $ $Date: 2011-09-06 18:07:53 -0500 (Tue, 06 Sep 2011) $ + */ +@Privilizing(@CallTo(Reflection.class)) +public enum ConstraintAnnotationAttributes { + /** + * "message" + */ + MESSAGE("message", m -> true), + + /** + * "groups" + */ + GROUPS("groups", ObjectUtils::isEmptyArray), + + /** + * "payload" + */ + PAYLOAD("payload", ObjectUtils::isEmptyArray), + + /** + * "validationAppliesTo" + */ + VALIDATION_APPLIES_TO("validationAppliesTo", Predicate.isEqual(ConstraintTarget.IMPLICIT)), + + /** + * "value" for multi-valued constraints + */ + VALUE("value", ObjectUtils::isEmptyArray); + + @SuppressWarnings("unused") + private static class Types { + String message; + Class<?>[] groups; + Class<? extends Payload>[] payload; + Annotation[] value; + ConstraintTarget validationAppliesTo; + } + + private static final Set<ConstraintAnnotationAttributes> MANDATORY = + Collections.unmodifiableSet(EnumSet.of(ConstraintAnnotationAttributes.MESSAGE, + ConstraintAnnotationAttributes.GROUPS, ConstraintAnnotationAttributes.PAYLOAD)); + + private final Class<?> type; + private final String attributeName; + private final Predicate<Object> validateDefaultValue; + + private ConstraintAnnotationAttributes(final String name, Predicate<Object> validateDefaultValue) { + this.attributeName = name; + try { + this.type = Types.class.getDeclaredField(getAttributeName()).getType(); + } catch (Exception e) { + // should never happen + throw new RuntimeException(e); + } + this.validateDefaultValue = Validate.notNull(validateDefaultValue, "validateDefaultValue"); + } + + /** + * Get the expected type of the represented attribute. + */ + public Class<?> getType() { + return type; + } + + /** + * Get the attribute name represented. + * + * @return String + */ + public String getAttributeName() { + return attributeName; + } + + @Override + public String toString() { + return attributeName; + } + + /** + * Put <code>value</code> into a map with <code>this.attributeName</code> as + * key. + * + * @param <V> + * @param map + * @param value + * @return previous value mapped to <code>this.attributeName</code> + */ + public <V> Object put(Map<? super String, ? super V> map, V value) { + return map.put(getAttributeName(), value); + } + + /** + * Get the value of <code>this.attributeName</code> from <code>map</code>. + * + * @param <V> + * @param map + * @return V if you say so + */ + public <V> V get(Map<? super String, ? super V> map) { + @SuppressWarnings("unchecked") + final V result = (V) map.get(getAttributeName()); + if (!TypeUtils.isInstance(result, getType())) { + Exceptions.raise(IllegalStateException::new, "Invalid '%s' value: %s", getAttributeName(), result); + } + return result; + } + + public <C extends Annotation> Worker<C> analyze(final Class<C> clazz) { + if (clazz.getName().startsWith("javax.validation.constraint.")) { // cache only APIs classes to avoid memory leaks + @SuppressWarnings({ "unchecked", "rawtypes" }) + final Worker<C> w = (Worker<C>) WORKER_CACHE.computeIfAbsent(clazz, c -> new Worker((c))); + return w; + } + return new Worker<C>(clazz); + } + + public boolean isMandatory() { + return MANDATORY.contains(this); + } + + public boolean isValidDefaultValue(Object o) { + return validateDefaultValue.test(o); + } + + // this is static but related to Worker + private static final ConcurrentMap<Class<?>, Worker<?>> WORKER_CACHE = new ConcurrentHashMap<>(); + private static final ConcurrentMap<Class<?>, ConcurrentMap<String, Method>> METHOD_BY_NAME_AND_CLASS = + new ConcurrentHashMap<>(); + private static final Method NULL_METHOD; + static { + try { + NULL_METHOD = Object.class.getMethod("hashCode"); // whatever, the only constraint here is to not use a constraint method, this value is used to cache null + } catch (NoSuchMethodException e) { + throw new RuntimeException("Impossible normally"); + } + } + + @Privilizing(@CallTo(Reflection.class)) + public class Worker<C extends Annotation> { + + public final Method method; + + /** + * Create a new Worker instance. + * @param constraintType to handle + */ + Worker(final Class<C> constraintType) { + method = findMethod(constraintType, attributeName); + } + + private Method findMethod(final Class<C> constraintType, final String attributeName) { + ConcurrentMap<String, Method> cache = + METHOD_BY_NAME_AND_CLASS.computeIfAbsent(constraintType, t -> new ConcurrentHashMap<>()); + + final Method found = cache.get(attributeName); + if (found != null) { + return found; + } + final Method m = Reflection.getPublicMethod(constraintType, attributeName); + if (m == null) { + cache.putIfAbsent(attributeName, NULL_METHOD); + return null; + } + return cache.computeIfAbsent(attributeName, s -> m); + } + + public boolean isValid() { + return method != null && method != NULL_METHOD && TypeUtils.isAssignable(method.getReturnType(), type); + } + + /** + * @since 2.0 + * @return {@link Type} + */ + public Type getSpecificType() { + return isValid() ? method.getGenericReturnType() : type; + } + + public <T> T read(final Annotation constraint) { + @SuppressWarnings("unchecked") + final T result = (T) doInvoke(constraint); + return result; + } + + private Object doInvoke(final Annotation constraint) { + final boolean unset = Reflection.setAccessible(method, true); + try { + return method.invoke(constraint); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + if (unset) { + Reflection.setAccessible(method, false); + } + } + } + } +} Added: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintCached.java URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintCached.java?rev=1843674&view=auto ============================================================================== --- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintCached.java (added) +++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintCached.java Fri Oct 12 15:00:48 2018 @@ -0,0 +1,144 @@ +/* + * 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.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.validation.ConstraintDefinitionException; +import javax.validation.ConstraintValidator; +import javax.validation.constraintvalidation.SupportedValidationTarget; +import javax.validation.constraintvalidation.ValidationTarget; + +import org.apache.bval.jsr.metadata.AnnotationDeclaredValidatorMappingProvider; +import org.apache.bval.jsr.metadata.CompositeValidatorMappingProvider; +import org.apache.bval.jsr.metadata.DualValidationMappingProvider; +import org.apache.bval.jsr.metadata.ValidatorMappingProvider; +import org.apache.bval.jsr.util.ToUnmodifiable; +import org.apache.bval.util.Exceptions; +import org.apache.bval.util.Lazy; +import org.apache.bval.util.Validate; + +/** + * Description: hold the relationship annotation->validatedBy[] + * ConstraintValidator classes that are already parsed in a cache.<br/> + */ +public class ConstraintCached { + + /** + * Describes a {@link ConstraintValidator} implementation type. + * + * @since 2.0 + */ + public static final class ConstraintValidatorInfo<T extends Annotation> { + private static final Set<ValidationTarget> DEFAULT_VALIDATION_TARGETS = + Collections.singleton(ValidationTarget.ANNOTATED_ELEMENT); + + private final Class<? extends ConstraintValidator<T, ?>> type; + private Set<ValidationTarget> supportedTargets; + + ConstraintValidatorInfo(Class<? extends ConstraintValidator<T, ?>> type) { + super(); + this.type = Validate.notNull(type); + final SupportedValidationTarget svt = type.getAnnotation(SupportedValidationTarget.class); + + supportedTargets = svt == null ? DEFAULT_VALIDATION_TARGETS + : Collections.unmodifiableSet(EnumSet.copyOf(Arrays.asList(svt.value()))); + + if (supportedTargets.isEmpty()) { + Exceptions.raise(ConstraintDefinitionException::new, "Illegally specified 0-length %s value on %s", + SupportedValidationTarget.class.getSimpleName(), type); + } + } + + public Class<? extends ConstraintValidator<T, ?>> getType() { + return type; + } + + public Set<ValidationTarget> getSupportedTargets() { + return supportedTargets; + } + + @Override + public boolean equals(Object obj) { + return obj == this + || obj instanceof ConstraintValidatorInfo<?> && ((ConstraintValidatorInfo<?>) obj).type.equals(type); + } + + @Override + public int hashCode() { + return Objects.hash(type); + } + } + + private final Map<Class<? extends Annotation>, Set<ConstraintValidatorInfo<?>>> constraintValidatorInfo = + new HashMap<>(); + + private final List<ValidatorMappingProvider> customValidatorMappingProviders = new ArrayList<>(); + private final Lazy<ValidatorMappingProvider> validatorMappingProvider = + new Lazy<>(this::createValidatorMappingProvider); + + public void add(ValidatorMappingProvider validatorMappingProvider) { + customValidatorMappingProviders.add(validatorMappingProvider); + this.validatorMappingProvider.reset(this::createValidatorMappingProvider); + } + + public <A extends Annotation> List<Class<? extends ConstraintValidator<A, ?>>> getConstraintValidatorClasses( + Class<A> constraintType) { + final Set<ConstraintValidatorInfo<A>> infos = infos(constraintType); + return infos == null ? Collections.emptyList() + : infos.stream().map(ConstraintValidatorInfo::getType).collect(ToUnmodifiable.list()); + } + + public <A extends Annotation> Set<ConstraintValidatorInfo<A>> getConstraintValidatorInfo(Class<A> constraintType) { + return Collections.unmodifiableSet(infos(constraintType)); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private <A extends Annotation> Set<ConstraintValidatorInfo<A>> infos(Class<A> constraintType) { + return (Set) constraintValidatorInfo.computeIfAbsent(constraintType, + c -> validatorMappingProvider.get().getValidatorMapping(c).getValidatorTypes().stream() + .map(ConstraintValidatorInfo::new).collect(Collectors.toSet())); + } + + private ValidatorMappingProvider createValidatorMappingProvider() { + final ValidatorMappingProvider configured; + if (customValidatorMappingProviders.isEmpty()) { + configured = AnnotationDeclaredValidatorMappingProvider.INSTANCE; + } else { + final ValidatorMappingProvider custom; + if (customValidatorMappingProviders.size() == 1) { + custom = customValidatorMappingProviders.get(0); + } else { + custom = new CompositeValidatorMappingProvider(customValidatorMappingProviders); + } + configured = new DualValidationMappingProvider(AnnotationDeclaredValidatorMappingProvider.INSTANCE, custom); + } + return new DualValidationMappingProvider(ConstraintDefaults.INSTANCE, configured); + } +} Added: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintDefaults.java URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintDefaults.java?rev=1843674&view=auto ============================================================================== --- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintDefaults.java (added) +++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintDefaults.java Fri Oct 12 15:00:48 2018 @@ -0,0 +1,90 @@ +/* + * 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.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.validation.ConstraintValidator; + +import org.apache.bval.jsr.metadata.ClassLoadingValidatorMappingProvider; +import org.apache.bval.jsr.metadata.ValidatorMapping; +import org.apache.bval.util.StringUtils; +import org.apache.bval.util.reflection.Reflection; +import org.apache.commons.weaver.privilizer.Privilizing; +import org.apache.commons.weaver.privilizer.Privilizing.CallTo; + +/** + * Description: Provides access to the default constraints/validator + * implementation classes built into the framework. These are configured in + * DefaultConstraints.properties.<br/> + */ +@Privilizing(@CallTo(Reflection.class)) +public class ConstraintDefaults extends ClassLoadingValidatorMappingProvider { + public static final ConstraintDefaults INSTANCE = new ConstraintDefaults(); + + private static final Logger log = Logger.getLogger(ConstraintDefaults.class.getName()); + private static final String DEFAULT_CONSTRAINTS = "org/apache/bval/jsr/DefaultConstraints.properties"; + + private final Properties properties; + + /** + * Create a new ConstraintDefaults instance. + */ + private ConstraintDefaults() { + this.properties = loadProperties(DEFAULT_CONSTRAINTS); + } + + private Properties loadProperties(String resource) { + final Properties result = new Properties(); + final ClassLoader classloader = getClassLoader(); + try (final InputStream stream = classloader.getResourceAsStream(resource)) { + if (stream == null) { + log.log(Level.WARNING, String.format("Cannot find %s", resource)); + } else { + result.load(stream); + } + } catch (IOException e) { + log.log(Level.SEVERE, String.format("Cannot load %s", resource), e); + } + return result; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public <A extends Annotation> ValidatorMapping<A> doGetValidatorMapping(Class<A> constraintType) { + + final String validators = properties.getProperty(constraintType.getName()); + + if (StringUtils.isBlank(validators)) { + return null; + } + return new ValidatorMapping<>("built-in", + load(Stream.of(StringUtils.split(validators, ',')).map(String::trim), + (Class<ConstraintValidator<A, ?>>) (Class) ConstraintValidator.class, + e -> log.log(Level.SEVERE, "exception loading default constraint validators", e)) + .collect(Collectors.toList())); + } +} Added: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintViolationImpl.java URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintViolationImpl.java?rev=1843674&view=auto ============================================================================== --- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintViolationImpl.java (added) +++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintViolationImpl.java Fri Oct 12 15:00:48 2018 @@ -0,0 +1,214 @@ +/* + * 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 javax.validation.ConstraintViolation; +import javax.validation.Path; +import javax.validation.ValidationException; +import javax.validation.metadata.ConstraintDescriptor; + +import org.apache.bval.util.Exceptions; + +import java.io.Serializable; +import java.lang.annotation.ElementType; +import java.util.Arrays; +import java.util.Objects; + +/** + * Description: Describe a constraint validation defect.<br/> + * From rootBean and propertyPath, it is possible to rebuild the context of the + * failure + */ +public class ConstraintViolationImpl<T> implements ConstraintViolation<T>, Serializable { + /** Serialization version */ + private static final long serialVersionUID = 1L; + + private final String messageTemplate; + private final String message; + /** root bean validation was invoked on. */ + private final T rootBean; + private final Class<T> rootBeanClass; + /** last bean validated. */ + private final Object leafBean; + private final Object value; + private final Path propertyPath; + private final ElementType elementType; + private final ConstraintDescriptor<?> constraintDescriptor; + private final Object returnValue; + private final Object[] parameters; + private final int hashCode; + + /** + * Create a new ConstraintViolationImpl instance. + * + * @param messageTemplate + * - message reason (raw message) + * @param message + * - interpolated message (locale specific) + * @param rootBean + * @param leafBean + * @param propertyPath + * @param value + * @param constraintDescriptor + * @param rootBeanClass + * @param elementType + * @param returnValue + * @param parameters + */ + public ConstraintViolationImpl(String messageTemplate, String message, T rootBean, Object leafBean, + Path propertyPath, Object value, ConstraintDescriptor<?> constraintDescriptor, Class<T> rootBeanClass, + ElementType elementType, Object returnValue, Object[] parameters) { + this.messageTemplate = messageTemplate; + this.message = message; + this.rootBean = rootBean; + this.rootBeanClass = rootBeanClass; + this.propertyPath = propertyPath; + this.leafBean = leafBean; + this.value = value; + this.constraintDescriptor = constraintDescriptor; + this.elementType = elementType; + this.returnValue = returnValue; + this.parameters = parameters; + this.hashCode = Arrays.deepHashCode(new Object[] { messageTemplate, message, rootBean, rootBeanClass, leafBean, + value, propertyPath, elementType, constraintDescriptor, returnValue, parameters }); + } + + /** + * {@inheritDoc} former name getInterpolatedMessage() + * + * @return The interpolated error message for this constraint violation. + */ + @Override + public String getMessage() { + return message; + } + + /** + * {@inheritDoc} + */ + @Override + public String getMessageTemplate() { + return messageTemplate; + } + + /** + * {@inheritDoc} + * + * @return Root bean being validated + */ + @Override + public T getRootBean() { + return rootBean; + } + + /** + * {@inheritDoc} + */ + @Override + public Class<T> getRootBeanClass() { + return rootBeanClass; + } + + /** + * {@inheritDoc} + */ + @Override + public Object getLeafBean() { + return leafBean; + } + + @Override + public Object[] getExecutableParameters() { + return parameters; + } + + @Override + public Object getExecutableReturnValue() { + return returnValue; + } + + /** + * {@inheritDoc} + * + * @return The value failing to pass the constraint + */ + @Override + public Object getInvalidValue() { + return value; + } + + /** + * {@inheritDoc} + * + * @return the property path to the value from <code>rootBean</code> Null if + * the value is the rootBean itself + */ + @Override + public Path getPropertyPath() { + return propertyPath; + } + + /** + * {@inheritDoc} + */ + @Override + public ConstraintDescriptor<?> getConstraintDescriptor() { + return constraintDescriptor; + } + + @Override + public <U> U unwrap(Class<U> type) { + if (!type.isInstance(this)) { + Exceptions.raise(ValidationException::new, "Type %s is not supported", type); + } + return type.cast(this); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return String.format("%s{rootBean=%s, propertyPath='%s', message='%s', leafBean=%s, value=%s}", + ConstraintViolationImpl.class.getSimpleName(), rootBean, propertyPath, message, leafBean, value); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || !getClass().equals(o.getClass())) { + return false; + } + + @SuppressWarnings("rawtypes") + final ConstraintViolationImpl that = (ConstraintViolationImpl) o; + + return Objects.equals(constraintDescriptor, that.constraintDescriptor) && elementType == that.elementType + && Objects.equals(leafBean, that.leafBean) && Objects.equals(message, that.message) + && Objects.equals(messageTemplate, that.messageTemplate) && Arrays.equals(parameters, that.parameters) + && Objects.equals(propertyPath, that.propertyPath) && Objects.equals(returnValue, that.returnValue) + && Objects.equals(rootBean, that.rootBean) && Objects.equals(rootBeanClass, that.rootBeanClass) + && Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return hashCode; + } +} Added: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultConstraintValidatorFactory.java URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultConstraintValidatorFactory.java?rev=1843674&view=auto ============================================================================== --- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultConstraintValidatorFactory.java (added) +++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultConstraintValidatorFactory.java Fri Oct 12 15:00:48 2018 @@ -0,0 +1,92 @@ +/* + * 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 org.apache.bval.cdi.BValExtension; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorFactory; +import javax.validation.ValidationException; +import java.io.Closeable; +import java.io.IOException; +import java.util.Collection; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Description: create constraint instances with the default / no-arg constructor <br/> + */ +public class DefaultConstraintValidatorFactory implements ConstraintValidatorFactory, Closeable { + private final Collection<BValExtension.Releasable<?>> releasables = new CopyOnWriteArrayList<>(); + private volatile Boolean useCdi = null; // store it to avoid NoClassDefFoundError when cdi is not present (it is slow) + lazily (to wait cdi is started) + + /** + * Instantiate a Constraint. + * + * @return Returns a new Constraint instance + * The ConstraintFactory is <b>not</b> responsible for calling Constraint#initialize + */ + @Override + public <T extends ConstraintValidator<?, ?>> T getInstance(final Class<T> constraintClass) { + if (useCdi == null) { + synchronized (this) { + if (useCdi == null) { + try { + useCdi = BValExtension.getBeanManager() != null; + } catch (NoClassDefFoundError | Exception error) { + useCdi = Boolean.FALSE; + } + } + } + } + + // 2011-03-27 jw: Do not use PrivilegedAction. + // Otherwise any user code would be executed with the privileges of this class. + try { + if (useCdi) { + try { + final BValExtension.Releasable<T> instance = BValExtension.inject(constraintClass); + if (instance != null) { + releasables.add(instance); + return instance.getInstance(); + } + throw new IllegalStateException("Can't create " + constraintClass.getName()); + } catch (Exception | NoClassDefFoundError e) { + } + } + return constraintClass.getConstructor().newInstance(); + } catch (final Exception ex) { + throw new ValidationException("Cannot instantiate : " + constraintClass, ex); + } + } + + @Override + public void releaseInstance(final ConstraintValidator<?, ?> instance) { + // no-op + } + + @Override + public void close() throws IOException { + for (final BValExtension.Releasable<?> releasable : releasables) { + // ensure to call this callback + releaseInstance(ConstraintValidator.class.cast(releasable.getInstance())); + releasable.release(); + } + releasables.clear(); + } +} Added: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultMessageInterpolator.java URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultMessageInterpolator.java?rev=1843674&view=auto ============================================================================== --- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultMessageInterpolator.java (added) +++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultMessageInterpolator.java Fri Oct 12 15:00:48 2018 @@ -0,0 +1,300 @@ +/* + * 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 org.apache.bval.el.MessageEvaluator; +import org.apache.bval.util.reflection.Reflection; +import org.apache.commons.weaver.privilizer.Privilizing; +import org.apache.commons.weaver.privilizer.Privilizing.CallTo; + +import javax.validation.MessageInterpolator; + +import java.util.Arrays; +import java.util.Locale; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Description: Resource bundle backed message interpolator. + * This message resolver resolve message descriptors + * into human-readable messages. It uses ResourceBundles to find the messages. + * This class is threadsafe.<br/> + */ +@Privilizing(@CallTo(Reflection.class)) +public class DefaultMessageInterpolator implements MessageInterpolator { + private static final Logger log = Logger.getLogger(DefaultMessageInterpolator.class.getName()); + private static final boolean LOG_FINEST = log.isLoggable(Level.FINEST); + private static final String DEFAULT_VALIDATION_MESSAGES = "org.apache.bval.jsr.ValidationMessages"; + private static final String USER_VALIDATION_MESSAGES = "ValidationMessages"; + + /** Regular expression used to do message interpolation. */ + private static final Pattern messageParameterPattern = Pattern.compile("(\\{[\\w\\.]+\\})"); + + /** The default locale for the current user. */ + private Locale defaultLocale; + + /** User specified resource bundles hashed against their locale. */ + private final Map<Locale, ResourceBundle> userBundlesMap = new ConcurrentHashMap<>(); + + /** Builtin resource bundles hashed against their locale. */ + private final Map<Locale, ResourceBundle> defaultBundlesMap = new ConcurrentHashMap<>(); + + private final MessageEvaluator evaluator; + + /** + * Create a new DefaultMessageInterpolator instance. + */ + public DefaultMessageInterpolator() { + this(null); + } + + /** + * Create a new DefaultMessageInterpolator instance. + * @param resourceBundle + */ + public DefaultMessageInterpolator(ResourceBundle resourceBundle) { + defaultLocale = Locale.getDefault(); + + // feed the cache with defaults at least + findDefaultResourceBundle(defaultLocale); + if (resourceBundle == null) { + findUserResourceBundle(defaultLocale); + } else { + userBundlesMap.put(defaultLocale, resourceBundle); + } + + MessageEvaluator ev; + try { + ev = MessageEvaluator.class + .cast(getClass().getClassLoader().loadClass("org.apache.bval.el.ELFacade") + .getConstructor().newInstance()); + } catch (final Throwable e) { // can be exception or error + ev = null; + } + evaluator = ev; + } + + /** {@inheritDoc} */ + @Override + public String interpolate(String message, Context context) { + // probably no need for caching, but it could be done by parameters since the map + // is immutable and uniquely built per Validation definition, the comparison has to be based on == and not equals though + return interpolate(message, context, defaultLocale); + } + + /** {@inheritDoc} */ + @Override + public String interpolate(String message, Context context, Locale locale) { + return interpolateMessage(message, context.getConstraintDescriptor().getAttributes(), locale, + context.getValidatedValue()); + } + + /** + * Runs the message interpolation according to algorithm specified in JSR 303. + * <br/> + * Note: + * <br/> + * Lookups in user bundles are recursive whereas lookups in default bundle are not! + * + * @param message the message to interpolate + * @param annotationParameters the parameters of the annotation for which to interpolate this message + * @param locale the <code>Locale</code> to use for the resource bundle. + * @return the interpolated message. + */ + private String interpolateMessage(String message, Map<String, Object> annotationParameters, Locale locale, + Object validatedValue) { + ResourceBundle userResourceBundle = findUserResourceBundle(locale); + ResourceBundle defaultResourceBundle = findDefaultResourceBundle(locale); + + String userBundleResolvedMessage; + String resolvedMessage = message; + boolean evaluatedDefaultBundleOnce = false; + do { + // search the user bundle recursive (step1) + userBundleResolvedMessage = replaceVariables(resolvedMessage, userResourceBundle, locale, true); + + // exit condition - we have at least tried to validate against the default bundle and there were no + // further replacements + if (evaluatedDefaultBundleOnce && !hasReplacementTakenPlace(userBundleResolvedMessage, resolvedMessage)) { + break; + } + + // search the default bundle non recursive (step2) + resolvedMessage = replaceVariables(userBundleResolvedMessage, defaultResourceBundle, locale, false); + + evaluatedDefaultBundleOnce = true; + } while (true); + + // resolve annotation attributes (step 4) + resolvedMessage = replaceAnnotationAttributes(resolvedMessage, annotationParameters); + + // EL handling + if (evaluator != null) { + resolvedMessage = evaluator.interpolate(resolvedMessage, annotationParameters, validatedValue); + } + + // curly braces need to be scaped in the original msg, so unescape them now + resolvedMessage = + resolvedMessage.replace("\\{", "{").replace("\\}", "}").replace("\\\\", "\\").replace("\\$", "$"); + + return resolvedMessage; + } + + private boolean hasReplacementTakenPlace(String origMessage, String newMessage) { + return !origMessage.equals(newMessage); + } + + /** + * Search current thread classloader for the resource bundle. If not found, search validator (this) classloader. + * + * @param locale The locale of the bundle to load. + * @return the resource bundle or <code>null</code> if none is found. + */ + private ResourceBundle getFileBasedResourceBundle(Locale locale) { + ResourceBundle rb; + final ClassLoader classLoader = Reflection.getClassLoader(DefaultMessageInterpolator.class); + if (classLoader != null) { + rb = loadBundle(classLoader, locale, USER_VALIDATION_MESSAGES + " not found by thread local classloader"); + } else { + // 2011-03-27 jw: No privileged action required. + // A class can always access the classloader of itself and of subclasses. + rb = loadBundle(getClass().getClassLoader(), locale, + USER_VALIDATION_MESSAGES + " not found by validator classloader"); + } + if (LOG_FINEST) { + if (rb == null) { + log.log(Level.FINEST, String.format("%s not found. Delegating to %s", USER_VALIDATION_MESSAGES, + DEFAULT_VALIDATION_MESSAGES)); + } else { + log.log(Level.FINEST, String.format("%s found", USER_VALIDATION_MESSAGES)); + } + } + return rb; + } + + private ResourceBundle loadBundle(ClassLoader classLoader, Locale locale, String message) { + try { + return ResourceBundle.getBundle(USER_VALIDATION_MESSAGES, locale, classLoader); + } catch (final MissingResourceException e) { + log.fine(message); + } + return null; + } + + private String replaceVariables(String message, ResourceBundle bundle, Locale locale, boolean recurse) { + final Matcher matcher = messageParameterPattern.matcher(message); + final StringBuffer sb = new StringBuffer(64); + while (matcher.find()) { + final String parameter = matcher.group(1); + String resolvedParameterValue = resolveParameter(parameter, bundle, locale, recurse); + matcher.appendReplacement(sb, sanitizeForAppendReplacement(resolvedParameterValue)); + } + matcher.appendTail(sb); + return sb.toString(); + } + + private String replaceAnnotationAttributes(final String message, final Map<String, Object> annotationParameters) { + Matcher matcher = messageParameterPattern.matcher(message); + StringBuffer sb = new StringBuffer(64); + while (matcher.find()) { + String resolvedParameterValue; + String parameter = matcher.group(1); + Object variable = annotationParameters.get(removeCurlyBrace(parameter)); + if (variable != null) { + if (variable.getClass().isArray()) { + resolvedParameterValue = Arrays.toString((Object[]) variable); + } else { + resolvedParameterValue = variable.toString(); + } + } else { + resolvedParameterValue = parameter; + } + matcher.appendReplacement(sb, sanitizeForAppendReplacement(resolvedParameterValue)); + } + matcher.appendTail(sb); + return sb.toString(); + } + + private String resolveParameter(String parameterName, ResourceBundle bundle, Locale locale, boolean recurse) { + String parameterValue; + try { + if (bundle == null) { + parameterValue = parameterName; + } else { + parameterValue = bundle.getString(removeCurlyBrace(parameterName)); + if (recurse) { + parameterValue = replaceVariables(parameterValue, bundle, locale, recurse); + } + } + } catch (final MissingResourceException e) { + // return parameter itself + parameterValue = parameterName; + } + + return parameterValue; + } + + private String removeCurlyBrace(String parameter) { + return parameter.substring(1, parameter.length() - 1); + } + + private ResourceBundle findDefaultResourceBundle(Locale locale) { + ResourceBundle bundle = defaultBundlesMap.get(locale); + if (bundle == null) { + bundle = ResourceBundle.getBundle(DEFAULT_VALIDATION_MESSAGES, locale); + defaultBundlesMap.put(locale, bundle); + } + return bundle; + } + + private ResourceBundle findUserResourceBundle(Locale locale) { + ResourceBundle bundle = userBundlesMap.get(locale); + if (bundle == null) { + bundle = getFileBasedResourceBundle(locale); + if (bundle != null) { + userBundlesMap.put(locale, bundle); + } + } + return bundle; + } + + /** + * Set the default locale used by this {@link DefaultMessageInterpolator}. + * @param locale + */ + public void setLocale(Locale locale) { + defaultLocale = locale; + } + + /** + * Escapes the string to comply with + * {@link Matcher#appendReplacement(StringBuffer, String)} requirements. + * + * @param src + * The original string. + * @return The sanitized string. + */ + private String sanitizeForAppendReplacement(String src) { + return src.replace("\\", "\\\\").replace("$", "\\$"); + } +} Added: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultValidationProviderResolver.java URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultValidationProviderResolver.java?rev=1843674&view=auto ============================================================================== --- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultValidationProviderResolver.java (added) +++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultValidationProviderResolver.java Fri Oct 12 15:00:48 2018 @@ -0,0 +1,84 @@ +/* + * 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.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; + +import javax.validation.ValidationException; +import javax.validation.ValidationProviderResolver; +import javax.validation.spi.ValidationProvider; + +import org.apache.bval.util.reflection.Reflection; +import org.apache.commons.weaver.privilizer.Privilizing; +import org.apache.commons.weaver.privilizer.Privilizing.CallTo; + +@Privilizing(@CallTo(Reflection.class)) +public class DefaultValidationProviderResolver implements ValidationProviderResolver { + + //TODO - Spec recommends caching per classloader + private static final String SPI_CFG = "META-INF/services/javax.validation.spi.ValidationProvider"; + + private static ClassLoader getCurrentClassLoader() { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + return cl == null ? DefaultValidationProviderResolver.class.getClassLoader() : cl; + } + + /** + * {@inheritDoc} + */ + @Override + public List<ValidationProvider<?>> getValidationProviders() { + List<ValidationProvider<?>> providers = new ArrayList<ValidationProvider<?>>(); + try { + // get our classloader + ClassLoader cl = getCurrentClassLoader(); + // find all service provider cfgs + Enumeration<URL> cfgs = cl.getResources(SPI_CFG); + while (cfgs.hasMoreElements()) { + final URL url = cfgs.nextElement(); + try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()), 256)) { + br.lines().filter(s -> s.charAt(0) != '#').map(String::trim).forEach(line -> { + // cfgs may contain multiple providers and/or comments + try { + // try loading the specified class + @SuppressWarnings("rawtypes") + final Class<? extends ValidationProvider> providerType = + cl.loadClass(line).asSubclass(ValidationProvider.class); + // create an instance to return + providers.add(Reflection.newInstance(providerType)); + } catch (ClassNotFoundException e) { + throw new ValidationException( + "Failed to load provider " + line + " configured in file " + url, e); + } + }); + } catch (IOException e) { + throw new ValidationException("Error trying to read " + url, e); + } + } + } catch (IOException e) { + throw new ValidationException("Error trying to read a " + SPI_CFG, e); + } + // caller must handle the case of no providers found + return providers; + } +} Added: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/GraphContext.java URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/GraphContext.java?rev=1843674&view=auto ============================================================================== --- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/GraphContext.java (added) +++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/GraphContext.java Fri Oct 12 15:00:48 2018 @@ -0,0 +1,153 @@ +/* + * 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.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.Map; +import java.util.Objects; + +import javax.validation.Path; +import javax.validation.ValidationException; + +import org.apache.bval.jsr.metadata.ContainerElementKey; +import org.apache.bval.jsr.util.NodeImpl; +import org.apache.bval.jsr.util.PathImpl; +import org.apache.bval.util.Exceptions; +import org.apache.bval.util.Validate; +import org.apache.bval.util.reflection.TypeUtils; + +public class GraphContext { + + private final ApacheFactoryContext validatorContext; + private final PathImpl path; + private final Object value; + private final GraphContext parent; + + public GraphContext(ApacheFactoryContext validatorContext, PathImpl path, Object value) { + this(validatorContext, path, value, null); + } + + private GraphContext(ApacheFactoryContext validatorContext, PathImpl path, Object value, GraphContext parent) { + super(); + this.validatorContext = Validate.notNull(validatorContext, "validatorContext"); + this.path = Validate.notNull(path, "path"); + this.value = value; + this.parent = parent; + } + + public ApacheFactoryContext getValidatorContext() { + return validatorContext; + } + + public PathImpl getPath() { + return PathImpl.copy(path); + } + + public Object getValue() { + return value; + } + + public GraphContext child(NodeImpl node, Object value) { + Validate.notNull(node, "node"); + final PathImpl p = PathImpl.copy(path); + p.addNode(node); + return new GraphContext(validatorContext, p, value, this); + } + + public GraphContext child(Path p, Object value) { + Validate.notNull(p, "Path"); + final PathImpl impl = PathImpl.of(p); + Validate.isTrue(impl.isSubPathOf(path), "%s is not a subpath of %s", p, path); + return new GraphContext(validatorContext, impl == p ? PathImpl.copy(impl) : impl, value, this); + } + + public boolean isRoot() { + return parent == null; + } + + public boolean isRecursive() { + GraphContext c = parent; + while (c != null) { + if (c.value == value) { + return true; + } + c = c.parent; + } + return false; + } + + public GraphContext getParent() { + return parent; + } + + @Override + public String toString() { + return String.format("%s: %s at '%s'", getClass().getSimpleName(), value, path); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || !obj.getClass().equals(getClass())) { + return false; + } + final GraphContext other = (GraphContext) obj; + return other.validatorContext == validatorContext && other.value == value && other.getPath().equals(path); + } + + @Override + public int hashCode() { + return Objects.hash(validatorContext, value, path); + } + + public ContainerElementKey runtimeKey(ContainerElementKey key) { + Validate.notNull(key); + if (value != null) { + final Class<?> containerClass = key.getContainerClass(); + final Class<? extends Object> runtimeType = value.getClass(); + if (!runtimeType.equals(containerClass)) { + Exceptions.raiseUnless(containerClass.isAssignableFrom(runtimeType), ValidationException::new, + "Value %s is not assignment-compatible with %s", value, containerClass); + + if (key.getTypeArgumentIndex() == null) { + return new ContainerElementKey(runtimeType, null); + } + final Map<TypeVariable<?>, Type> typeArguments = + TypeUtils.getTypeArguments(runtimeType, containerClass); + + Type type = + typeArguments.get(containerClass.getTypeParameters()[key.getTypeArgumentIndex().intValue()]); + + while (type instanceof TypeVariable<?>) { + final TypeVariable<?> var = (TypeVariable<?>) type; + final Type nextType = typeArguments.get(var); + if (nextType instanceof TypeVariable<?>) { + type = nextType; + } else { + return ContainerElementKey.forTypeVariable(var); + } + } + } + } + return key; + } +} Added: tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ParticipantFactory.java URL: http://svn.apache.org/viewvc/tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ParticipantFactory.java?rev=1843674&view=auto ============================================================================== --- tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ParticipantFactory.java (added) +++ tomee/deps/branches/bval-2/bval-jsr/src/main/java/org/apache/bval/jsr/ParticipantFactory.java Fri Oct 12 15:00:48 2018 @@ -0,0 +1,125 @@ +/* + * 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.BufferedReader; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +import javax.validation.ValidationException; + +import org.apache.bval.cdi.BValExtension; +import org.apache.bval.jsr.util.ToUnmodifiable; +import org.apache.bval.util.Validate; +import org.apache.commons.weaver.privilizer.Privileged; + +/** + * Factory object for helper/participant classes. The typical pattern is that this factory loads an instance of a class + * by name, taking into account whether Apache BVal is operating in a CDI environment. + */ +class ParticipantFactory implements Closeable { + private static final Logger log = Logger.getLogger(ParticipantFactory.class.getName()); + private static final String META_INF_SERVICES = "META-INF/services/"; + + private final Collection<BValExtension.Releasable<?>> releasables = new CopyOnWriteArrayList<>(); + private final List<ClassLoader> loaders; + + ParticipantFactory(ClassLoader... loaders) { + super(); + this.loaders = Collections.unmodifiableList(Arrays.asList(Validate + .noNullElements(loaders, "null %s specified at index %d", ClassLoader.class.getSimpleName()).clone())); + } + + @Override + public void close() throws IOException { + for (final BValExtension.Releasable<?> releasable : releasables) { + releasable.release(); + } + releasables.clear(); + } + + <T> T create(String classname) { + return newInstance(loadClass(classname)); + } + + <T> Set<T> loadServices(Class<T> type) { + Validate.notNull(type); + final Set<URL> resources = new LinkedHashSet<>(); + final String resourceName = META_INF_SERVICES + type.getName(); + for (ClassLoader loader : loaders) { + try { + for (Enumeration<URL> urls = loader.getResources(resourceName); urls.hasMoreElements();) { + resources.add(urls.nextElement()); + } + } catch (IOException e) { + log.log(Level.SEVERE, "Error searching for resource(s) " + resourceName, e); + } + } + return resources.stream().map(this::read).flatMap(Collection::stream).<T> map(this::create) + .collect(ToUnmodifiable.set()); + } + + private Set<String> read(URL url) { + try (BufferedReader r = new BufferedReader(new InputStreamReader(url.openStream()))) { + return r.lines().map(String::trim).filter(line -> line.charAt(0) != '#').collect(Collectors.toSet()); + } catch (IOException e) { + log.log(Level.SEVERE, "Unable to read resource " + url, e); + return Collections.emptySet(); + } + } + + @SuppressWarnings("unchecked") + private <T> Class<T> loadClass(final String className) { + for (ClassLoader loader : loaders) { + try { + return (Class<T>) Class.forName(className, true, loader); + } catch (final ClassNotFoundException ex) { + } + } + throw new ValidationException("Unable to load class " + className); + } + + @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); + } + } +}