refactor 'participant' object creation logic to support CDI-injectable service loader based ValueExtractors (abstracted for future implementation-specific use)
Project: http://git-wip-us.apache.org/repos/asf/bval/repo Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/4fd0140f Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/4fd0140f Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/4fd0140f Branch: refs/heads/bv2 Commit: 4fd0140fd68856bc0dcdc7f22e9c60b37fce2a87 Parents: d781216 Author: Matt Benson <[email protected]> Authored: Fri Mar 23 09:57:29 2018 -0500 Committer: Matt Benson <[email protected]> Committed: Fri Mar 23 09:57:29 2018 -0500 ---------------------------------------------------------------------- .../apache/bval/jsr/ApacheValidatorFactory.java | 43 +++++--- .../org/apache/bval/jsr/ConfigurationImpl.java | 81 ++++++-------- .../org/apache/bval/jsr/ParticipantFactory.java | 107 +++++++++++++++++++ .../org/apache/bval/util/CloseableAble.java | 27 +++++ 4 files changed, 197 insertions(+), 61 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/bval/blob/4fd0140f/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorFactory.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorFactory.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorFactory.java index 528e543..35b2919 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorFactory.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ApacheValidatorFactory.java @@ -35,12 +35,14 @@ 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.metadata.MetadataBuilders; import org.apache.bval.jsr.util.AnnotationsManager; import org.apache.bval.jsr.valueextraction.ValueExtractors; import org.apache.bval.jsr.xml.ValidationMappingParser; +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; @@ -55,19 +57,6 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable { private static volatile ApacheValidatorFactory DEFAULT_FACTORY; - private MessageInterpolator messageResolver; - private TraversableResolver traversableResolver; - private ConstraintValidatorFactory constraintValidatorFactory; - private ParameterNameProvider parameterNameProvider; - private ClockProvider clockProvider; - 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 ValueExtractors valueExtractors = new ValueExtractors(); - private final ConstraintCached constraintsCache = new ConstraintCached(); - private final Collection<Closeable> toClose = new ArrayList<>(); - /** * Convenience method to retrieve a default global ApacheValidatorFactory * @@ -94,6 +83,26 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable { DEFAULT_FACTORY = aDefaultFactory; } + private static ValueExtractors createBaseValueExtractors(ParticipantFactory participantFactory) { + final ValueExtractors result = new ValueExtractors(); + participantFactory.loadServices(ValueExtractor.class).forEach(result::add); + return result; + } + + private MessageInterpolator messageResolver; + private TraversableResolver traversableResolver; + private ConstraintValidatorFactory constraintValidatorFactory; + private ParameterNameProvider parameterNameProvider; + private ClockProvider clockProvider; + 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 Collection<Closeable> toClose = new ArrayList<>(); + private final ParticipantFactory participantFactory; + private final ValueExtractors valueExtractors; + /** * Create a new ApacheValidatorFactory instance. */ @@ -105,9 +114,13 @@ public class ApacheValidatorFactory implements ValidatorFactory, Cloneable { constraintValidatorFactory = configuration.getConstraintValidatorFactory(); clockProvider = configuration.getClockProvider(); - if (ConfigurationImpl.class.isInstance(configuration)) { - toClose.add(ConfigurationImpl.class.cast(configuration).getClosable()); + if (configuration instanceof CloseableAble) { + toClose.add(((CloseableAble) configuration).getCloseable()); } + participantFactory = new ParticipantFactory(ApacheValidatorFactory.class.getClassLoader()); + toClose.add(participantFactory); + + valueExtractors = createBaseValueExtractors(participantFactory).createChild(); configuration.getValueExtractors().forEach(valueExtractors::add); annotationsManager = new AnnotationsManager(this); http://git-wip-us.apache.org/repos/asf/bval/blob/4fd0140f/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java index b4d19f6..84b4421 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ConfigurationImpl.java @@ -21,14 +21,13 @@ 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.LinkedHashSet; import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Supplier; import javax.validation.BootstrapConfiguration; @@ -45,11 +44,12 @@ 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.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; @@ -60,7 +60,7 @@ import org.apache.commons.weaver.privilizer.Privileged; * hence this can be passed to buildValidatorFactory(ConfigurationState). * <br/> */ -public class ConfigurationImpl implements ApacheValidatorConfiguration, ConfigurationState { +public class ConfigurationImpl implements ApacheValidatorConfiguration, ConfigurationState, CloseableAble { private class LazyParticipant<T> extends Lazy<T> { private boolean locked; @@ -135,11 +135,13 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur private final LazyParticipant<ClockProvider> clockProvider = new LazyParticipant<>(this::getDefaultClockProvider); - private Lazy<BootstrapConfiguration> bootstrapConfiguration = new Lazy<>(this::createBootstrapConfiguration); + private final ValueExtractors bootstrapValueExtractors = new ValueExtractors(); + private final ValueExtractors valueExtractors = bootstrapValueExtractors.createChild(); - private Collection<BValExtension.Releasable<?>> releasables = new CopyOnWriteArrayList<>(); + private final Lazy<BootstrapConfiguration> bootstrapConfiguration = new Lazy<>(this::createBootstrapConfiguration); - private Set<ValueExtractor<?>> valueExtractors = new HashSet<>(); + private final Set<InputStream> mappingStreams = new HashSet<>(); + private final Map<String, String> properties = new HashMap<>(); private boolean beforeCdi = false; private ClassLoader loader; @@ -151,10 +153,10 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur private boolean prepared = false; // END DEFAULTS - private final Set<InputStream> mappingStreams = new HashSet<>(); - private final Map<String, String> properties = new HashMap<>(); private boolean ignoreXmlConfiguration = false; + private ParticipantFactory participantFactory; + /** * Create a new ConfigurationImpl instance. * @param aState bootstrap state @@ -369,7 +371,7 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur @Override public Set<ValueExtractor<?>> getValueExtractors() { - return Collections.unmodifiableSet(valueExtractors); + return Collections.unmodifiableSet(new LinkedHashSet<>(valueExtractors.getValueExtractors().values())); } public void deferBootstrapOverrides() { @@ -383,13 +385,13 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur } } - public Closeable getClosable() { - return () -> { - for (final BValExtension.Releasable<?> releasable : releasables) { - releasable.release(); - } - releasables.clear(); - }; + @Override + public Closeable getCloseable() { + if (participantFactory == null) { + return () -> { + }; + } + return participantFactory; } @Privileged @@ -407,16 +409,20 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur } private BootstrapConfiguration createBootstrapConfiguration() { - if (!ignoreXmlConfiguration) { - loader = ValidationParser.class.getClassLoader(); - final BootstrapConfiguration xmlBootstrap = - ValidationParser.processValidationConfig(getProperties().get(Properties.VALIDATION_XML_PATH), this); - if (xmlBootstrap != null) { - return xmlBootstrap; + try { + if (!ignoreXmlConfiguration) { + loader = ValidationParser.class.getClassLoader(); + final BootstrapConfiguration xmlBootstrap = + ValidationParser.processValidationConfig(getProperties().get(Properties.VALIDATION_XML_PATH), this); + if (xmlBootstrap != null) { + return xmlBootstrap; + } } + loader = ApacheValidatorFactory.class.getClassLoader(); + return BootstrapConfigurationImpl.DEFAULT; + } finally { + participantFactory = new ParticipantFactory(loader); } - loader = ApacheValidatorFactory.class.getClassLoader(); - return BootstrapConfigurationImpl.DEFAULT; } private void applyBootstrapConfiguration() { @@ -425,7 +431,6 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur if (bootstrapConfig.getDefaultProviderClassName() != null) { this.providerClass = loadClass(bootstrapConfig.getDefaultProviderClassName()); } - bootstrapConfig.getProperties().forEach(this::addProperty); bootstrapConfig.getConstraintMappingResourcePaths().stream().map(ValidationParser::open) .forEach(this::addMapping); @@ -442,6 +447,9 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur override(constraintValidatorFactory, bootstrapConfig::getConstraintValidatorFactoryClassName); override(parameterNameProvider, bootstrapConfig::getParameterNameProviderClassName); override(clockProvider, bootstrapConfig::getClockProviderClassName); + + bootstrapConfig.getValueExtractorClassNames().stream().<ValueExtractor<?>> map(participantFactory::create) + .forEach(bootstrapValueExtractors::add); } @SuppressWarnings("unchecked") @@ -475,25 +483,6 @@ public class ConfigurationImpl implements ApacheValidatorConfiguration, Configur } private <T> void override(LazyParticipant<T> participant, Supplier<String> getClassName) { - final String className = getClassName.get(); - if (className != null) { - final Class<T> t = loadClass(className); - participant.override(newInstance(t)); - } - } - - @Privileged - private <T> T newInstance(final Class<T> cls) { - try { - final BValExtension.Releasable<T> releasable = BValExtension.inject(cls); - releasables.add(releasable); - return releasable.getInstance(); - } catch (Exception | NoClassDefFoundError e) { - } - try { - return cls.getConstructor().newInstance(); - } catch (final Exception e) { - throw new ValidationException(e.getMessage(), e); - } + Optional.ofNullable(getClassName.get()).<T> map(participantFactory::create).ifPresent(participant::override); } } http://git-wip-us.apache.org/repos/asf/bval/blob/4fd0140f/bval-jsr/src/main/java/org/apache/bval/jsr/ParticipantFactory.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ParticipantFactory.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ParticipantFactory.java new file mode 100644 index 0000000..bde8483 --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ParticipantFactory.java @@ -0,0 +1,107 @@ +/* + * 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.Collection; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; +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 String META_INF_SERVICES = "/META_INF/services/"; + + private final Collection<BValExtension.Releasable<?>> releasables = new CopyOnWriteArrayList<>(); + private final ClassLoader loader; + + ParticipantFactory(ClassLoader loader) { + super(); + this.loader = Validate.notNull(loader); + } + + @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); + try { + return Collections.list(loader.getResources(META_INF_SERVICES + type.getName())).stream().map(this::read) + .flatMap(Collection::stream).<T> map(this::create).collect(ToUnmodifiable.set()); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + 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) { + throw new IllegalStateException(e); + } + } + + @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); + } + } + + @Privileged + private <T> T newInstance(final Class<T> cls) { + try { + final BValExtension.Releasable<T> releasable = BValExtension.inject(cls); + releasables.add(releasable); + return releasable.getInstance(); + } catch (Exception | NoClassDefFoundError e) { + } + try { + return cls.getConstructor().newInstance(); + } catch (final Exception e) { + throw new ValidationException(e.getMessage(), e); + } + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/4fd0140f/bval-jsr/src/main/java/org/apache/bval/util/CloseableAble.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/util/CloseableAble.java b/bval-jsr/src/main/java/org/apache/bval/util/CloseableAble.java new file mode 100644 index 0000000..6fbe316 --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/util/CloseableAble.java @@ -0,0 +1,27 @@ +/* + * 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.util; + +import java.io.Closeable; + +@FunctionalInterface +public interface CloseableAble { + + Closeable getCloseable(); +}
