diff --git a/validator/src/main/java/io/avaje/validation/Validator.java b/validator/src/main/java/io/avaje/validation/Validator.java index f551417b..523ab515 100644 --- a/validator/src/main/java/io/avaje/validation/Validator.java +++ b/validator/src/main/java/io/avaje/validation/Validator.java @@ -131,6 +131,9 @@ interface Builder { /** Adds additional Locales for this validator */ Builder addLocales(Locale... locales); + /** Set the ClassLoader to use when loading adapters. */ + Builder classLoader(ClassLoader classLoader); + /** * Contract for obtaining the Clock used as the reference for now when validating the * {@code @Future} and {@code @Past} constraints. @@ -172,7 +175,6 @@ interface Builder { * Build and return the Validator instance with all the given adapters and factories registered. */ Validator build(); - } /** Function to build a ValidationAdapter from a Validation Context */ diff --git a/validator/src/main/java/io/avaje/validation/core/DValidator.java b/validator/src/main/java/io/avaje/validation/core/DValidator.java index cb4bec70..5ad7b80e 100644 --- a/validator/src/main/java/io/avaje/validation/core/DValidator.java +++ b/validator/src/main/java/io/avaje/validation/core/DValidator.java @@ -38,7 +38,6 @@ /** Default implementation of Validator. */ final class DValidator implements Validator, ValidationContext { - private static final ExtensionLoader SPI_LOADER = new ExtensionLoader(); private final CoreAdapterBuilder builder; private final Map> typeCache = new ConcurrentHashMap<>(); private final MessageInterpolator interpolator; @@ -188,6 +187,7 @@ static final class DBuilder implements Validator.Builder { private final List bundleNames = new ArrayList<>(); private final List bundles = new ArrayList<>(); private final List otherLocales = new ArrayList<>(); + private ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); private Locale defaultLocale = Locale.getDefault(); private Supplier clockSupplier = DEFAULT_CLOCK; private Duration temporalTolerance = ZERO; @@ -256,6 +256,12 @@ public Builder addLocales(Locale... locals) { return this; } + @Override + public Builder classLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + return this; + } + @Override public Builder clockProvider(Supplier clockSupplier) { this.clockSupplier = clockSupplier; @@ -281,11 +287,12 @@ public Builder messageInterpolator(MessageInterpolator interpolator) { } private void registerComponents() { + ExtensionLoader.init(classLoader); // first register all user defined ValidatorCustomizer - for (final ValidatorCustomizer next : SPI_LOADER.customizers()) { + for (final ValidatorCustomizer next : ExtensionLoader.customizers()) { next.customize(this); } - for (final GeneratedComponent next : SPI_LOADER.generatedComponents()) { + for (final GeneratedComponent next : ExtensionLoader.generatedComponents()) { next.customize(this); } } @@ -302,7 +309,7 @@ public Validator build() { final var localeResolver = new LocaleResolver(defaultLocale, otherLocales); final var interpolator = Optional.ofNullable(this.userInterpolator) - .or(SPI_LOADER::interpolator) + .or(ExtensionLoader::interpolator) .orElseGet(BasicMessageInterpolator::new); return new DValidator( diff --git a/validator/src/main/java/io/avaje/validation/core/ExtensionLoader.java b/validator/src/main/java/io/avaje/validation/core/ExtensionLoader.java index 2ddacad9..4cac220d 100644 --- a/validator/src/main/java/io/avaje/validation/core/ExtensionLoader.java +++ b/validator/src/main/java/io/avaje/validation/core/ExtensionLoader.java @@ -9,20 +9,20 @@ import io.avaje.validation.spi.AnnotationFactory; import io.avaje.validation.spi.GeneratedComponent; import io.avaje.validation.spi.MessageInterpolator; -import io.avaje.validation.spi.ValidatorCustomizer; import io.avaje.validation.spi.ValidationExtension; +import io.avaje.validation.spi.ValidatorCustomizer; /** Load all the services using the common service interface. */ final class ExtensionLoader { - private final List generatedComponents = new ArrayList<>(); - private final List customizers = new ArrayList<>(); - private final List adapterFactories = new ArrayList<>(); - private final List annotationFactories = new ArrayList<>(); - private Optional interpolator = Optional.empty(); + private static final List generatedComponents = new ArrayList<>(); + private static final List customizers = new ArrayList<>(); + private static final List adapterFactories = new ArrayList<>(); + private static final List annotationFactories = new ArrayList<>(); + private static Optional interpolator = Optional.empty(); - ExtensionLoader() { - for (var spi : ServiceLoader.load(ValidationExtension.class)) { + static void init(ClassLoader classLoader) { + for (var spi : ServiceLoader.load(ValidationExtension.class, classLoader)) { if (spi instanceof GeneratedComponent gc) { generatedComponents.add(gc); } else if (spi instanceof ValidatorCustomizer c) { @@ -37,23 +37,23 @@ final class ExtensionLoader { } } - Optional interpolator() { + static Optional interpolator() { return interpolator; } - List generatedComponents() { + static List generatedComponents() { return generatedComponents; } - List customizers() { + static List customizers() { return customizers; } - List adapterFactories() { + static List adapterFactories() { return adapterFactories; } - List annotationFactories() { + static List annotationFactories() { return annotationFactories; } } diff --git a/validator/src/main/java/io/avaje/validation/spi/Generated.java b/validator/src/main/java/io/avaje/validation/spi/Generated.java index 2f36cd24..baa47750 100644 --- a/validator/src/main/java/io/avaje/validation/spi/Generated.java +++ b/validator/src/main/java/io/avaje/validation/spi/Generated.java @@ -1,13 +1,13 @@ package io.avaje.validation.spi; +import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -/** - * Marks source code that has been generated. - */ +/** Marks source code that has been generated. */ +@Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.CLASS) public @interface Generated {