diff --git a/core/src/main/java/com/linecorp/armeria/client/ClientFactoryBuilder.java b/core/src/main/java/com/linecorp/armeria/client/ClientFactoryBuilder.java index 8a3bff0cd93..35af93a7993 100644 --- a/core/src/main/java/com/linecorp/armeria/client/ClientFactoryBuilder.java +++ b/core/src/main/java/com/linecorp/armeria/client/ClientFactoryBuilder.java @@ -47,6 +47,7 @@ import com.linecorp.armeria.common.CommonPools; import com.linecorp.armeria.common.Flags; import com.linecorp.armeria.common.Request; +import com.linecorp.armeria.internal.common.RequestContextUtil; import io.micrometer.core.instrument.MeterRegistry; import io.netty.channel.ChannelOption; @@ -560,6 +561,8 @@ private ClientFactoryOptions buildOptions() { * Returns a newly-created {@link ClientFactory} based on the properties of this builder. */ public ClientFactory build() { + // To initialize the context storage when the factory is built not the first request is sent. + assert RequestContextUtil.storage() != null; return new DefaultClientFactory(new HttpClientFactory(buildOptions())); } diff --git a/core/src/main/java/com/linecorp/armeria/client/ClientRequestContext.java b/core/src/main/java/com/linecorp/armeria/client/ClientRequestContext.java index c34a2b35ac6..65d870e62c1 100644 --- a/core/src/main/java/com/linecorp/armeria/client/ClientRequestContext.java +++ b/core/src/main/java/com/linecorp/armeria/client/ClientRequestContext.java @@ -34,6 +34,7 @@ import com.linecorp.armeria.client.endpoint.EndpointGroup; import com.linecorp.armeria.common.ContentTooLargeException; +import com.linecorp.armeria.common.ContextStorage; import com.linecorp.armeria.common.HttpHeaders; import com.linecorp.armeria.common.HttpHeadersBuilder; import com.linecorp.armeria.common.HttpRequest; @@ -46,7 +47,7 @@ import com.linecorp.armeria.common.logging.RequestLog; import com.linecorp.armeria.common.util.SafeCloseable; import com.linecorp.armeria.common.util.TimeoutMode; -import com.linecorp.armeria.internal.common.RequestContextThreadLocal; +import com.linecorp.armeria.internal.common.RequestContextUtil; import com.linecorp.armeria.server.Service; import com.linecorp.armeria.server.ServiceRequestContext; @@ -322,24 +323,25 @@ static ClientRequestContextBuilder builder(RpcRequest request, URI uri) { */ @Override default SafeCloseable push() { - final RequestContext oldCtx = RequestContextThreadLocal.getAndSet(this); + final ContextStorage contextStorage = RequestContextUtil.storage(); + final RequestContext oldCtx = contextStorage.push(this); if (oldCtx == this) { // Reentrance return noopSafeCloseable(); } if (oldCtx == null) { - return RequestContextThreadLocal::remove; + return () -> contextStorage.pop(null); } final ServiceRequestContext root = root(); if ((oldCtx instanceof ServiceRequestContext && oldCtx == root) || oldCtx instanceof ClientRequestContext && ((ClientRequestContext) oldCtx).root() == root) { - return () -> RequestContextThreadLocal.set(oldCtx); + return () -> contextStorage.pop(oldCtx); } // Put the oldCtx back before throwing an exception. - RequestContextThreadLocal.set(oldCtx); + contextStorage.pop(oldCtx); throw newIllegalContextPushingException(this, oldCtx); } diff --git a/core/src/main/java/com/linecorp/armeria/common/ContextStorage.java b/core/src/main/java/com/linecorp/armeria/common/ContextStorage.java new file mode 100644 index 00000000000..eb01b43f351 --- /dev/null +++ b/core/src/main/java/com/linecorp/armeria/common/ContextStorage.java @@ -0,0 +1,45 @@ +/* + * Copyright 2020 LINE Corporation + * + * LINE Corporation licenses this file to you 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 com.linecorp.armeria.common; + +import javax.annotation.Nullable; + +import com.linecorp.armeria.common.util.UnstableApi; + +/** + * Storage. + */ +@UnstableApi +public interface ContextStorage { + + /** + * Push. + */ + @Nullable + T push(RequestContext toPush); + + /** + * Pop. + */ + void pop(@Nullable RequestContext toRestore); + + /** + * Current. + */ + @Nullable + T currentOrNull(); +} diff --git a/core/src/main/java/com/linecorp/armeria/common/ContextStorageProvider.java b/core/src/main/java/com/linecorp/armeria/common/ContextStorageProvider.java new file mode 100644 index 00000000000..44260fc8981 --- /dev/null +++ b/core/src/main/java/com/linecorp/armeria/common/ContextStorageProvider.java @@ -0,0 +1,32 @@ +/* + * Copyright 2020 LINE Corporation + * + * LINE Corporation licenses this file to you 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 com.linecorp.armeria.common; + +import com.linecorp.armeria.common.util.UnstableApi; + +/** + * Creates a new {@link ContextStorage} dynamically via Java SPI (Service Provider Interface). + */ +@UnstableApi +@FunctionalInterface +public interface ContextStorageProvider { + + /** + * Creates a new {@link ContextStorage}. + */ + ContextStorage newContextStorage(); +} diff --git a/core/src/main/java/com/linecorp/armeria/common/ContextStorageThreadLocal.java b/core/src/main/java/com/linecorp/armeria/common/ContextStorageThreadLocal.java new file mode 100644 index 00000000000..40a6b5d21b4 --- /dev/null +++ b/core/src/main/java/com/linecorp/armeria/common/ContextStorageThreadLocal.java @@ -0,0 +1,59 @@ +/* + * Copyright 2020 LINE Corporation + * + * LINE Corporation licenses this file to you 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 com.linecorp.armeria.common; + +import static java.util.Objects.requireNonNull; + +import javax.annotation.Nullable; + +import com.linecorp.armeria.common.util.UnstableApi; + +import io.netty.util.concurrent.FastThreadLocal; +import io.netty.util.internal.InternalThreadLocalMap; + +/** + * A {@link ContextStorage} that uses thread-local to store {@link RequestContext}. + * Override this. + */ +@UnstableApi +public class ContextStorageThreadLocal implements ContextStorage { + + private static final FastThreadLocal context = new FastThreadLocal<>(); + + @Nullable + @Override + @SuppressWarnings("unchecked") + public T push(RequestContext toPush) { + requireNonNull(toPush, "toPush"); + final InternalThreadLocalMap map = InternalThreadLocalMap.get(); + final RequestContext oldCtx = context.get(map); + context.set(map, toPush); + return (T) oldCtx; + } + + @Override + public void pop(@Nullable RequestContext toRestore) { + context.set(toRestore); + } + + @Nullable + @Override + @SuppressWarnings("unchecked") + public T currentOrNull() { + return (T) context.get(); + } +} diff --git a/core/src/main/java/com/linecorp/armeria/common/RequestContext.java b/core/src/main/java/com/linecorp/armeria/common/RequestContext.java index d89f8a4fb02..6a5773e17e3 100644 --- a/core/src/main/java/com/linecorp/armeria/common/RequestContext.java +++ b/core/src/main/java/com/linecorp/armeria/common/RequestContext.java @@ -45,7 +45,7 @@ import com.linecorp.armeria.common.logging.RequestLogBuilder; import com.linecorp.armeria.common.util.SafeCloseable; import com.linecorp.armeria.internal.common.JavaVersionSpecific; -import com.linecorp.armeria.internal.common.RequestContextThreadLocal; +import com.linecorp.armeria.internal.common.RequestContextUtil; import com.linecorp.armeria.server.ServiceRequestContext; import io.micrometer.core.instrument.MeterRegistry; @@ -80,7 +80,7 @@ static T current() { */ @Nullable static T currentOrNull() { - return RequestContextThreadLocal.get(); + return RequestContextUtil.storage().currentOrNull(); } /** @@ -328,11 +328,9 @@ default EventLoop contextAwareEventLoop() { * @see ServiceRequestContext#push() */ default SafeCloseable replace() { - final RequestContext oldCtx = RequestContextThreadLocal.getAndSet(this); - if (oldCtx == null) { - return RequestContextThreadLocal::remove; - } - return () -> RequestContextThreadLocal.set(oldCtx); + final ContextStorage contextStorage = RequestContextUtil.storage(); + final RequestContext oldCtx = contextStorage.push(this); + return () -> contextStorage.pop(oldCtx); } /** diff --git a/core/src/main/java/com/linecorp/armeria/internal/common/RequestContextThreadLocal.java b/core/src/main/java/com/linecorp/armeria/internal/common/RequestContextThreadLocal.java deleted file mode 100644 index 1918d717e55..00000000000 --- a/core/src/main/java/com/linecorp/armeria/internal/common/RequestContextThreadLocal.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2016 LINE Corporation - * - * LINE Corporation licenses this file to you 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 com.linecorp.armeria.internal.common; - -import static java.util.Objects.requireNonNull; - -import javax.annotation.Nullable; - -import com.linecorp.armeria.common.RequestContext; - -import io.netty.util.concurrent.FastThreadLocal; -import io.netty.util.internal.InternalThreadLocalMap; - -public final class RequestContextThreadLocal { - - private static final FastThreadLocal context = new FastThreadLocal<>(); - - /** - * Returns the current {@link RequestContext} in the thread-local. - */ - @Nullable - @SuppressWarnings("unchecked") - public static T get() { - return (T) context.get(); - } - - /** - * Sets the specified {@link RequestContext} in the thread-local and returns the old {@link RequestContext}. - */ - @Nullable - @SuppressWarnings("unchecked") - public static T getAndSet(RequestContext ctx) { - requireNonNull(ctx, "ctx"); - final InternalThreadLocalMap map = InternalThreadLocalMap.get(); - final RequestContext oldCtx = context.get(map); - context.set(map, ctx); - return (T) oldCtx; - } - - /** - * Removes the {@link RequestContext} in the thread-local and returns it. - */ - @Nullable - @SuppressWarnings("unchecked") - public static T getAndRemove() { - final InternalThreadLocalMap map = InternalThreadLocalMap.get(); - final RequestContext oldCtx = context.get(map); - context.remove(); - return (T) oldCtx; - } - - /** - * Sets the specified {@link RequestContext} in the thread-local. - */ - public static void set(RequestContext ctx) { - requireNonNull(ctx, "ctx"); - context.set(ctx); - } - - /** - * Removes the current {@link RequestContext} in the thread-local. - */ - public static void remove() { - context.remove(); - } - - private RequestContextThreadLocal() {} -} diff --git a/core/src/main/java/com/linecorp/armeria/internal/common/RequestContextUtil.java b/core/src/main/java/com/linecorp/armeria/internal/common/RequestContextUtil.java index d0ee6ab7475..227055f1ae2 100644 --- a/core/src/main/java/com/linecorp/armeria/internal/common/RequestContextUtil.java +++ b/core/src/main/java/com/linecorp/armeria/internal/common/RequestContextUtil.java @@ -16,16 +16,23 @@ package com.linecorp.armeria.internal.common; +import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; import java.util.Collections; +import java.util.List; +import java.util.ServiceLoader; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.MapMaker; +import com.google.common.collect.Streams; +import com.linecorp.armeria.common.ContextStorage; +import com.linecorp.armeria.common.ContextStorageProvider; +import com.linecorp.armeria.common.ContextStorageThreadLocal; import com.linecorp.armeria.common.RequestContext; import com.linecorp.armeria.common.util.SafeCloseable; @@ -48,6 +55,31 @@ public final class RequestContextUtil { private static final Set REPORTED_THREADS = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap()); + private static final ContextStorage contextStorage; + + static { + final List providers = Streams.stream( + ServiceLoader.load(ContextStorageProvider.class)).collect(toImmutableList()); + + if (providers.isEmpty()) { + contextStorage = new ContextStorageThreadLocal(); + } else { + final ContextStorageProvider provider = providers.get(0); + if (providers.size() > 1) { + logger.warn("Found more than one {}. Only the first provider is used: {}, providers: {}", + ContextStorageProvider.class.getSimpleName(), provider, providers); + } + ContextStorage temp; + try { + temp = provider.newContextStorage(); + } catch (Throwable t) { + logger.warn("Failed to create context storage. provider: {}", provider, t); + temp = new ContextStorageThreadLocal(); + } + contextStorage = temp; + } + } + /** * Returns the {@link SafeCloseable} which doesn't do anything. */ @@ -55,6 +87,13 @@ public static SafeCloseable noopSafeCloseable() { return noopSafeCloseable; } + /** + * Returns the {@link ContextStorage}. + */ + public static ContextStorage storage() { + return contextStorage; + } + /** * Returns an {@link IllegalStateException} which is raised when pushing a context from * the unexpected thread or forgetting to close the previous context. @@ -83,12 +122,14 @@ public static IllegalStateException newIllegalContextPushingException( * eventloop might have the wrong {@link RequestContext} in the thread-local, so we should pop it. */ public static SafeCloseable pop() { - final RequestContext oldCtx = RequestContextThreadLocal.getAndRemove(); + final ContextStorage contextStorage = storage(); + final RequestContext oldCtx = contextStorage.currentOrNull(); if (oldCtx == null) { return noopSafeCloseable(); } - return () -> RequestContextThreadLocal.set(oldCtx); + contextStorage.pop(null); + return () -> contextStorage.push(oldCtx); } private RequestContextUtil() {} diff --git a/core/src/main/java/com/linecorp/armeria/server/ServerBuilder.java b/core/src/main/java/com/linecorp/armeria/server/ServerBuilder.java index 20fff98c11b..8a5e8d98828 100644 --- a/core/src/main/java/com/linecorp/armeria/server/ServerBuilder.java +++ b/core/src/main/java/com/linecorp/armeria/server/ServerBuilder.java @@ -66,6 +66,7 @@ import com.linecorp.armeria.common.RequestId; import com.linecorp.armeria.common.SessionProtocol; import com.linecorp.armeria.common.util.SystemInfo; +import com.linecorp.armeria.internal.common.RequestContextUtil; import com.linecorp.armeria.internal.server.annotation.AnnotatedServiceExtensions; import com.linecorp.armeria.server.annotation.ExceptionHandlerFunction; import com.linecorp.armeria.server.annotation.RequestConverterFunction; @@ -1476,6 +1477,8 @@ ports, setSslContextIfAbsent(defaultVirtualHost, defaultSslContext), virtualHost enableServerHeader, enableDateHeader, requestIdGenerator), sslContexts); serverListeners.forEach(server::addListener); + // To initialize the context storage at the server start time not when the first request is received. + assert RequestContextUtil.storage() != null; return server; } diff --git a/core/src/main/java/com/linecorp/armeria/server/ServiceRequestContext.java b/core/src/main/java/com/linecorp/armeria/server/ServiceRequestContext.java index 526ddea1131..396e369f640 100644 --- a/core/src/main/java/com/linecorp/armeria/server/ServiceRequestContext.java +++ b/core/src/main/java/com/linecorp/armeria/server/ServiceRequestContext.java @@ -35,6 +35,7 @@ import com.linecorp.armeria.client.ClientRequestContext; import com.linecorp.armeria.common.ContentTooLargeException; +import com.linecorp.armeria.common.ContextStorage; import com.linecorp.armeria.common.HttpHeaders; import com.linecorp.armeria.common.HttpHeadersBuilder; import com.linecorp.armeria.common.HttpRequest; @@ -49,7 +50,7 @@ import com.linecorp.armeria.common.RpcRequest; import com.linecorp.armeria.common.util.SafeCloseable; import com.linecorp.armeria.common.util.TimeoutMode; -import com.linecorp.armeria.internal.common.RequestContextThreadLocal; +import com.linecorp.armeria.internal.common.RequestContextUtil; import com.linecorp.armeria.server.logging.AccessLogWriter; /** @@ -212,22 +213,23 @@ default InetAddress clientAddress() { */ @Override default SafeCloseable push() { - final RequestContext oldCtx = RequestContextThreadLocal.getAndSet(this); + final ContextStorage contextStorage = RequestContextUtil.storage(); + final RequestContext oldCtx = contextStorage.push(this); if (oldCtx == this) { // Reentrance return noopSafeCloseable(); } - if (oldCtx instanceof ClientRequestContext && ((ClientRequestContext) oldCtx).root() == this) { - return () -> RequestContextThreadLocal.set(oldCtx); + if (oldCtx == null) { + return () -> contextStorage.pop(null); } - if (oldCtx == null) { - return RequestContextThreadLocal::remove; + if (oldCtx instanceof ClientRequestContext && ((ClientRequestContext) oldCtx).root() == this) { + return () -> contextStorage.pop(oldCtx); } // Put the oldCtx back before throwing an exception. - RequestContextThreadLocal.set(oldCtx); + contextStorage.pop(oldCtx); throw newIllegalContextPushingException(this, oldCtx); } diff --git a/it/context-storage/build.gradle b/it/context-storage/build.gradle new file mode 100644 index 00000000000..07718c06f1b --- /dev/null +++ b/it/context-storage/build.gradle @@ -0,0 +1,3 @@ +// This module is for testing ContextStorage, so we don't need other dependency except `:core`. +// To override ContextStorage, we have to use Java SPI, which affects other tests, so we need +// an isolated module. diff --git a/it/context-storage/src/test/java/com/linecorp/armeria/common/ContextStorageCustomizingTest.java b/it/context-storage/src/test/java/com/linecorp/armeria/common/ContextStorageCustomizingTest.java new file mode 100644 index 00000000000..ec2f74c54d4 --- /dev/null +++ b/it/context-storage/src/test/java/com/linecorp/armeria/common/ContextStorageCustomizingTest.java @@ -0,0 +1,81 @@ +/* + * Copyright 2020 LINE Corporation + * + * LINE Corporation licenses this file to you 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 com.linecorp.armeria.common; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.concurrent.CountDownLatch; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import com.linecorp.armeria.common.util.SafeCloseable; +import com.linecorp.armeria.server.ServiceRequestContext; +import com.linecorp.armeria.testing.junit.common.EventLoopExtension; + +import io.netty.channel.EventLoop; + +class ContextStorageCustomizingTest { + + @RegisterExtension + static final EventLoopExtension eventLoopExtension = new EventLoopExtension(); + + @Test + void contextStorageDoesNotAffectOtherThread() throws InterruptedException { + final EventLoop eventLoop = eventLoopExtension.get(); + final ServiceRequestContext ctx = newCtx(); + + final CountDownLatch latch1 = new CountDownLatch(1); + final CountDownLatch latch2 = new CountDownLatch(1); + final CountDownLatch latch3 = new CountDownLatch(1); + try (SafeCloseable ignored = ctx.push()) { + assertThat(CustomContextStorageProvider.current()).isEqualTo(ctx); + assertThat(CustomContextStorageProvider.pushCalled()).isOne(); + + eventLoop.execute(() -> { + final ServiceRequestContext ctx1 = newCtx(); + try (SafeCloseable ignored1 = ctx1.push()) { + assertThat(CustomContextStorageProvider.current()).isEqualTo(ctx1); + assertThat(CustomContextStorageProvider.pushCalled()).isEqualTo(2); + latch1.countDown(); + try { + latch2.await(); + } catch (InterruptedException e) { + // ignore + } + } + assertThat(CustomContextStorageProvider.current()).isNull(); + assertThat(CustomContextStorageProvider.popCalled()).isEqualTo(2); + latch3.countDown(); + }); + + latch1.await(); + assertThat(CustomContextStorageProvider.current()).isEqualTo(ctx); + assertThat(CustomContextStorageProvider.pushCalled()).isEqualTo(2); + + } + assertThat(CustomContextStorageProvider.current()).isNull(); + assertThat(CustomContextStorageProvider.popCalled()).isOne(); + latch2.countDown(); + latch3.await(); + } + + private static ServiceRequestContext newCtx() { + return ServiceRequestContext.builder(HttpRequest.of(HttpMethod.GET, "/")) + .build(); + } +} diff --git a/it/context-storage/src/test/java/com/linecorp/armeria/common/CustomContextStorageProvider.java b/it/context-storage/src/test/java/com/linecorp/armeria/common/CustomContextStorageProvider.java new file mode 100644 index 00000000000..cfd79579aa0 --- /dev/null +++ b/it/context-storage/src/test/java/com/linecorp/armeria/common/CustomContextStorageProvider.java @@ -0,0 +1,79 @@ +/* + * Copyright 2020 LINE Corporation + * + * LINE Corporation licenses this file to you 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 com.linecorp.armeria.common; + +import static java.util.Objects.requireNonNull; + +import java.util.concurrent.atomic.AtomicInteger; + +import javax.annotation.Nullable; + +import io.netty.util.concurrent.FastThreadLocal; +import io.netty.util.internal.InternalThreadLocalMap; + +public final class CustomContextStorageProvider implements ContextStorageProvider { + + private static final FastThreadLocal context = new FastThreadLocal<>(); + + private static final AtomicInteger pushCalled = new AtomicInteger(); + + private static final AtomicInteger popCalled = new AtomicInteger(); + + @Nullable + static RequestContext current() { + return context.get(); + } + + static int pushCalled() { + return pushCalled.get(); + } + + static int popCalled() { + return popCalled.get(); + } + + @Override + public ContextStorage newContextStorage() { + return new ContextStorage() { + + @Nullable + @Override + @SuppressWarnings("unchecked") + public T push(RequestContext toPush) { + requireNonNull(toPush, "toPush"); + pushCalled.incrementAndGet(); + final InternalThreadLocalMap map = InternalThreadLocalMap.get(); + final RequestContext oldCtx = context.get(map); + context.set(map, toPush); + return (T) oldCtx; + } + + @Override + public void pop(@Nullable RequestContext toRestore) { + popCalled.incrementAndGet(); + context.set(toRestore); + } + + @Nullable + @Override + @SuppressWarnings("unchecked") + public T currentOrNull() { + return (T) context.get(); + } + }; + } +} diff --git a/it/context-storage/src/test/resources/META-INF/services/com.linecorp.armeria.common.ContextStorageProvider b/it/context-storage/src/test/resources/META-INF/services/com.linecorp.armeria.common.ContextStorageProvider new file mode 100644 index 00000000000..07e234c0fed --- /dev/null +++ b/it/context-storage/src/test/resources/META-INF/services/com.linecorp.armeria.common.ContextStorageProvider @@ -0,0 +1 @@ +com.linecorp.armeria.common.CustomContextStorageProvider diff --git a/settings.gradle b/settings.gradle index f73abeb796e..c5780cc856c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -38,6 +38,7 @@ includeWithFlags ':saml', 'java', 'publish', 'relo // Unpublished Java projects includeWithFlags ':benchmarks', 'java' +includeWithFlags ':it:context-storage', 'java' includeWithFlags ':it:server', 'java', 'relocate' includeWithFlags ':it:spring:boot-tomcat', 'java', 'relocate' includeWithFlags ':it:spring:boot-tomcat8.5', 'java', 'relocate'