Skip to content

Reduce ServiceLoader Invocations #210

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion blackbox-test/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
requires io.avaje.validation.contraints;
requires jakarta.validation;
requires jakarta.inject;
provides io.avaje.validation.Validator.GeneratedComponent with example.avaje.valid.GeneratedValidatorComponent;
provides io.avaje.validation.spi.ValidationExtension with example.avaje.valid.GeneratedValidatorComponent;
provides io.avaje.inject.spi.InjectExtension with example.avaje.GeneratedModule;
}
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<maven.compiler.release>17</maven.compiler.release>
<inject.version>10.0-RC7</inject.version>
<http.version>2.0-RC2</http.version>
<spi.version>1.9</spi.version>
</properties>

<modules>
Expand Down
2 changes: 1 addition & 1 deletion validator-generator/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-spi-service</artifactId>
<version>1.9</version>
<version>${spi.version}</version>
</dependency>
<dependency>
<groupId>io.avaje</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
final class Constants {

static final String META_INF_COMPONENT =
"META-INF/services/io.avaje.validation.Validator$GeneratedComponent";
"META-INF/services/io.avaje.validation.spi.ValidationExtension";
static final String META_INF_CUSTOMIZER =
"META-INF/services/io.avaje.validation.spi.ValidatorCustomizer";
public static final String VALID_SPI = "io.avaje.validation.spi.*";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ private void writeMetaDataEntry(List<String> entries) {
private void writeImports() {
importTypes.add(Constants.VALIDATOR);
importTypes.add(Constants.VALID_SPI);
importTypes.add("io.avaje.validation.Validator.GeneratedComponent");
importTypes.add("io.avaje.validation.spi.GeneratedComponent");
importTypes.addAll(metaData.allImports());

for (final String importType : importTypes) {
Expand Down
7 changes: 7 additions & 0 deletions validator-http-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@
<optional>true</optional>
</dependency>

<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-spi-service</artifactId>
<version>${spi.version}</version>
<optional>true</optional>
</dependency>

<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-validator-inject-plugin</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.avaje.validation.http;

import io.avaje.inject.BeanScopeBuilder;
import io.avaje.spi.ServiceProvider;

import java.util.ArrayList;
import java.util.Arrays;
Expand All @@ -9,7 +10,8 @@
/**
* Plugin for avaje inject that provides a default Http Validator instance.
*/
public final class HttpValidatorProvider implements io.avaje.inject.spi.Plugin {
@ServiceProvider
public final class HttpValidatorProvider implements io.avaje.inject.spi.InjectPlugin {

private static final Class<?> VALIDATOR_HTTP_CLASS = avajeHttpOnClasspath();

Expand All @@ -33,7 +35,7 @@ public void apply(BeanScopeBuilder builder) {
}

builder.provideDefault(null, VALIDATOR_HTTP_CLASS, () -> {
final var props = builder.propertyPlugin();
final var props = builder.configPlugin();
final var locales = new ArrayList<Locale>();

props.get("validation.locale.default")
Expand Down
3 changes: 2 additions & 1 deletion validator-http-plugin/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

requires transitive io.avaje.validation.plugin;
requires transitive io.avaje.http.api;
requires static io.avaje.spi;

provides io.avaje.inject.spi.Plugin with io.avaje.validation.http.HttpValidatorProvider;
provides io.avaje.inject.spi.InjectExtension with io.avaje.validation.http.HttpValidatorProvider;
}

This file was deleted.

7 changes: 7 additions & 0 deletions validator-inject-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-spi-service</artifactId>
<version>${spi.version}</version>
<optional>true</optional>
</dependency>

<dependency>
<groupId>io.avaje</groupId>
<artifactId>junit</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@
import io.avaje.inject.BeanScopeBuilder;
import io.avaje.inject.aop.AspectProvider;
import io.avaje.inject.spi.GenericType;
import io.avaje.inject.spi.InjectPlugin;
import io.avaje.spi.ServiceProvider;
import io.avaje.validation.ValidMethod;
import io.avaje.validation.Validator;
import io.avaje.validation.adapter.MethodAdapterProvider;
import io.avaje.validation.inject.aspect.AOPMethodValidator;

/** Plugin for avaje inject that provides a default Jsonb instance. */
public final class DefaultValidatorProvider implements io.avaje.inject.spi.Plugin {
/** Plugin for avaje inject that provides a default Validator instance. */
@ServiceProvider
public final class DefaultValidatorProvider implements InjectPlugin {

@Override
public Class<?>[] provides() {
Expand All @@ -39,7 +42,7 @@ private void validator(BeanScopeBuilder builder) {
null,
Validator.class,
() -> {
final var props = builder.propertyPlugin();
final var props = builder.configPlugin();
final var validator =
Validator.builder().failFast(props.equalTo("validation.failFast", "true"));

Expand Down
3 changes: 2 additions & 1 deletion validator-inject-plugin/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
requires transitive io.avaje.validation;
requires transitive io.avaje.inject;
requires transitive io.avaje.inject.aop;
requires static io.avaje.spi;

provides io.avaje.inject.spi.Plugin with io.avaje.validation.inject.spi.DefaultValidatorProvider;
provides io.avaje.inject.spi.InjectExtension with io.avaje.validation.inject.spi.DefaultValidatorProvider;
}

This file was deleted.

7 changes: 7 additions & 0 deletions validator/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@
<optional>true</optional>
</dependency>

<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-spi-service</artifactId>
<version>${spi.version}</version>
<optional>true</optional>
</dependency>

<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
Expand Down
15 changes: 4 additions & 11 deletions validator/src/main/java/io/avaje/validation/Validator.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import io.avaje.validation.adapter.ValidationContext;
import io.avaje.validation.adapter.ValidationContext.AdapterCreateRequest;
import io.avaje.validation.core.DefaultBootstrap;
import io.avaje.validation.spi.AdapterFactory;
import io.avaje.validation.spi.AnnotationFactory;
import io.avaje.validation.spi.MessageInterpolator;
import io.avaje.validation.spi.ValidatorCustomizer;

Expand Down Expand Up @@ -160,10 +162,10 @@ interface Builder {
Builder add(ValidatorCustomizer component);

/** Add a ValidationAdapter.Factory which provides ValidationAdapters to use. */
Builder add(ValidationContext.AdapterFactory factory);
Builder add(AdapterFactory factory);

/** Add a ValidationAdapter.Factory which provides ValidationAdapters to use. */
Builder add(ValidationContext.AnnotationFactory factory);
Builder add(AnnotationFactory factory);

/**
* Build and return the Validator instance with all the given adapters and factories registered.
Expand All @@ -187,13 +189,4 @@ interface AnnotationAdapterBuilder {
/** Create a ValidationAdapter given the Validator instance. */
ValidationAdapter<?> build(AdapterCreateRequest request);
}

/** Components register ValidationAdapters with the Validator.Builder */
@FunctionalInterface
interface GeneratedComponent extends ValidatorCustomizer {

/** Customize the Builder with generated ValidationAdapters. */
@Override
void customize(Builder builder);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,36 +116,6 @@ interface Message {
*/
String lookupkey();
}

/** Factory for creating a ValidationAdapter for a given type. */
@FunctionalInterface
interface AdapterFactory {

/**
* Create and return a ValidationAdapter given the type and annotations or return null.
* Returning null means that the adapter could be created by another factory.
*
* @param type The type for which the adapter is being created
* @param ctx The validation context
* @return The created validation adapter or null if not applicable
*/
ValidationAdapter<?> create(Type type, ValidationContext ctx);
}

/** Factory for creating an Annotation Adapter for a given annotation. */
@FunctionalInterface
interface AnnotationFactory {

/**
* Create and return a ValidationAdapter given the type and annotations or return null.
* Returning null means that the adapter could be created by another factory.
*
* @param request Holds the details used to create the adapter
* @return The created validation adapter or null if not applicable
*/
ValidationAdapter<?> create(AdapterCreateRequest request);
}

/** Request to create a Validation Adapter. */
interface AdapterCreateRequest {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
import java.lang.reflect.Type;
import java.time.Clock;
import java.time.Duration;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;

Expand All @@ -15,6 +19,8 @@
import io.avaje.validation.core.adapters.FuturePastAdapterFactory;
import io.avaje.validation.core.adapters.NumberAdapters;
import io.avaje.validation.groups.Default;
import io.avaje.validation.spi.AdapterFactory;
import io.avaje.validation.spi.AnnotationFactory;

/** Builds and caches the ValidationAdapter adapters for DValidator. */
final class CoreAdapterBuilder {
Expand All @@ -23,14 +29,14 @@ final class CoreAdapterBuilder {

private static final Set<Class<?>> DEFAULT_GROUP = Set.of(Default.class);
private final DValidator context;
private final List<ValidationContext.AdapterFactory> factories = new ArrayList<>();
private final List<ValidationContext.AnnotationFactory> annotationFactories = new ArrayList<>();
private final List<AdapterFactory> factories = new ArrayList<>();
private final List<AnnotationFactory> annotationFactories = new ArrayList<>();
private final Map<Object, ValidationAdapter<?>> adapterCache = new ConcurrentHashMap<>();

CoreAdapterBuilder(
DValidator context,
List<ValidationContext.AdapterFactory> userFactories,
List<ValidationContext.AnnotationFactory> userAnnotationFactories,
List<AdapterFactory> userFactories,
List<AnnotationFactory> userAnnotationFactories,
Supplier<Clock> clockSupplier,
Duration temporalTolerance) {
this.context = context;
Expand Down Expand Up @@ -62,7 +68,7 @@ <T> ValidationAdapter<T> annotationAdapter(
@SuppressWarnings("unchecked")
<T> ValidationAdapter<T> build(Type type, Object cacheKey) {
// Ask each factory to create the validation adapter.
for (final ValidationContext.AdapterFactory factory : factories) {
for (final AdapterFactory factory : factories) {
final var result = (ValidationAdapter<T>) factory.create(type, context);
if (result != null) {
return result;
Expand Down
11 changes: 7 additions & 4 deletions validator/src/main/java/io/avaje/validation/core/DValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import java.util.Map;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
Expand All @@ -28,12 +27,16 @@
import io.avaje.validation.adapter.ValidationAdapter;
import io.avaje.validation.adapter.ValidationContext;
import io.avaje.validation.adapter.ValidationRequest;
import io.avaje.validation.spi.AdapterFactory;
import io.avaje.validation.spi.AnnotationFactory;
import io.avaje.validation.spi.GeneratedComponent;
import io.avaje.validation.spi.MessageInterpolator;
import io.avaje.validation.spi.ValidatorCustomizer;

/** 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<Type, ValidationType<?>> typeCache = new ConcurrentHashMap<>();
private final MessageInterpolator interpolator;
Expand Down Expand Up @@ -283,10 +286,10 @@ public Builder messageInterpolator(MessageInterpolator interpolator) {

private void registerComponents() {
// first register all user defined ValidatorCustomizer
for (final ValidatorCustomizer next : ServiceLoader.load(ValidatorCustomizer.class)) {
for (final ValidatorCustomizer next : SPI_LOADER.customizers()) {
next.customize(this);
}
for (final GeneratedComponent next : ServiceLoader.load(GeneratedComponent.class)) {
for (final GeneratedComponent next : SPI_LOADER.generatedComponents()) {
next.customize(this);
}
}
Expand All @@ -298,7 +301,7 @@ public DValidator build() {
final var localeResolver = new LocaleResolver(defaultLocale, otherLocales);
final var interpolator =
Optional.ofNullable(this.userInterpolator)
.or(() -> ServiceLoader.load(MessageInterpolator.class).findFirst())
.or(SPI_LOADER::interpolator)
.orElseGet(BasicMessageInterpolator::new);

return new DValidator(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package io.avaje.validation.core;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.ServiceLoader;

import io.avaje.validation.spi.AdapterFactory;
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;

/** Load all the services using the common service interface. */
final class ExtensionLoader {

private final List<GeneratedComponent> generatedComponents = new ArrayList<>();
private final List<ValidatorCustomizer> customizers = new ArrayList<>();
private final List<AdapterFactory> adapterFactories = new ArrayList<>();
private final List<AnnotationFactory> annotationFactories = new ArrayList<>();
private Optional<MessageInterpolator> interpolator = Optional.empty();

ExtensionLoader() {
for (var spi : ServiceLoader.load(ValidationExtension.class)) {
if (spi instanceof GeneratedComponent gc) {
generatedComponents.add(gc);
} else if (spi instanceof ValidatorCustomizer c) {
customizers.add(c);
} else if (spi instanceof MessageInterpolator m) {
interpolator = Optional.of(m);
} else if (spi instanceof AdapterFactory af) {
adapterFactories.add(af);
} else if (spi instanceof AnnotationFactory af) {
annotationFactories.add(af);
}
}
}

Optional<MessageInterpolator> interpolator() {
return interpolator;
}

List<GeneratedComponent> generatedComponents() {
return generatedComponents;
}

List<ValidatorCustomizer> customizers() {
return customizers;
}

List<AdapterFactory> adapterFactories() {
return adapterFactories;
}

List<AnnotationFactory> annotationFactories() {
return annotationFactories;
}
}
Loading
Loading