From 98d39bc21dd91dcf482ea213e1a06de49aa2516b Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Tue, 13 Feb 2024 21:25:32 +0100 Subject: [PATCH] Remove Context Propagation deprecated API usage Prior to this commit, gh-676 fixed the local and global context inheritance in the `ContextDataFetcherDecorator` with a workaround. This commit undoes this workaround and uses the new Context Propagation API for this, effectively raising the requirement for version 1.0.3 and removing all deprecated APIs in the meantime. Closes gh-688 --- .../method/InvocableHandlerMethodSupport.java | 8 ++- .../ContextDataFetcherDecorator.java | 15 ++--- .../DataFetcherExceptionResolverAdapter.java | 9 +-- .../execution/DefaultBatchLoaderRegistry.java | 12 ++-- .../DefaultExecutionGraphQlService.java | 8 +-- .../ExceptionResolversExceptionHandler.java | 8 ++- .../SecurityContextThreadLocalAccessor.java | 56 +++++++++++++++++-- .../SubscriptionExceptionResolverAdapter.java | 9 ++- .../DefaultWebGraphQlHandlerBuilder.java | 8 ++- .../webmvc/GraphQlWebSocketHandler.java | 8 ++- 10 files changed, 99 insertions(+), 42 deletions(-) diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/InvocableHandlerMethodSupport.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/InvocableHandlerMethodSupport.java index 2c3d4000..9f6183a2 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/InvocableHandlerMethodSupport.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/InvocableHandlerMethodSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -26,6 +26,7 @@ import java.util.concurrent.Executor; import graphql.GraphQLContext; import io.micrometer.context.ContextSnapshot; +import io.micrometer.context.ContextSnapshotFactory; import reactor.core.publisher.Mono; import org.springframework.core.CoroutinesUtils; @@ -45,6 +46,8 @@ public abstract class InvocableHandlerMethodSupport extends HandlerMethod { private static final Object NO_VALUE = new Object(); + private static final ContextSnapshotFactory SNAPSHOT_FACTORY = ContextSnapshotFactory.builder().build(); + private final boolean hasCallableReturnValue; @@ -122,13 +125,12 @@ public abstract class InvocableHandlerMethodSupport extends HandlerMethod { } @Nullable - @SuppressWarnings("deprecation") private Object handleReturnValue(GraphQLContext graphQLContext, @Nullable Object result) { if (this.hasCallableReturnValue && result != null) { return CompletableFuture.supplyAsync( () -> { try { - return ContextSnapshot.captureFrom(graphQLContext).wrap((Callable) result).call(); + return SNAPSHOT_FACTORY.captureFrom(graphQLContext).wrap((Callable) result).call(); } catch (Exception ex) { throw new IllegalStateException( diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/ContextDataFetcherDecorator.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/ContextDataFetcherDecorator.java index 562d86dc..a18ec49e 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/ContextDataFetcherDecorator.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/ContextDataFetcherDecorator.java @@ -32,6 +32,7 @@ import graphql.schema.GraphQLTypeVisitorStub; import graphql.util.TraversalControl; import graphql.util.TraverserContext; import io.micrometer.context.ContextSnapshot; +import io.micrometer.context.ContextSnapshotFactory; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -58,6 +59,8 @@ final class ContextDataFetcherDecorator implements DataFetcher { private final SubscriptionExceptionResolver subscriptionExceptionResolver; + private final ContextSnapshotFactory snapshotFactory = ContextSnapshotFactory.builder().build(); + private ContextDataFetcherDecorator( DataFetcher delegate, boolean subscription, SubscriptionExceptionResolver subscriptionExceptionResolver) { @@ -70,21 +73,15 @@ final class ContextDataFetcherDecorator implements DataFetcher { } @Override - @SuppressWarnings("deprecation") public Object get(DataFetchingEnvironment environment) throws Exception { - GraphQLContext context; - // temporarily merge global and local graphql context until https://github.com/micrometer-metrics/context-propagation/pull/98 + ContextSnapshot snapshot; if (environment.getLocalContext() instanceof GraphQLContext localContext) { - context = GraphQLContext.newContext() - .of(environment.getGraphQlContext()) - .of(localContext) - .build(); + snapshot = snapshotFactory.captureFrom(environment.getGraphQlContext(), localContext); } else { - context = environment.getGraphQlContext(); + snapshot = snapshotFactory.captureFrom(environment.getGraphQlContext()); } - ContextSnapshot snapshot = ContextSnapshot.captureFrom(context); Object value = snapshot.wrap(() -> this.delegate.get(environment)).call(); if (this.subscription) { diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/DataFetcherExceptionResolverAdapter.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/DataFetcherExceptionResolverAdapter.java index 24e5c58e..a855432e 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/DataFetcherExceptionResolverAdapter.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/DataFetcherExceptionResolverAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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,7 +21,7 @@ import java.util.function.BiFunction; import graphql.GraphQLError; import graphql.schema.DataFetchingEnvironment; -import io.micrometer.context.ContextSnapshot; +import io.micrometer.context.ContextSnapshotFactory; import io.micrometer.context.ThreadLocalAccessor; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -52,6 +52,8 @@ public abstract class DataFetcherExceptionResolverAdapter implements DataFetcher protected final Log logger = LogFactory.getLog(getClass()); + protected final ContextSnapshotFactory snapshotFactory = ContextSnapshotFactory.builder().build(); + private boolean threadLocalContextAware; @@ -93,13 +95,12 @@ public abstract class DataFetcherExceptionResolverAdapter implements DataFetcher } @Nullable - @SuppressWarnings("deprecation") private List resolveInternal(Throwable exception, DataFetchingEnvironment env) { if (!this.threadLocalContextAware) { return resolveToMultipleErrors(exception, env); } try { - return ContextSnapshot.captureFrom(env.getGraphQlContext()) + return snapshotFactory.captureFrom(env.getGraphQlContext()) .wrap(() -> resolveToMultipleErrors(exception, env)) .call(); } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultBatchLoaderRegistry.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultBatchLoaderRegistry.java index 8a52466e..9aabac37 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultBatchLoaderRegistry.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultBatchLoaderRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -27,6 +27,7 @@ import java.util.function.Supplier; import graphql.GraphQLContext; import io.micrometer.context.ContextSnapshot; +import io.micrometer.context.ContextSnapshotFactory; import org.dataloader.BatchLoaderContextProvider; import org.dataloader.BatchLoaderEnvironment; import org.dataloader.BatchLoaderWithContext; @@ -52,6 +53,8 @@ import org.springframework.util.StringUtils; */ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry { + private static final ContextSnapshotFactory SNAPSHOT_FACTORY = ContextSnapshotFactory.builder().build(); + private final List> loaders = new ArrayList<>(); private final List> mappedLoaders = new ArrayList<>(); @@ -59,6 +62,7 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry { private final Supplier defaultOptionsSupplier; + /** * Default constructor. */ @@ -223,10 +227,9 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry { } @Override - @SuppressWarnings("deprecation") public CompletionStage> load(List keys, BatchLoaderEnvironment environment) { GraphQLContext graphQLContext = environment.getContext(); - ContextSnapshot snapshot = ContextSnapshot.captureFrom(graphQLContext); + ContextSnapshot snapshot = SNAPSHOT_FACTORY.captureFrom(graphQLContext); try { return snapshot.wrap(() -> this.loader.apply(keys, environment) @@ -272,10 +275,9 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry { } @Override - @SuppressWarnings("deprecation") public CompletionStage> load(Set keys, BatchLoaderEnvironment environment) { GraphQLContext graphQLContext = environment.getContext(); - ContextSnapshot snapshot = ContextSnapshot.captureFrom(graphQLContext); + ContextSnapshot snapshot = SNAPSHOT_FACTORY.captureFrom(graphQLContext); try { return snapshot.wrap(() -> this.loader.apply(keys, environment) diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultExecutionGraphQlService.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultExecutionGraphQlService.java index 267e3db8..7d77a8bf 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultExecutionGraphQlService.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultExecutionGraphQlService.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -25,7 +25,7 @@ import graphql.GraphQL; import graphql.GraphQLContext; import graphql.execution.ExecutionIdProvider; import graphql.execution.instrumentation.dataloader.DataLoaderDispatcherInstrumentationState; -import io.micrometer.context.ContextSnapshot; +import io.micrometer.context.ContextSnapshotFactory; import org.dataloader.DataLoaderRegistry; import reactor.core.publisher.Mono; @@ -46,6 +46,7 @@ public class DefaultExecutionGraphQlService implements ExecutionGraphQlService { private static final BiFunction RESET_EXECUTION_ID_CONFIGURER = (executionInput, builder) -> builder.executionId(null).build(); + private final ContextSnapshotFactory snapshotFactory = ContextSnapshotFactory.builder().build(); private final GraphQlSource graphQlSource; @@ -72,14 +73,13 @@ public class DefaultExecutionGraphQlService implements ExecutionGraphQlService { @Override - @SuppressWarnings("deprecation") public final Mono execute(ExecutionGraphQlRequest request) { return Mono.deferContextual((contextView) -> { if (!this.isDefaultExecutionIdProvider && request.getExecutionId() == null) { request.configureExecutionInput(RESET_EXECUTION_ID_CONFIGURER); } ExecutionInput executionInput = request.toExecutionInput(); - ContextSnapshot.captureFrom(contextView).updateContext(executionInput.getGraphQLContext()); + snapshotFactory.captureFrom(contextView).updateContext(executionInput.getGraphQLContext()); ExecutionInput updatedExecutionInput = registerDataLoaders(executionInput); return Mono.fromFuture(this.graphQlSource.graphQl().executeAsync(updatedExecutionInput)) .map(result -> new DefaultExecutionGraphQlResponse(updatedExecutionInput, result)); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/ExceptionResolversExceptionHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/ExceptionResolversExceptionHandler.java index 8cbc88c9..b0af67fc 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/ExceptionResolversExceptionHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/ExceptionResolversExceptionHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -29,6 +29,7 @@ import graphql.execution.DataFetcherExceptionHandlerResult; import graphql.execution.ExecutionId; import graphql.schema.DataFetchingEnvironment; import io.micrometer.context.ContextSnapshot; +import io.micrometer.context.ContextSnapshotFactory; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import reactor.core.publisher.Flux; @@ -47,6 +48,8 @@ class ExceptionResolversExceptionHandler implements DataFetcherExceptionHandler private static final Log logger = LogFactory.getLog(ExceptionResolversExceptionHandler.class); + private final ContextSnapshotFactory snapshotFactory = ContextSnapshotFactory.builder().build(); + private final List resolvers; /** @@ -60,11 +63,10 @@ class ExceptionResolversExceptionHandler implements DataFetcherExceptionHandler @Override - @SuppressWarnings("deprecation") public CompletableFuture handleException(DataFetcherExceptionHandlerParameters params) { Throwable exception = unwrapException(params); DataFetchingEnvironment env = params.getDataFetchingEnvironment(); - ContextSnapshot snapshot = ContextSnapshot.captureFrom(env.getGraphQlContext()); + ContextSnapshot snapshot = snapshotFactory.captureFrom(env.getGraphQlContext()); try { return Flux.fromIterable(this.resolvers) .flatMap(resolver -> resolver.resolveException(exception, env)) diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/SecurityContextThreadLocalAccessor.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/SecurityContextThreadLocalAccessor.java index ed2ca52f..4da0eedf 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/SecurityContextThreadLocalAccessor.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/SecurityContextThreadLocalAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -72,13 +72,31 @@ public class SecurityContextThreadLocalAccessor implements ThreadLocalAccessor void restoreInternal(Object previousValue) { + ((ThreadLocalAccessor) this.delegate).restore((V) previousValue); + } + + @Override + public void restore() { + this.delegate.restore(); + } + private static class DelegateAccessor implements ThreadLocalAccessor { @Override @@ -97,6 +115,22 @@ public class SecurityContextThreadLocalAccessor implements ThreadLocalAccessor { @Override @@ -122,6 +155,19 @@ public class SecurityContextThreadLocalAccessor implements ThreadLocalAccessor> resolveException(Throwable exception) { if (this.threadLocalContextAware) { return Mono.deferContextual(contextView -> { - ContextSnapshot snapshot = ContextSnapshot.captureFrom(contextView); + ContextSnapshot snapshot = snapshotFactory.captureFrom(contextView); try { List errors = snapshot.wrap(() -> resolveToMultipleErrors(exception)).call(); return Mono.justOrEmpty(errors); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/DefaultWebGraphQlHandlerBuilder.java b/spring-graphql/src/main/java/org/springframework/graphql/server/DefaultWebGraphQlHandlerBuilder.java index f38e6954..d5107ce6 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/DefaultWebGraphQlHandlerBuilder.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/DefaultWebGraphQlHandlerBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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,6 +21,7 @@ import java.util.Arrays; import java.util.List; import io.micrometer.context.ContextSnapshot; +import io.micrometer.context.ContextSnapshotFactory; import reactor.core.publisher.Mono; import org.springframework.graphql.ExecutionGraphQlService; @@ -70,6 +71,8 @@ class DefaultWebGraphQlHandlerBuilder implements WebGraphQlHandler.Builder { @Override public WebGraphQlHandler build() { + ContextSnapshotFactory snapshotFactory = ContextSnapshotFactory.builder().build(); + Chain endOfChain = request -> this.service.execute(request).map(WebGraphQlResponse::new); Chain executionChain = this.interceptors.stream() @@ -86,9 +89,8 @@ class DefaultWebGraphQlHandlerBuilder implements WebGraphQlHandler.Builder { } @Override - @SuppressWarnings("deprecation") public Mono handleRequest(WebGraphQlRequest request) { - ContextSnapshot snapshot = ContextSnapshot.captureAll(); + ContextSnapshot snapshot = snapshotFactory.captureAll(); return executionChain.next(request).contextWrite(snapshot::updateContext); } }; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlWebSocketHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlWebSocketHandler.java index 65e3988a..88cc3e40 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlWebSocketHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlWebSocketHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -36,6 +36,7 @@ import graphql.ExecutionResult; import graphql.GraphQLError; import graphql.GraphqlErrorBuilder; import io.micrometer.context.ContextSnapshot; +import io.micrometer.context.ContextSnapshotFactory; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.reactivestreams.Publisher; @@ -357,13 +358,14 @@ public class GraphQlWebSocketHandler extends TextWebSocketHandler implements Sub private static final String KEY = ContextSnapshot.class.getName(); + private static final ContextSnapshotFactory SNAPSHOT_FACTORY = ContextSnapshotFactory.builder().build(); + @Override - @SuppressWarnings("deprecation") public boolean beforeHandshake( ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map attributes) { - attributes.put(KEY, ContextSnapshot.captureAll()); + attributes.put(KEY, SNAPSHOT_FACTORY.captureAll()); return true; }