Remove built-in ThreadLocalAccessor

The ThreadLocalAccessor in 1.0 was intended as a temporary solution
that is now replaced by a similar contract in the
`io.micrometer:context-propagation` library.

Unfortunately, there is no way to adapt one to the other, but
it is trivial to implement the new contract and register it either with
`ContextRegistry#getInstance` on startup or through the
`ServiceLoader` mechanism.

See gh-459
This commit is contained in:
rstoyanchev
2022-08-30 08:51:15 +01:00
parent f680892dfc
commit f5e710372b
7 changed files with 1 additions and 249 deletions

View File

@@ -1,55 +0,0 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.execution;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Default implementation of a composite accessor that is returned from
* {@link ThreadLocalAccessor#composite(List)}.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
@SuppressWarnings("deprecation")
class CompositeThreadLocalAccessor implements ThreadLocalAccessor {
private final List<ThreadLocalAccessor> accessors;
CompositeThreadLocalAccessor(List<ThreadLocalAccessor> accessors) {
this.accessors = new ArrayList<>(accessors);
}
@Override
public void extractValues(Map<String, Object> container) {
this.accessors.forEach((accessor) -> accessor.extractValues(container));
}
@Override
public void restoreValues(Map<String, Object> values) {
this.accessors.forEach((accessor) -> accessor.restoreValues(values));
}
@Override
public void resetValues(Map<String, Object> values) {
this.accessors.forEach((accessor) -> accessor.resetValues(values));
}
}

View File

@@ -15,8 +15,6 @@
*/
package org.springframework.graphql.execution;
import java.util.Map;
import io.micrometer.context.ThreadLocalAccessor;
import org.springframework.security.core.context.SecurityContext;
@@ -33,9 +31,7 @@ import org.springframework.util.ClassUtils;
* @author Rossen Stoyanchev
* @since 1.0.0
*/
@SuppressWarnings("deprecation")
public class SecurityContextThreadLocalAccessor implements ThreadLocalAccessor<Object>,
org.springframework.graphql.execution.ThreadLocalAccessor {
public class SecurityContextThreadLocalAccessor implements ThreadLocalAccessor<Object> {
private final static boolean springSecurityPresent = ClassUtils.isPresent(
"org.springframework.security.core.context.SecurityContext",
@@ -81,27 +77,6 @@ public class SecurityContextThreadLocalAccessor implements ThreadLocalAccessor<O
}
// Temporary implementation of deprecated ThreadLocalAccessor while it is still used
// in the Boot starter. If registered as such, it is ignored.
@Override
public void extractValues(Map<String, Object> container) {
container.put((String) key(), SecurityContextHolder.getContext());
}
@Override
public void restoreValues(Map<String, Object> values) {
if (values.containsKey((String) key())) {
SecurityContextHolder.setContext((SecurityContext) values.get((String) key()));
}
}
@Override
public void resetValues(Map<String, Object> values) {
SecurityContextHolder.clearContext();
}
private static class DelegateAccessor implements ThreadLocalAccessor<Object> {
@Override

View File

@@ -1,80 +0,0 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.execution;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.ObjectProvider;
/**
* Interface to be implemented to assist with the extraction of ThreadLocal
* values at the start of GraphQL request execution, e.g. in the web layer.
* Those values are saved in {@link graphql.ExecutionInput} and restored later
* around the invocation of data fetchers and exception resolvers which may be
* in a different thread.
*
* <p>Implementations of this interface are typically declared as beans in
* Spring configuration and are invoked in order as defined in
* {@link ObjectProvider#orderedStream()}.
*
* <p>Currently supported for GraphQL requests over HTTP and WebSocket in
* Spring MVC applications.
*
* @author Rossen Stoyanchev
* @since 1.0.0
* @deprecated as of 1.1.0 in favor of using
* {@link io.micrometer.context.ThreadLocalAccessor} from the
* {@code "io.micrometer:context-propagation"} library.
* @see org.springframework.graphql.server.WebGraphQlHandler.Builder#threadLocalAccessor(ThreadLocalAccessor...)
*/
@Deprecated
public interface ThreadLocalAccessor {
/**
* Extract ThreadLocal values and add them to the given Map, so they can be
* saved and subsequently {@link #restoreValues(Map) restored} around the
* invocation of data fetchers and exception resolvers.
* @param container to add extracted ThreadLocal values to
*/
void extractValues(Map<String, Object> container);
/**
* Restore ThreadLocal context by looking up previously
* {@link #extractValues(Map) extracted} values.
* @param values previously extracted saved ThreadLocal values
*/
void restoreValues(Map<String, Object> values);
/**
* Reset ThreadLocal context for the given, previously
* {@link #extractValues(Map) extracted} and then
* {@link #restoreValues(Map) restored} values.
* @param values previously extracted saved ThreadLocal values
*/
void resetValues(Map<String, Object> values);
/**
* Create a composite accessor that applies all of the given ThreadLocal accessors.
* @param accessors the accessors to apply
* @return the composite accessor
*/
static ThreadLocalAccessor composite(List<ThreadLocalAccessor> accessors) {
return new CompositeThreadLocalAccessor(accessors);
}
}

View File

@@ -21,16 +21,12 @@ import java.util.Arrays;
import java.util.List;
import io.micrometer.context.ContextSnapshot;
import org.springframework.graphql.execution.SecurityContextThreadLocalAccessor;
import org.springframework.graphql.execution.ThreadLocalAccessor;
import reactor.core.publisher.Mono;
import org.springframework.graphql.ExecutionGraphQlService;
import org.springframework.graphql.server.WebGraphQlInterceptor.Chain;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/**
@@ -38,7 +34,6 @@ import org.springframework.util.CollectionUtils;
*
* @author Rossen Stoyanchev
*/
@SuppressWarnings("deprecation")
class DefaultWebGraphQlHandlerBuilder implements WebGraphQlHandler.Builder {
private final ExecutionGraphQlService service;
@@ -48,9 +43,6 @@ class DefaultWebGraphQlHandlerBuilder implements WebGraphQlHandler.Builder {
@Nullable
private WebSocketGraphQlInterceptor webSocketInterceptor;
@Nullable
private List<ThreadLocalAccessor> accessors;
DefaultWebGraphQlHandlerBuilder(ExecutionGraphQlService service) {
Assert.notNull(service, "GraphQlService is required");
@@ -75,30 +67,6 @@ class DefaultWebGraphQlHandlerBuilder implements WebGraphQlHandler.Builder {
return this;
}
@SuppressWarnings("deprecation")
@Override
public WebGraphQlHandler.Builder threadLocalAccessor(ThreadLocalAccessor... accessors) {
return threadLocalAccessors(Arrays.asList(accessors));
}
@SuppressWarnings("deprecation")
@Override
public WebGraphQlHandler.Builder threadLocalAccessors(List<ThreadLocalAccessor> accessors) {
// Filter out SecurityContextThreadLocalAccessor which is registered as a ThreadLocalAccessor
// from the micrometer-metrics/context-propagatation library. This code can be removed when
// SecurityContextThreadLocalAccessor no longer implements the deprecated ThreadLocalAccessor.
accessors = accessors.stream()
.filter(a -> !(a instanceof SecurityContextThreadLocalAccessor))
.toList();
if (!CollectionUtils.isEmpty(accessors)) {
this.accessors = (this.accessors != null) ? this.accessors : new ArrayList<>();
this.accessors.addAll(accessors);
}
return this;
}
@Override
public WebGraphQlHandler build() {
@@ -117,12 +85,6 @@ class DefaultWebGraphQlHandlerBuilder implements WebGraphQlHandler.Builder {
webSocketInterceptor : new WebSocketGraphQlInterceptor() {});
}
@Nullable
@Override
public ThreadLocalAccessor getThreadLocalAccessor() {
return (CollectionUtils.isEmpty(accessors) ? null : ThreadLocalAccessor.composite(accessors));
}
@Override
public Mono<WebGraphQlResponse> handleRequest(WebGraphQlRequest request) {
ContextSnapshot snapshot = ContextSnapshot.capture();

View File

@@ -21,8 +21,6 @@ import java.util.List;
import reactor.core.publisher.Mono;
import org.springframework.graphql.ExecutionGraphQlService;
import org.springframework.graphql.execution.ThreadLocalAccessor;
import org.springframework.lang.Nullable;
/**
@@ -41,15 +39,6 @@ public interface WebGraphQlHandler {
*/
WebSocketGraphQlInterceptor getWebSocketInterceptor();
/**
* Return the composite {@link ThreadLocalAccessor} that the handler is
* configured with.
* @deprecated as of 1.1.0, together with {@link ThreadLocalAccessor}.
*/
@Deprecated
@Nullable
ThreadLocalAccessor getThreadLocalAccessor();
/**
* Execute the given request and return the response.
* @param request the request to execute
@@ -97,28 +86,6 @@ public interface WebGraphQlHandler {
*/
Builder interceptors(List<WebGraphQlInterceptor> interceptors);
/**
* Configure accessors for ThreadLocal variables to use to extract
* ThreadLocal values at the start of GraphQL execution in the web layer,
* and have those saved, and restored around the invocation of data
* fetchers and exception resolvers.
* @param accessors the accessors to add
* @return this builder
* @deprecated as of 1.1.0 together with {@link ThreadLocalAccessor}.
*/
@Deprecated
Builder threadLocalAccessor(ThreadLocalAccessor... accessors);
/**
* Alternative to {@link #threadLocalAccessor(ThreadLocalAccessor...)} with a
* List.
* @param accessors the list of accessors to add
* @return this builder
* @deprecated as of 1.1.0 together with {@link ThreadLocalAccessor}.
*/
@Deprecated
Builder threadLocalAccessors(List<ThreadLocalAccessor> accessors);
/**
* Build the {@link WebGraphQlHandler} instance.
* @return the built WebGraphQlHandler

View File

@@ -35,12 +35,10 @@ import org.springframework.graphql.execution.DefaultExecutionGraphQlService;
import org.springframework.graphql.execution.GraphQlSource;
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
import org.springframework.graphql.execution.SubscriptionExceptionResolver;
import org.springframework.graphql.execution.ThreadLocalAccessor;
import org.springframework.graphql.server.WebGraphQlHandler;
import org.springframework.graphql.server.WebGraphQlInterceptor;
import org.springframework.graphql.server.WebGraphQlSetup;
import org.springframework.graphql.server.webflux.GraphQlHttpHandler;
import org.springframework.lang.Nullable;
/**
* Workflow for GraphQL tests setup that starts with {@link GraphQlSource.Builder}
@@ -58,8 +56,6 @@ public class GraphQlSetup implements GraphQlServiceSetup {
private final List<WebGraphQlInterceptor> interceptors = new ArrayList<>();
private final List<ThreadLocalAccessor> accessors = new ArrayList<>();
private GraphQlSetup(Resource... schemaResources) {
this.graphQlSourceBuilder = GraphQlSource.schemaResourceBuilder().schemaResources(schemaResources);
@@ -147,19 +143,10 @@ public class GraphQlSetup implements GraphQlServiceSetup {
return this;
}
@Override
public WebGraphQlSetup threadLocalAccessor(@Nullable ThreadLocalAccessor accessor) {
if (accessor != null) {
this.accessors.add(accessor);
}
return this;
}
public WebGraphQlHandler toWebGraphQlHandler() {
ExecutionGraphQlService service = toGraphQlService();
return WebGraphQlHandler.builder(service)
.interceptors(this.interceptors)
.threadLocalAccessors(this.accessors)
.build();
}

View File

@@ -15,9 +15,7 @@
*/
package org.springframework.graphql.server;
import org.springframework.graphql.execution.ThreadLocalAccessor;
import org.springframework.graphql.server.webflux.GraphQlHttpHandler;
import org.springframework.lang.Nullable;
/**
* Workflow that results in the creation of a {@link WebGraphQlHandler} or
@@ -29,8 +27,6 @@ public interface WebGraphQlSetup {
WebGraphQlSetup interceptor(WebGraphQlInterceptor... interceptors);
WebGraphQlSetup threadLocalAccessor(@Nullable ThreadLocalAccessor accessor);
WebGraphQlHandler toWebGraphQlHandler();
org.springframework.graphql.server.webmvc.GraphQlHttpHandler toHttpHandler();