Revert "Configurable ContextSnapshotFactory instance"

This reverts commit dcaa1511a1.
This commit is contained in:
rstoyanchev
2024-03-15 10:02:08 +00:00
parent 267b514cf2
commit 47b873af12
23 changed files with 67 additions and 377 deletions

View File

@@ -17,12 +17,10 @@
package org.springframework.graphql.data.federation;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import graphql.schema.DataFetchingEnvironment;
import io.micrometer.context.ContextSnapshotFactory;
import reactor.core.publisher.Mono;
import org.springframework.graphql.data.method.HandlerMethod;
@@ -38,19 +36,11 @@ import org.springframework.lang.Nullable;
*/
final class EntityHandlerMethod extends DataFetcherHandlerMethodSupport {
/**
* Create an instance.
* @param handlerMethod the handler method
* @param resolvers the argument resolvers
* @param executor {@code Executor} to use for {@link Callable} methods
* @param snapshotFactory for context propagation with {@link Callable} methods
* @since 1.3
*/
public EntityHandlerMethod(
HandlerMethod handlerMethod, HandlerMethodArgumentResolverComposite resolvers,
@Nullable Executor executor, @Nullable ContextSnapshotFactory snapshotFactory) {
@Nullable Executor executor) {
super(handlerMethod, resolvers, executor, snapshotFactory);
super(handlerMethod, resolvers, executor);
}

View File

@@ -87,8 +87,8 @@ public final class FederationSchemaFactory
super.afterPropertiesSet();
detectHandlerMethods().forEach(info ->
this.handlerMethods.put(info.typeName(), new EntityHandlerMethod(
info.handlerMethod(), getArgumentResolvers(), getExecutor(), getContextSnapshotFactory())));
this.handlerMethods.put(info.typeName(),
new EntityHandlerMethod(info.handlerMethod(), getArgumentResolvers(), getExecutor())));
if (this.typeResolver == null) {
this.typeResolver = new ClassNameTypeResolver();

View File

@@ -46,7 +46,7 @@ public abstract class InvocableHandlerMethodSupport extends HandlerMethod {
private static final Object NO_VALUE = new Object();
private static final ContextSnapshotFactory DEFAULT_SNAPSHOT_FACTORY = ContextSnapshotFactory.builder().build();
private static final ContextSnapshotFactory SNAPSHOT_FACTORY = ContextSnapshotFactory.builder().build();
private final boolean hasCallableReturnValue;
@@ -54,35 +54,16 @@ public abstract class InvocableHandlerMethodSupport extends HandlerMethod {
@Nullable
private final Executor executor;
private final ContextSnapshotFactory snapshotFactory;
/**
* Create an instance for a controller method and an optional {@link Executor}
* to use for {@link Callable} return values.
* @deprecated in favor of
* {@link #InvocableHandlerMethodSupport(HandlerMethod, Executor, ContextSnapshotFactory)}.
*/
@Deprecated(since = "1.3", forRemoval = true)
protected InvocableHandlerMethodSupport(HandlerMethod handlerMethod, @Nullable Executor executor) {
this(handlerMethod, executor, null);
}
/**
* Create an instance.
* @param handlerMethod the handler method
* @param executor {@code Executor} to use for {@link Callable} methods
* @param snapshotFactory for context propagation with {@link Callable} methods
* @since 1.3
* @param handlerMethod the controller method
* @param executor an {@link Executor} to use for {@link Callable} return values
*/
protected InvocableHandlerMethodSupport(
HandlerMethod handlerMethod, @Nullable Executor executor, @Nullable ContextSnapshotFactory snapshotFactory) {
protected InvocableHandlerMethodSupport(HandlerMethod handlerMethod, @Nullable Executor executor) {
super(handlerMethod.createWithResolvedBean());
this.hasCallableReturnValue = getReturnType().getParameterType().equals(Callable.class);
this.executor = executor;
this.snapshotFactory = (snapshotFactory != null ? snapshotFactory : DEFAULT_SNAPSHOT_FACTORY);
Assert.isTrue(!this.hasCallableReturnValue || this.executor != null,
"Controller method declared with Callable return value, but no Executor configured: " +
handlerMethod.getBridgedMethod().toGenericString());
@@ -149,8 +130,7 @@ public abstract class InvocableHandlerMethodSupport extends HandlerMethod {
return CompletableFuture.supplyAsync(
() -> {
try {
ContextSnapshot snapshot = this.snapshotFactory.captureFrom(graphQLContext);
return snapshot.wrap((Callable<?>) result).call();
return SNAPSHOT_FACTORY.captureFrom(graphQLContext).wrap((Callable<?>) result).call();
}
catch (Exception ex) {
throw new IllegalStateException(

View File

@@ -38,7 +38,6 @@ import graphql.schema.DataFetchingEnvironment;
import graphql.schema.FieldCoordinates;
import graphql.schema.GraphQLCodeRegistry;
import graphql.schema.idl.RuntimeWiring;
import io.micrometer.context.ContextSnapshotFactory;
import org.dataloader.DataLoader;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
@@ -233,8 +232,7 @@ public class AnnotatedControllerConfigurer
DataFetcher<?> dataFetcher;
if (!info.isBatchMapping()) {
dataFetcher = new SchemaMappingDataFetcher(
info, getArgumentResolvers(), this.validationHelper, getExceptionResolver(),
getExecutor(), getContextSnapshotFactory());
info, getArgumentResolvers(), this.validationHelper, getExceptionResolver(), getExecutor());
}
else {
dataFetcher = registerBatchLoader(info);
@@ -328,8 +326,7 @@ public class AnnotatedControllerConfigurer
}
HandlerMethod handlerMethod = info.getHandlerMethod();
BatchLoaderHandlerMethod invocable =
new BatchLoaderHandlerMethod(handlerMethod, getExecutor(), getContextSnapshotFactory());
BatchLoaderHandlerMethod invocable = new BatchLoaderHandlerMethod(handlerMethod, getExecutor());
MethodParameter returnType = handlerMethod.getReturnType();
Class<?> clazz = returnType.getParameterType();
@@ -400,18 +397,15 @@ public class AnnotatedControllerConfigurer
@Nullable
private final Executor executor;
@Nullable final ContextSnapshotFactory snapshotFactory;
private final boolean subscription;
SchemaMappingDataFetcher(
DataFetcherMappingInfo info, HandlerMethodArgumentResolverComposite argumentResolvers,
@Nullable ValidationHelper helper, HandlerDataFetcherExceptionResolver exceptionResolver,
@Nullable Executor executor, @Nullable ContextSnapshotFactory snapshotFactory) {
@Nullable Executor executor) {
this.mappingInfo = info;
this.argumentResolvers = argumentResolvers;
this.snapshotFactory = snapshotFactory;
this.methodValidationHelper =
(helper != null ? helper.getValidationHelperFor(info.getHandlerMethod()) : null);
@@ -459,7 +453,7 @@ public class AnnotatedControllerConfigurer
DataFetcherHandlerMethod handlerMethod = new DataFetcherHandlerMethod(
getHandlerMethod(), this.argumentResolvers, this.methodValidationHelper,
this.subscription, this.executor, this.snapshotFactory);
this.executor, this.subscription);
try {
Object result = handlerMethod.invoke(environment);

View File

@@ -29,7 +29,6 @@ import java.util.concurrent.Executor;
import java.util.stream.Collectors;
import graphql.schema.DataFetcher;
import io.micrometer.context.ContextSnapshotFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -91,9 +90,6 @@ public abstract class AnnotatedControllerDetectionSupport<M> implements Applicat
@Nullable
private Executor executor;
@Nullable
private ContextSnapshotFactory snapshotFactory;
@Nullable
private HandlerMethodArgumentResolverComposite argumentResolvers;
@@ -165,21 +161,6 @@ public abstract class AnnotatedControllerDetectionSupport<M> implements Applicat
return this.executor;
}
/**
* Configure the {@link ContextSnapshotFactory} instance to use to establish
* {@code ThreadLocal} context for asynchronous handling with a {@link Callable}
* return value. If not set, then an instance with default settings is used.
* @since 1.3
*/
public void setContextSnapshotFactory(@Nullable ContextSnapshotFactory snapshotFactory) {
this.snapshotFactory = snapshotFactory;
}
@Nullable
public ContextSnapshotFactory getContextSnapshotFactory() {
return this.snapshotFactory;
}
/**
* Return the configured argument resolvers.
*/

View File

@@ -215,9 +215,9 @@ final class AnnotatedControllerExceptionResolver implements HandlerDataFetcherEx
private Mono<List<GraphQLError>> invokeExceptionHandler(
Throwable exception, DataFetchingEnvironment env, Object controllerOrAdvice, MethodHolder methodHolder) {
HandlerMethod handlerMethod = new HandlerMethod(controllerOrAdvice, methodHolder.getMethod());
DataFetcherHandlerMethod exceptionHandler =
new DataFetcherHandlerMethod(handlerMethod, this.argumentResolvers, null, false, null, null);
DataFetcherHandlerMethod exceptionHandler = new DataFetcherHandlerMethod(
new HandlerMethod(controllerOrAdvice, methodHolder.getMethod()), this.argumentResolvers,
null, null, false);
List<Throwable> exceptions = new ArrayList<>();
try {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,13 +19,11 @@ import java.security.Principal;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.Function;
import graphql.GraphQLContext;
import io.micrometer.context.ContextSnapshotFactory;
import org.dataloader.BatchLoaderEnvironment;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -60,27 +58,8 @@ public class BatchLoaderHandlerMethod extends InvocableHandlerMethodSupport {
private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
/**
* @deprecated in favor of
* {@link #BatchLoaderHandlerMethod(HandlerMethod, Executor, ContextSnapshotFactory)}.
*/
@Deprecated(since = "1.3", forRemoval = true)
public BatchLoaderHandlerMethod(HandlerMethod handlerMethod, @Nullable Executor executor) {
this(handlerMethod, executor, null);
}
/**
* Create an instance.
* @param handlerMethod the handler method
* @param executor {@code Executor} to use for {@link Callable} methods
* @param snapshotFactory for context propagation with {@link Callable} methods
* @since 1.3
*/
public BatchLoaderHandlerMethod(
HandlerMethod handlerMethod, @Nullable Executor executor,
@Nullable ContextSnapshotFactory snapshotFactory) {
super(handlerMethod, executor, snapshotFactory);
super(handlerMethod, executor);
}

View File

@@ -16,13 +16,11 @@
package org.springframework.graphql.data.method.annotation.support;
import java.util.Arrays;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.BiConsumer;
import graphql.schema.DataFetchingEnvironment;
import io.micrometer.context.ContextSnapshotFactory;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -47,34 +45,18 @@ public class DataFetcherHandlerMethod extends DataFetcherHandlerMethodSupport {
/**
* @deprecated in favor of
* {@link #DataFetcherHandlerMethod(HandlerMethod, HandlerMethodArgumentResolverComposite, BiConsumer, boolean, Executor, ContextSnapshotFactory)}
* Constructor with a parent handler method.
* @param handlerMethod the handler method
* @param resolvers the argument resolvers
* @param validationHelper to apply bean validation with
* @param subscription whether the field being fetched is of subscription type
*/
@Deprecated(since = "1.3", forRemoval = true)
public DataFetcherHandlerMethod(
HandlerMethod handlerMethod, HandlerMethodArgumentResolverComposite resolvers,
@Nullable BiConsumer<Object, Object[]> validationHelper, @Nullable Executor executor,
boolean subscription) {
this(handlerMethod, resolvers, validationHelper, subscription, executor, null);
}
/**
* Create an instance.
* @param handlerMethod the handler method
* @param resolvers the argument resolvers
* @param validationHelper to apply bean validation with
* @param subscription whether the field being fetched is of subscription type
* @param executor {@code Executor} to use for {@link Callable} methods
* @param snapshotFactory for context propagation with {@link Callable} methods
* @since 1.3
*/
public DataFetcherHandlerMethod(
HandlerMethod handlerMethod, HandlerMethodArgumentResolverComposite resolvers,
@Nullable BiConsumer<Object, Object[]> validationHelper, boolean subscription,
@Nullable Executor executor, @Nullable ContextSnapshotFactory snapshotFactory) {
super(handlerMethod, resolvers, executor, snapshotFactory);
super(handlerMethod, resolvers, executor);
Assert.isTrue(!resolvers.getResolvers().isEmpty(), "No argument resolvers");
this.validationHelper = (validationHelper != null ? validationHelper : (controller, args) -> {});
this.subscription = subscription;

View File

@@ -16,11 +16,9 @@
package org.springframework.graphql.data.method.annotation.support;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import graphql.schema.DataFetchingEnvironment;
import io.micrometer.context.ContextSnapshotFactory;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
@@ -48,31 +46,11 @@ public class DataFetcherHandlerMethodSupport extends InvocableHandlerMethodSuppo
private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
/**
* @deprecated in favor of
* {@link #DataFetcherHandlerMethodSupport(HandlerMethod, HandlerMethodArgumentResolverComposite, Executor, ContextSnapshotFactory)}
*/
@Deprecated(since = "1.3", forRemoval = true)
protected DataFetcherHandlerMethodSupport(
HandlerMethod handlerMethod, HandlerMethodArgumentResolverComposite resolvers,
@Nullable Executor executor) {
this(handlerMethod, resolvers, executor, null);
}
/**
* Create an instance.
* @param handlerMethod the handler method
* @param resolvers the argument resolvers
* @param executor {@code Executor} to use for {@link Callable} methods
* @param snapshotFactory for context propagation with {@link Callable} methods
* @since 1.3
*/
protected DataFetcherHandlerMethodSupport(
HandlerMethod handlerMethod, HandlerMethodArgumentResolverComposite resolvers,
@Nullable Executor executor, @Nullable ContextSnapshotFactory snapshotFactory) {
super(handlerMethod, executor, snapshotFactory);
super(handlerMethod, executor);
this.resolvers = resolvers;
}

View File

@@ -30,7 +30,6 @@ import graphql.schema.GraphQLSchema;
import graphql.schema.GraphQLTypeVisitor;
import graphql.schema.SchemaTransformer;
import graphql.schema.SchemaTraverser;
import io.micrometer.context.ContextSnapshotFactory;
import org.springframework.lang.Nullable;
@@ -45,9 +44,6 @@ import org.springframework.lang.Nullable;
*/
public abstract class AbstractGraphQlSourceBuilder<B extends GraphQlSource.Builder<B>> implements GraphQlSource.Builder<B> {
private static final ContextSnapshotFactory DEFAULT_SNAPSHOT_FACTORY = ContextSnapshotFactory.builder().build();
private final List<DataFetcherExceptionResolver> exceptionResolvers = new ArrayList<>();
private final List<SubscriptionExceptionResolver> subscriptionExceptionResolvers = new ArrayList<>();
@@ -58,9 +54,6 @@ public abstract class AbstractGraphQlSourceBuilder<B extends GraphQlSource.Build
private final List<Instrumentation> instrumentations = new ArrayList<>();
@Nullable
private ContextSnapshotFactory snapshotFactory;
@Nullable
private Consumer<GraphQL.Builder> graphQlConfigurer;
@@ -95,12 +88,6 @@ public abstract class AbstractGraphQlSourceBuilder<B extends GraphQlSource.Build
return self();
}
@Override
public B contextSnapshotFactory(ContextSnapshotFactory snapshotFactory) {
this.snapshotFactory = snapshotFactory;
return self();
}
@Override
public B configureGraphQl(Consumer<GraphQL.Builder> configurer) {
this.graphQlConfigurer = (this.graphQlConfigurer != null ?
@@ -117,15 +104,12 @@ public abstract class AbstractGraphQlSourceBuilder<B extends GraphQlSource.Build
public GraphQlSource build() {
GraphQLSchema schema = initGraphQlSchema();
ContextSnapshotFactory snapshotFactory =
(this.snapshotFactory != null ? this.snapshotFactory : DEFAULT_SNAPSHOT_FACTORY);
schema = applyTypeVisitorsToTransformSchema(schema);
schema = applyTypeVisitors(schema, snapshotFactory);
schema = applyTypeVisitors(schema);
GraphQL.Builder builder = GraphQL.newGraphQL(schema);
builder.defaultDataFetcherExceptionHandler(
DataFetcherExceptionResolver.createExceptionHandler(this.exceptionResolvers, snapshotFactory));
DataFetcherExceptionResolver.createExceptionHandler(this.exceptionResolvers));
if (!this.instrumentations.isEmpty()) {
builder = builder.instrumentation(new ChainedInstrumentation(this.instrumentations));
@@ -150,7 +134,7 @@ public abstract class AbstractGraphQlSourceBuilder<B extends GraphQlSource.Build
return schema;
}
private GraphQLSchema applyTypeVisitors(GraphQLSchema schema, ContextSnapshotFactory snapshotFactory) {
private GraphQLSchema applyTypeVisitors(GraphQLSchema schema) {
GraphQLCodeRegistry.Builder outputCodeRegistry =
GraphQLCodeRegistry.newCodeRegistry(schema.getCodeRegistry());
@@ -159,11 +143,8 @@ public abstract class AbstractGraphQlSourceBuilder<B extends GraphQlSource.Build
vars.put(GraphQLCodeRegistry.Builder.class, outputCodeRegistry);
vars.put(TypeVisitorHelper.class, TypeVisitorHelper.create(schema));
GraphQLTypeVisitor contextDataFetcherVisitor =
ContextDataFetcherDecorator.createVisitor(this.subscriptionExceptionResolvers, snapshotFactory);
List<GraphQLTypeVisitor> visitorsToUse = new ArrayList<>(this.typeVisitors);
visitorsToUse.add(contextDataFetcherVisitor);
visitorsToUse.add(ContextDataFetcherDecorator.createVisitor(this.subscriptionExceptionResolvers));
new SchemaTraverser().depthFirstFullSchema(visitorsToUse, schema, vars);
return schema.transformWithoutTypes(builder -> builder.codeRegistry(outputCodeRegistry));

View File

@@ -59,23 +59,19 @@ final class ContextDataFetcherDecorator implements DataFetcher<Object> {
private final SubscriptionExceptionResolver subscriptionExceptionResolver;
private final ContextSnapshotFactory snapshotFactory;
private final ContextSnapshotFactory snapshotFactory = ContextSnapshotFactory.builder().build();
private ContextDataFetcherDecorator(
DataFetcher<?> delegate, boolean subscription,
SubscriptionExceptionResolver subscriptionExceptionResolver,
ContextSnapshotFactory snapshotFactory) {
SubscriptionExceptionResolver subscriptionExceptionResolver) {
Assert.notNull(delegate, "'delegate' DataFetcher is required");
Assert.notNull(subscriptionExceptionResolver, "'subscriptionExceptionResolver' is required");
this.delegate = delegate;
this.subscription = subscription;
this.subscriptionExceptionResolver = subscriptionExceptionResolver;
this.snapshotFactory = snapshotFactory;
}
@Override
public Object get(DataFetchingEnvironment environment) throws Exception {
@@ -117,16 +113,8 @@ final class ContextDataFetcherDecorator implements DataFetcher<Object> {
* Static factory method to create {@link GraphQLTypeVisitor} that wraps
* data fetchers with the {@link ContextDataFetcherDecorator}.
*/
static GraphQLTypeVisitor createVisitor(
List<SubscriptionExceptionResolver> resolvers, ContextSnapshotFactory snapshotFactory) {
resolvers.forEach(resolver -> {
if (resolver instanceof SubscriptionExceptionResolverAdapter adapter) {
adapter.setContextSnapshotFactory(snapshotFactory);
}
});
return new ContextTypeVisitor(resolvers, snapshotFactory);
static GraphQLTypeVisitor createVisitor(List<SubscriptionExceptionResolver> resolvers) {
return new ContextTypeVisitor(resolvers);
}
@@ -137,13 +125,8 @@ final class ContextDataFetcherDecorator implements DataFetcher<Object> {
private final SubscriptionExceptionResolver exceptionResolver;
private final ContextSnapshotFactory snapshotFactory;
private ContextTypeVisitor(
List<SubscriptionExceptionResolver> resolvers, ContextSnapshotFactory snapshotFactory) {
private ContextTypeVisitor(List<SubscriptionExceptionResolver> resolvers) {
this.exceptionResolver = new CompositeSubscriptionExceptionResolver(resolvers);
this.snapshotFactory = snapshotFactory;
}
@Override
@@ -159,8 +142,7 @@ final class ContextDataFetcherDecorator implements DataFetcher<Object> {
if (applyDecorator(dataFetcher)) {
boolean handlesSubscription = visitorHelper.isSubscriptionType(parent);
dataFetcher = new ContextDataFetcherDecorator(
dataFetcher, handlesSubscription, this.exceptionResolver, this.snapshotFactory);
dataFetcher = new ContextDataFetcherDecorator(dataFetcher, handlesSubscription, exceptionResolver);
codeRegistry.dataFetcher(fieldCoordinates, dataFetcher);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,7 +22,6 @@ import java.util.function.BiFunction;
import graphql.GraphQLError;
import graphql.execution.DataFetcherExceptionHandler;
import graphql.schema.DataFetchingEnvironment;
import io.micrometer.context.ContextSnapshotFactory;
import reactor.core.publisher.Mono;
/**
@@ -102,24 +101,7 @@ public interface DataFetcherExceptionResolver {
* @since 1.1.1
*/
static DataFetcherExceptionHandler createExceptionHandler(List<DataFetcherExceptionResolver> resolvers) {
return createExceptionHandler(resolvers, ContextSnapshotFactory.builder().build());
}
/**
* Variant of {@link #createExceptionHandler(List)} with a
* {@link ContextSnapshotFactory} instance to use.
* @since 1.3
*/
static DataFetcherExceptionHandler createExceptionHandler(
List<DataFetcherExceptionResolver> resolvers, ContextSnapshotFactory snapshotFactory) {
resolvers.forEach(resolver -> {
if (resolver instanceof DataFetcherExceptionResolverAdapter adapter) {
adapter.setContextSnapshotFactory(snapshotFactory);
}
});
return new ExceptionResolversExceptionHandler(resolvers, snapshotFactory);
return new ExceptionResolversExceptionHandler(resolvers);
}
}

View File

@@ -50,12 +50,9 @@ import org.springframework.lang.Nullable;
*/
public abstract class DataFetcherExceptionResolverAdapter implements DataFetcherExceptionResolver {
private static final ContextSnapshotFactory DEFAULT_SNAPSHOT_FACTORY = ContextSnapshotFactory.builder().build();
protected final Log logger = LogFactory.getLog(getClass());
private ContextSnapshotFactory snapshotFactory = DEFAULT_SNAPSHOT_FACTORY;
protected final ContextSnapshotFactory snapshotFactory = ContextSnapshotFactory.builder().build();
private boolean threadLocalContextAware;
@@ -92,18 +89,6 @@ public abstract class DataFetcherExceptionResolverAdapter implements DataFetcher
return this.threadLocalContextAware;
}
/**
* Internal method to allow
* via {@link DataFetcherExceptionResolver#createExceptionHandler(List, ContextSnapshotFactory)}
* to set the {@link ContextSnapshotFactory} instance to use.
* @since 1.3
*/
void setContextSnapshotFactory(ContextSnapshotFactory snapshotFactory) {
this.snapshotFactory = snapshotFactory;
}
@Override
public final Mono<List<GraphQLError>> resolveException(Throwable ex, DataFetchingEnvironment env) {
return Mono.defer(() -> Mono.justOrEmpty(resolveInternal(ex, env)));
@@ -115,7 +100,7 @@ public abstract class DataFetcherExceptionResolverAdapter implements DataFetcher
return resolveToMultipleErrors(exception, env);
}
try {
return this.snapshotFactory.captureFrom(env.getGraphQlContext())
return snapshotFactory.captureFrom(env.getGraphQlContext())
.wrap(() -> resolveToMultipleErrors(exception, env))
.call();
}

View File

@@ -19,7 +19,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.function.BiFunction;
@@ -54,13 +53,14 @@ import org.springframework.util.StringUtils;
*/
public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry {
private static final ContextSnapshotFactory SNAPSHOT_FACTORY = ContextSnapshotFactory.builder().build();
private final List<ReactorBatchLoader<?,?>> loaders = new ArrayList<>();
private final List<ReactorMappedBatchLoader<?,?>> mappedLoaders = new ArrayList<>();
private final Supplier<DataLoaderOptions> defaultOptionsSupplier;
private ContextSnapshotFactory snapshotFactory = ContextSnapshotFactory.builder().build();
/**
@@ -81,26 +81,6 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry {
}
/**
* Configure the {@link ContextSnapshotFactory} instance to use to establish
* {@code ThreadLocal} context for batch loader methods that return {@link Callable}.
* If not set, then an instance with default settings is used.
* <ul>
* <li>{@link DefaultExecutionGraphQlService#setContextSnapshotFactory}
* <li>{@link GraphQlSource.Builder#contextSnapshotFactory}
* <li>{@link org.springframework.graphql.server.WebGraphQlHandler.Builder#contextSnapshotFactory}
* </ul>
* @since 1.3
*/
public void setContextSnapshotFactory(ContextSnapshotFactory snapshotFactory) {
this.snapshotFactory = snapshotFactory;
}
public ContextSnapshotFactory getContextSnapshotFactory() {
return this.snapshotFactory;
}
@Override
public <K, V> RegistrationSpec<K, V> forTypePair(Class<K> keyType, Class<V> valueType) {
return new DefaultRegistrationSpec<>(valueType);
@@ -181,13 +161,13 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry {
@Override
public void registerBatchLoader(BiFunction<List<K>, BatchLoaderEnvironment, Flux<V>> loader) {
DefaultBatchLoaderRegistry.this.loaders.add(
new ReactorBatchLoader<>(initName(), loader, initOptionsSupplier(), snapshotFactory));
new ReactorBatchLoader<>(initName(), loader, initOptionsSupplier()));
}
@Override
public void registerMappedBatchLoader(BiFunction<Set<K>, BatchLoaderEnvironment, Mono<Map<K, V>>> loader) {
DefaultBatchLoaderRegistry.this.mappedLoaders.add(
new ReactorMappedBatchLoader<>(initName(), loader, initOptionsSupplier(), snapshotFactory));
new ReactorMappedBatchLoader<>(initName(), loader, initOptionsSupplier()));
}
private String initName() {
@@ -229,16 +209,13 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry {
private final Supplier<DataLoaderOptions> optionsSupplier;
private final ContextSnapshotFactory snapshotFactory;
private ReactorBatchLoader(String name,
BiFunction<List<K>, BatchLoaderEnvironment, Flux<V>> loader,
Supplier<DataLoaderOptions> optionsSupplier, ContextSnapshotFactory snapshotFactory) {
Supplier<DataLoaderOptions> optionsSupplier) {
this.name = name;
this.loader = loader;
this.optionsSupplier = optionsSupplier;
this.snapshotFactory = snapshotFactory;
}
public String getName() {
@@ -252,7 +229,7 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry {
@Override
public CompletionStage<List<V>> load(List<K> keys, BatchLoaderEnvironment environment) {
GraphQLContext graphQLContext = environment.getContext();
ContextSnapshot snapshot = this.snapshotFactory.captureFrom(graphQLContext);
ContextSnapshot snapshot = SNAPSHOT_FACTORY.captureFrom(graphQLContext);
try {
return snapshot.wrap(() ->
this.loader.apply(keys, environment)
@@ -280,16 +257,13 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry {
private final Supplier<DataLoaderOptions> optionsSupplier;
private final ContextSnapshotFactory snapshotFactory;
private ReactorMappedBatchLoader(String name,
BiFunction<Set<K>, BatchLoaderEnvironment, Mono<Map<K, V>>> loader,
Supplier<DataLoaderOptions> optionsSupplier, ContextSnapshotFactory snapshotFactory) {
Supplier<DataLoaderOptions> optionsSupplier) {
this.name = name;
this.loader = loader;
this.optionsSupplier = optionsSupplier;
this.snapshotFactory = snapshotFactory;
}
public String getName() {
@@ -303,7 +277,7 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry {
@Override
public CompletionStage<Map<K, V>> load(Set<K> keys, BatchLoaderEnvironment environment) {
GraphQLContext graphQLContext = environment.getContext();
ContextSnapshot snapshot = this.snapshotFactory.captureFrom(graphQLContext);
ContextSnapshot snapshot = SNAPSHOT_FACTORY.captureFrom(graphQLContext);
try {
return snapshot.wrap(() ->
this.loader.apply(keys, environment)

View File

@@ -46,13 +46,12 @@ public class DefaultExecutionGraphQlService implements ExecutionGraphQlService {
private static final BiFunction<ExecutionInput, ExecutionInput.Builder, ExecutionInput> RESET_EXECUTION_ID_CONFIGURER =
(executionInput, builder) -> builder.executionId(null).build();
private final ContextSnapshotFactory snapshotFactory = ContextSnapshotFactory.builder().build();
private final GraphQlSource graphQlSource;
private final List<DataLoaderRegistrar> dataLoaderRegistrars = new ArrayList<>();
private ContextSnapshotFactory snapshotFactory = ContextSnapshotFactory.builder().build();
private boolean hasDataLoaderRegistrations;
private final boolean isDefaultExecutionIdProvider;
@@ -81,23 +80,6 @@ public class DefaultExecutionGraphQlService implements ExecutionGraphQlService {
return !registry.getDataLoaders().isEmpty();
}
/**
* Configure the {@link ContextSnapshotFactory} instance to use to propagate
* {@code TreadLocal} and Reactor context through {@link GraphQLContext}.
* If not set, then an instance with default settings is used.
* <p>Note that there are other components that would also need to be
* configured similarly to use a single instance:
* <ul>
* <li>{@link GraphQlSource.Builder#contextSnapshotFactory}
* <li>{@link DefaultBatchLoaderRegistry#setContextSnapshotFactory}
* <li>{@link org.springframework.graphql.server.WebGraphQlHandler.Builder#contextSnapshotFactory}
* </ul>
* @since 1.3
*/
public void setContextSnapshotFactory(ContextSnapshotFactory snapshotFactory) {
this.snapshotFactory = snapshotFactory;
}
@Override
public final Mono<ExecutionGraphQlResponse> execute(ExecutionGraphQlRequest request) {
@@ -109,7 +91,7 @@ public class DefaultExecutionGraphQlService implements ExecutionGraphQlService {
ExecutionInput executionInput = request.toExecutionInput();
GraphQLContext graphQLContext = executionInput.getGraphQLContext();
this.snapshotFactory.captureFrom(contextView).updateContext(graphQLContext);
snapshotFactory.captureFrom(contextView).updateContext(executionInput.getGraphQLContext());
ExecutionInput updatedExecutionInput =
(this.hasDataLoaderRegistrations ? registerDataLoaders(executionInput) : executionInput);

View File

@@ -48,23 +48,17 @@ class ExceptionResolversExceptionHandler implements DataFetcherExceptionHandler
private static final Log logger = LogFactory.getLog(ExceptionResolversExceptionHandler.class);
private final ContextSnapshotFactory snapshotFactory;
private final ContextSnapshotFactory snapshotFactory = ContextSnapshotFactory.builder().build();
private final List<DataFetcherExceptionResolver> resolvers;
/**
* Create an instance.
* @param resolvers the resolvers to use
* @param snapshotFactory the factory instance to use for context propagation
*/
ExceptionResolversExceptionHandler(
List<DataFetcherExceptionResolver> resolvers, ContextSnapshotFactory snapshotFactory) {
ExceptionResolversExceptionHandler(List<DataFetcherExceptionResolver> resolvers) {
Assert.notNull(resolvers, "'resolvers' is required");
Assert.notNull(resolvers, "ContextSnapshotFactory is required");
this.resolvers = new ArrayList<>(resolvers);
this.snapshotFactory = snapshotFactory;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,14 +21,12 @@ import java.util.function.BiFunction;
import java.util.function.Consumer;
import graphql.GraphQL;
import graphql.GraphQLContext;
import graphql.execution.instrumentation.Instrumentation;
import graphql.schema.GraphQLSchema;
import graphql.schema.GraphQLTypeVisitor;
import graphql.schema.TypeResolver;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.TypeDefinitionRegistry;
import io.micrometer.context.ContextSnapshotFactory;
import org.springframework.core.io.Resource;
@@ -141,22 +139,6 @@ public interface GraphQlSource {
*/
B instrumentation(List<Instrumentation> instrumentations);
/**
* Configure the {@link ContextSnapshotFactory} instance to use for
* context propagation of {@code ThreadLocal}, and Reactor context
* values through the {@link GraphQLContext}.
* <p>Note that there are other components that would also need to be
* configured similarly to use a single instance:
* <ul>
* <li>{@link DefaultExecutionGraphQlService#setContextSnapshotFactory}
* <li>{@link DefaultBatchLoaderRegistry#setContextSnapshotFactory}
* <li>{@link org.springframework.graphql.server.WebGraphQlHandler.Builder#contextSnapshotFactory}
* </ul>
* If not set, then an instance with default settings is used.
* @since 1.3
*/
B contextSnapshotFactory(ContextSnapshotFactory snapshotFactory);
/**
* Configure consumers to be given access to the {@link GraphQL.Builder}
* used to build {@link GraphQL}.

View File

@@ -51,7 +51,7 @@ public abstract class SubscriptionExceptionResolverAdapter implements Subscripti
protected final Log logger = LogFactory.getLog(getClass());
private ContextSnapshotFactory snapshotFactory = ContextSnapshotFactory.builder().build();
protected final ContextSnapshotFactory snapshotFactory = ContextSnapshotFactory.builder().build();
private boolean threadLocalContextAware;
@@ -80,23 +80,13 @@ public abstract class SubscriptionExceptionResolverAdapter implements Subscripti
return this.threadLocalContextAware;
}
/**
* Internal method to allow
* via {@link ContextDataFetcherDecorator#createVisitor(List, ContextSnapshotFactory)}
* to set the {@link ContextSnapshotFactory} instance to use.
* @since 1.3
*/
void setContextSnapshotFactory(ContextSnapshotFactory snapshotFactory) {
this.snapshotFactory = snapshotFactory;
}
@SuppressWarnings({"unused", "try"})
@Override
public final Mono<List<GraphQLError>> resolveException(Throwable exception) {
if (this.threadLocalContextAware) {
return Mono.deferContextual(contextView -> {
ContextSnapshot snapshot = this.snapshotFactory.captureFrom(contextView);
ContextSnapshot snapshot = snapshotFactory.captureFrom(contextView);
try {
List<GraphQLError> errors = snapshot.wrap(() -> resolveToMultipleErrors(exception)).call();
return Mono.justOrEmpty(errors);

View File

@@ -41,9 +41,6 @@ class DefaultWebGraphQlHandlerBuilder implements WebGraphQlHandler.Builder {
private final List<WebGraphQlInterceptor> interceptors = new ArrayList<>();
@Nullable
private ContextSnapshotFactory snapshotFactory;
@Nullable
private WebSocketGraphQlInterceptor webSocketInterceptor;
@@ -71,15 +68,11 @@ class DefaultWebGraphQlHandlerBuilder implements WebGraphQlHandler.Builder {
return this;
}
@Override
public WebGraphQlHandler.Builder contextSnapshotFactory(ContextSnapshotFactory snapshotFactory) {
this.snapshotFactory = snapshotFactory;
return this;
}
@Override
public WebGraphQlHandler build() {
ContextSnapshotFactory snapshotFactory = ContextSnapshotFactory.builder().build();
Chain endOfChain = request -> this.service.execute(request).map(WebGraphQlResponse::new);
Chain executionChain = this.interceptors.stream()
@@ -87,9 +80,6 @@ class DefaultWebGraphQlHandlerBuilder implements WebGraphQlHandler.Builder {
.map(interceptor -> interceptor.apply(endOfChain))
.orElse(endOfChain);
ContextSnapshotFactory snapshotFactory =
(this.snapshotFactory != null ? this.snapshotFactory : ContextSnapshotFactory.builder().build());
return new WebGraphQlHandler() {
@Override
@@ -98,11 +88,6 @@ class DefaultWebGraphQlHandlerBuilder implements WebGraphQlHandler.Builder {
webSocketInterceptor : new WebSocketGraphQlInterceptor() {});
}
@Override
public ContextSnapshotFactory contextSnapshotFactory() {
return snapshotFactory;
}
@Override
public Mono<WebGraphQlResponse> handleRequest(WebGraphQlRequest request) {
ContextSnapshot snapshot = snapshotFactory.captureAll();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,13 +18,9 @@ package org.springframework.graphql.server;
import java.util.List;
import io.micrometer.context.ContextSnapshotFactory;
import reactor.core.publisher.Mono;
import org.springframework.graphql.ExecutionGraphQlService;
import org.springframework.graphql.execution.DefaultBatchLoaderRegistry;
import org.springframework.graphql.execution.DefaultExecutionGraphQlService;
import org.springframework.graphql.execution.GraphQlSource;
/**
@@ -43,13 +39,6 @@ public interface WebGraphQlHandler {
*/
WebSocketGraphQlInterceptor getWebSocketInterceptor();
/**
* Return the {@link WebGraphQlHandler.Builder#contextSnapshotFactory configured}
* {@code ContextSnapshotFactory} instance to use.
* @since 1.3
*/
ContextSnapshotFactory contextSnapshotFactory();
/**
* Execute the given request and return the response.
* @param request the request to execute
@@ -97,22 +86,6 @@ public interface WebGraphQlHandler {
*/
Builder interceptors(List<WebGraphQlInterceptor> interceptors);
/**
* Configure the {@link ContextSnapshotFactory} instance to use for
* context propagation of {@code ThreadLocal}, and Reactor context
* values from the transport layer to {@link DefaultExecutionGraphQlService}.
* <p>Note that there are other components that would also need to be
* configured similarly to use a single instance:
* <ul>
* <li>{@link DefaultExecutionGraphQlService#setContextSnapshotFactory}
* <li>{@link DefaultBatchLoaderRegistry#setContextSnapshotFactory}
* <li>{@link GraphQlSource.Builder#contextSnapshotFactory}
* </ul>
* If not set, then an instance with default settings is used.
* @since 1.3
*/
Builder contextSnapshotFactory(ContextSnapshotFactory snapshotFactory);
/**
* Build the {@link WebGraphQlHandler} instance.
* @return the built WebGraphQlHandler

View File

@@ -120,7 +120,7 @@ public class GraphQlWebSocketHandler extends TextWebSocketHandler implements Sub
Assert.notNull(converter, "HttpMessageConverter for JSON is required");
this.graphQlHandler = graphQlHandler;
this.contextHandshakeInterceptor = new ContextHandshakeInterceptor(graphQlHandler.contextSnapshotFactory());
this.contextHandshakeInterceptor = new ContextHandshakeInterceptor();
this.webSocketGraphQlInterceptor = this.graphQlHandler.getWebSocketInterceptor();
this.initTimeoutDuration = connectionInitTimeout;
this.converter = converter;
@@ -358,18 +358,14 @@ public class GraphQlWebSocketHandler extends TextWebSocketHandler implements Sub
private static final String KEY = ContextSnapshot.class.getName();
private final ContextSnapshotFactory snapshotFactory;
public ContextHandshakeInterceptor(ContextSnapshotFactory snapshotFactory) {
this.snapshotFactory = snapshotFactory;
}
private static final ContextSnapshotFactory SNAPSHOT_FACTORY = ContextSnapshotFactory.builder().build();
@Override
public boolean beforeHandshake(
ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Map<String, Object> attributes) {
attributes.put(KEY, this.snapshotFactory.captureAll());
attributes.put(KEY, SNAPSHOT_FACTORY.captureAll());
return true;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -124,7 +124,7 @@ public class ContextValueMethodArgumentResolverTests {
DataFetcherHandlerMethod handlerMethod = new DataFetcherHandlerMethod(
new HandlerMethod(new TestController(), TestController.class.getMethod("handleMono", Mono.class)),
resolvers, null, false, null, null);
resolvers, null, null, false);
GraphQLContext graphQLContext = new GraphQLContext.Builder().build();
@@ -146,7 +146,7 @@ public class ContextValueMethodArgumentResolverTests {
BatchLoaderHandlerMethod handlerMethod = new BatchLoaderHandlerMethod(
new HandlerMethod(controller,
TestController.class.getMethod("getAuthors", List.class, Long.class)), null, null);
TestController.class.getMethod("getAuthors", List.class, Long.class)), null);
GraphQLContext context = new GraphQLContext.Builder().build();
context.put("id", 123L);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -59,7 +59,7 @@ public class DataFetcherHandlerMethodTests {
resolvers.addResolver(new ArgumentMethodArgumentResolver(new GraphQlArgumentBinder()));
DataFetcherHandlerMethod handlerMethod = new DataFetcherHandlerMethod(
handlerMethodFor(new TestController(), "hello"), resolvers, null, false, null, null);
handlerMethodFor(new TestController(), "hello"), resolvers, null, null, false);
Object result = handlerMethod.invoke(
DataFetchingEnvironmentImpl.newDataFetchingEnvironment()
@@ -77,7 +77,7 @@ public class DataFetcherHandlerMethodTests {
DataFetcherHandlerMethod handlerMethod = new DataFetcherHandlerMethod(
handlerMethodFor(new TestController(), "handleAndReturnCallable"), resolvers, null,
false, new SimpleAsyncTaskExecutor(), null);
new SimpleAsyncTaskExecutor(), false);
DataFetchingEnvironment environment = DataFetchingEnvironmentImpl
.newDataFetchingEnvironment()
@@ -99,7 +99,7 @@ public class DataFetcherHandlerMethodTests {
DataFetcherHandlerMethod handlerMethod = new DataFetcherHandlerMethod(
handlerMethodFor(new TestController(), "handleAndReturnFuture"), resolvers,
null, false, null, null);
null, null, false);
SecurityContextHolder.setContext(new SecurityContextImpl(new TestingAuthenticationToken("usr", "pwd")));
try {