| /* |
| * 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.lang.reflect.Modifier; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.Optional; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.function.BiConsumer; |
| |
| import javax.validation.ClockProvider; |
| import javax.validation.ConstraintValidatorFactory; |
| import javax.validation.MessageInterpolator; |
| import javax.validation.ParameterNameProvider; |
| import javax.validation.TraversableResolver; |
| import javax.validation.Validation; |
| import javax.validation.ValidationException; |
| import javax.validation.Validator; |
| import javax.validation.ValidatorFactory; |
| import javax.validation.spi.ConfigurationState; |
| import javax.validation.valueextraction.ValueExtractor; |
| |
| import org.apache.bval.jsr.descriptor.DescriptorManager; |
| import org.apache.bval.jsr.groups.GroupsComputer; |
| import org.apache.bval.jsr.metadata.MetadataBuilder; |
| import org.apache.bval.jsr.metadata.MetadataBuilder.ForBean; |
| import org.apache.bval.jsr.metadata.MetadataBuilders; |
| import org.apache.bval.jsr.metadata.MetadataSource; |
| import org.apache.bval.jsr.util.AnnotationsManager; |
| import org.apache.bval.jsr.valueextraction.ValueExtractors; |
| import org.apache.bval.jsr.valueextraction.ValueExtractors.OnDuplicateContainerElementKey; |
| import org.apache.bval.util.CloseableAble; |
| import org.apache.bval.util.reflection.Reflection; |
| import org.apache.commons.weaver.privilizer.Privilizing; |
| import org.apache.commons.weaver.privilizer.Privilizing.CallTo; |
| |
| /** |
| * Description: a factory is a complete configurated object that can create |
| * validators.<br/> |
| * This instance is not thread-safe.<br/> |
| */ |
| @Privilizing(@CallTo(Reflection.class)) |
| public class ApacheValidatorFactory implements ValidatorFactory, Cloneable { |
| |
| private static volatile ApacheValidatorFactory DEFAULT_FACTORY; |
| |
| /** |
| * Convenience method to retrieve a default global ApacheValidatorFactory |
| * |
| * @return {@link ApacheValidatorFactory} |
| */ |
| public static ApacheValidatorFactory getDefault() { |
| if (DEFAULT_FACTORY == null) { |
| synchronized (ApacheValidatorFactory.class) { |
| if (DEFAULT_FACTORY == null) { |
| DEFAULT_FACTORY = Validation.byProvider(ApacheValidationProvider.class).configure() |
| .buildValidatorFactory().unwrap(ApacheValidatorFactory.class); |
| } |
| } |
| } |
| return DEFAULT_FACTORY; |
| } |
| |
| /** |
| * Set a particular {@link ApacheValidatorFactory} instance as the default. |
| * |
| * @param aDefaultFactory |
| */ |
| public static void setDefault(ApacheValidatorFactory aDefaultFactory) { |
| DEFAULT_FACTORY = aDefaultFactory; |
| } |
| |
| private static ValueExtractors createBaseValueExtractors(ParticipantFactory participantFactory) { |
| final ValueExtractors result = new ValueExtractors(OnDuplicateContainerElementKey.OVERWRITE); |
| participantFactory.loadServices(ValueExtractor.class).forEach(result::add); |
| return result; |
| } |
| |
| private final Map<String, String> properties; |
| private final AnnotationsManager annotationsManager; |
| private final DescriptorManager descriptorManager = new DescriptorManager(this); |
| private final MetadataBuilders metadataBuilders = new MetadataBuilders(); |
| private final ConstraintCached constraintsCache = new ConstraintCached(); |
| private final Map<Class<?>, Class<?>> unwrappedClassCache = new ConcurrentHashMap<>(); |
| private final Collection<Closeable> toClose = new ArrayList<>(); |
| private final GroupsComputer groupsComputer = new GroupsComputer(); |
| private final ParticipantFactory participantFactory; |
| private final ValueExtractors valueExtractors; |
| |
| private MessageInterpolator messageResolver; |
| private TraversableResolver traversableResolver; |
| private ConstraintValidatorFactory constraintValidatorFactory; |
| private ParameterNameProvider parameterNameProvider; |
| private ClockProvider clockProvider; |
| |
| /** |
| * Create a new ApacheValidatorFactory instance. |
| */ |
| public ApacheValidatorFactory(ConfigurationState configuration) { |
| properties = new HashMap<>(configuration.getProperties()); |
| parameterNameProvider = configuration.getParameterNameProvider(); |
| messageResolver = configuration.getMessageInterpolator(); |
| traversableResolver = configuration.getTraversableResolver(); |
| constraintValidatorFactory = configuration.getConstraintValidatorFactory(); |
| clockProvider = configuration.getClockProvider(); |
| |
| if (configuration instanceof CloseableAble) { |
| toClose.add(((CloseableAble) configuration).getCloseable()); |
| } |
| participantFactory = new ParticipantFactory(Thread.currentThread().getContextClassLoader(), |
| ApacheValidatorFactory.class.getClassLoader()); |
| |
| toClose.add(participantFactory); |
| |
| valueExtractors = createBaseValueExtractors(participantFactory).createChild(); |
| configuration.getValueExtractors().forEach(valueExtractors::add); |
| |
| annotationsManager = new AnnotationsManager(this); |
| loadAndVerifyUserCustomizations(configuration); |
| } |
| |
| public Map<Class<?>, Class<?>> getUnwrappedClassCache() { |
| return unwrappedClassCache; |
| } |
| |
| /** |
| * Get the property map of this {@link ApacheValidatorFactory}. |
| * |
| * @return Map<String, String> |
| */ |
| public Map<String, String> getProperties() { |
| return properties; |
| } |
| |
| /** |
| * Shortcut method to create a new Validator instance with factory's settings |
| * |
| * @return the new validator instance |
| */ |
| @Override |
| public Validator getValidator() { |
| return usingContext().getValidator(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @return the validator factory's context |
| */ |
| @Override |
| public ApacheFactoryContext usingContext() { |
| return new ApacheFactoryContext(this); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public synchronized ApacheValidatorFactory clone() { |
| try { |
| return (ApacheValidatorFactory) super.clone(); |
| } catch (CloneNotSupportedException e) { |
| throw new InternalError(); // VM bug. |
| } |
| } |
| |
| /** |
| * Set the {@link MessageInterpolator} used. |
| * |
| * @param messageResolver |
| */ |
| public final void setMessageInterpolator(MessageInterpolator messageResolver) { |
| if (messageResolver != null) { |
| this.messageResolver = messageResolver; |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public MessageInterpolator getMessageInterpolator() { |
| return messageResolver; |
| } |
| |
| /** |
| * Set the {@link TraversableResolver} used. |
| * |
| * @param traversableResolver |
| */ |
| public final void setTraversableResolver(TraversableResolver traversableResolver) { |
| if (traversableResolver != null) { |
| this.traversableResolver = traversableResolver; |
| } |
| } |
| |
| public void setParameterNameProvider(final ParameterNameProvider parameterNameProvider) { |
| if (parameterNameProvider != null) { |
| this.parameterNameProvider = parameterNameProvider; |
| } |
| } |
| |
| public void setClockProvider(final ClockProvider clockProvider) { |
| if (clockProvider != null) { |
| this.clockProvider = clockProvider; |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public TraversableResolver getTraversableResolver() { |
| return traversableResolver; |
| } |
| |
| /** |
| * Set the {@link ConstraintValidatorFactory} used. |
| * |
| * @param constraintValidatorFactory |
| */ |
| public final void setConstraintValidatorFactory(ConstraintValidatorFactory constraintValidatorFactory) { |
| if (constraintValidatorFactory != null) { |
| this.constraintValidatorFactory = constraintValidatorFactory; |
| if (DefaultConstraintValidatorFactory.class.isInstance(constraintValidatorFactory)) { |
| toClose.add(Closeable.class.cast(constraintValidatorFactory)); |
| } |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public ConstraintValidatorFactory getConstraintValidatorFactory() { |
| return constraintValidatorFactory; |
| } |
| |
| @Override |
| public ParameterNameProvider getParameterNameProvider() { |
| return parameterNameProvider; |
| } |
| |
| @Override |
| public ClockProvider getClockProvider() { |
| return clockProvider; |
| } |
| |
| @Override |
| public void close() { |
| try { |
| for (final Closeable c : toClose) { |
| c.close(); |
| } |
| toClose.clear(); |
| } catch (final Exception e) { |
| // no-op |
| } |
| } |
| |
| /** |
| * Return an object of the specified type to allow access to the provider-specific API. If the Bean Validation |
| * provider implementation does not support the specified class, the ValidationException is thrown. |
| * |
| * @param type |
| * the class of the object to be returned. |
| * @return an instance of the specified class |
| * @throws ValidationException |
| * if the provider does not support the call. |
| */ |
| @Override |
| public <T> T unwrap(final Class<T> type) { |
| if (type.isInstance(this)) { |
| @SuppressWarnings("unchecked") |
| final T result = (T) this; |
| return result; |
| } |
| |
| // FIXME 2011-03-27 jw: |
| // This code is unsecure. |
| // It should allow only a fixed set of classes. |
| // Can't fix this because don't know which classes this method should support. |
| |
| if (!(type.isInterface() || Modifier.isAbstract(type.getModifiers()))) { |
| return newInstance(type); |
| } |
| try { |
| final Class<?> cls = Reflection.toClass(type.getName() + "Impl"); |
| if (type.isAssignableFrom(cls)) { |
| @SuppressWarnings("unchecked") |
| T result = (T) newInstance(cls); |
| return result; |
| } |
| } catch (ClassNotFoundException e) { |
| // do nothing |
| } |
| throw new ValidationException("Type " + type + " not supported"); |
| } |
| |
| private <T> T newInstance(final Class<T> cls) { |
| try { |
| return Reflection.newInstance(cls); |
| } catch (final RuntimeException e) { |
| throw new ValidationException(e.getCause()); |
| } |
| } |
| |
| /** |
| * Get the constraint cache used. |
| * |
| * @return {@link ConstraintCached} |
| */ |
| public ConstraintCached getConstraintsCache() { |
| return constraintsCache; |
| } |
| |
| /** |
| * Get the {@link AnnotationsManager}. |
| * |
| * @return {@link AnnotationsManager} |
| */ |
| public AnnotationsManager getAnnotationsManager() { |
| return annotationsManager; |
| } |
| |
| /** |
| * Get the {@link DescriptorManager}. |
| * |
| * @return {@link DescriptorManager} |
| */ |
| public DescriptorManager getDescriptorManager() { |
| return descriptorManager; |
| } |
| |
| /** |
| * Get the {@link ValueExtractors}. |
| * |
| * @return {@link ValueExtractors} |
| */ |
| public ValueExtractors getValueExtractors() { |
| return valueExtractors; |
| } |
| |
| public MetadataBuilders getMetadataBuilders() { |
| return metadataBuilders; |
| } |
| |
| public GroupsComputer getGroupsComputer() { |
| return groupsComputer; |
| } |
| |
| private void loadAndVerifyUserCustomizations(ConfigurationState configuration) { |
| @SuppressWarnings({ "unchecked", "rawtypes" }) |
| final BiConsumer<Class<?>, ForBean<?>> addBuilder = (t, b) -> { |
| getMetadataBuilders().registerCustomBuilder((Class) t, (MetadataBuilder.ForBean) b); |
| }; |
| participantFactory.loadServices(MetadataSource.class) |
| .forEach(ms -> { |
| ms.initialize(this); |
| ms.process(configuration, getConstraintsCache()::add, addBuilder); |
| }); |
| } |
| } |