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; }