From ccff1723f72d5490f94f5df12b7a620bd7bd7f61 Mon Sep 17 00:00:00 2001 From: Gihwan Kim Date: Thu, 24 Dec 2020 16:53:47 +0900 Subject: [PATCH] Enable existing service to be as `TransientService` (#3221) ### Motivation - Provide a way to make existing services as `TransientService` - In some cases, users want to make a service not to collect metrics like a `GrpcService` which implements [GRPC Health Checking Protocol](https://github.com/grpc/grpc/blob/master/doc/health-checking.md) ### Modifications - Add `newDecorator` to `TransientHttpService` and `TransientRpcService` - Add `WrappingTransientHttpService` and `WrappingTransientRpcService` internally ### Result - Users can make existing service to be as `TransientService` by decorating with the `TransientHttpService` or `TransientRpcService` as follows: ```java builder.service("/docs", new DocService().decorate( TransientHttpService.newDecorator(TransientServiceOption.WITH_ACCESS_LOGGING))); ``` --- .../armeria/server/TransientHttpService.java | 28 ++++++++++++ .../armeria/server/TransientRpcService.java | 28 ++++++++++++ .../server/WrappingTransientHttpService.java | 45 +++++++++++++++++++ .../server/WrappingTransientRpcService.java | 45 +++++++++++++++++++ .../WrappingTransientHttpServiceTest.java | 45 +++++++++++++++++++ .../WrappingTransientRpcServiceTest.java | 45 +++++++++++++++++++ 6 files changed, 236 insertions(+) create mode 100644 core/src/main/java/com/linecorp/armeria/server/WrappingTransientHttpService.java create mode 100644 core/src/main/java/com/linecorp/armeria/server/WrappingTransientRpcService.java create mode 100644 core/src/test/java/com/linecorp/armeria/server/WrappingTransientHttpServiceTest.java create mode 100644 core/src/test/java/com/linecorp/armeria/server/WrappingTransientRpcServiceTest.java diff --git a/core/src/main/java/com/linecorp/armeria/server/TransientHttpService.java b/core/src/main/java/com/linecorp/armeria/server/TransientHttpService.java index 140770f72f7..0baacfc5f8e 100644 --- a/core/src/main/java/com/linecorp/armeria/server/TransientHttpService.java +++ b/core/src/main/java/com/linecorp/armeria/server/TransientHttpService.java @@ -16,6 +16,13 @@ package com.linecorp.armeria.server; +import static java.util.Objects.requireNonNull; + +import java.util.function.Function; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; + import com.linecorp.armeria.common.HttpRequest; import com.linecorp.armeria.common.HttpResponse; @@ -24,4 +31,25 @@ */ @FunctionalInterface public interface TransientHttpService extends TransientService, HttpService { + + /** + * Returns a new {@link HttpService} decorator which makes the specified {@link HttpService} as + * {@link TransientService}. + */ + static Function newDecorator( + TransientServiceOption... transientServiceOptions) { + requireNonNull(transientServiceOptions, "transientServiceOptions"); + return newDecorator(ImmutableSet.copyOf(transientServiceOptions)); + } + + /** + * Returns a new {@link HttpService} decorator which makes the specified {@link HttpService} as + * {@link TransientService}. + */ + static Function newDecorator( + Iterable transientServiceOptions) { + requireNonNull(transientServiceOptions, "transientServiceOptions"); + return delegate -> new WrappingTransientHttpService(delegate, + Sets.immutableEnumSet(transientServiceOptions)); + } } diff --git a/core/src/main/java/com/linecorp/armeria/server/TransientRpcService.java b/core/src/main/java/com/linecorp/armeria/server/TransientRpcService.java index 969f182f211..ac732a5a4c1 100644 --- a/core/src/main/java/com/linecorp/armeria/server/TransientRpcService.java +++ b/core/src/main/java/com/linecorp/armeria/server/TransientRpcService.java @@ -16,6 +16,13 @@ package com.linecorp.armeria.server; +import static java.util.Objects.requireNonNull; + +import java.util.function.Function; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; + import com.linecorp.armeria.common.RpcRequest; import com.linecorp.armeria.common.RpcResponse; @@ -24,4 +31,25 @@ */ @FunctionalInterface public interface TransientRpcService extends TransientService, RpcService { + + /** + * Returns a new {@link RpcService} decorator which makes the specified {@link RpcService} as + * {@link TransientService}. + */ + static Function newDecorator( + TransientServiceOption... transientServiceOptions) { + requireNonNull(transientServiceOptions, "transientServiceOptions"); + return newDecorator(ImmutableSet.copyOf(transientServiceOptions)); + } + + /** + * Returns a new {@link RpcService} decorator which makes the specified {@link RpcService} as + * {@link TransientService}. + */ + static Function newDecorator( + Iterable transientServiceOptions) { + requireNonNull(transientServiceOptions, "transientServiceOptions"); + return delegate -> new WrappingTransientRpcService(delegate, + Sets.immutableEnumSet(transientServiceOptions)); + } } diff --git a/core/src/main/java/com/linecorp/armeria/server/WrappingTransientHttpService.java b/core/src/main/java/com/linecorp/armeria/server/WrappingTransientHttpService.java new file mode 100644 index 00000000000..77d0327f9ba --- /dev/null +++ b/core/src/main/java/com/linecorp/armeria/server/WrappingTransientHttpService.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.server; + +import java.util.Set; + +import com.linecorp.armeria.common.HttpRequest; +import com.linecorp.armeria.common.HttpResponse; + +/** + * Decorates a {@link HttpService} to be treated as {@link TransientService} without inheritance. + */ +final class WrappingTransientHttpService extends SimpleDecoratingHttpService implements TransientHttpService { + + private final Set transientServiceOptions; + + WrappingTransientHttpService(HttpService delegate, Set transientServiceOptions) { + super(delegate); + this.transientServiceOptions = transientServiceOptions; + } + + @Override + public HttpResponse serve(ServiceRequestContext ctx, HttpRequest req) throws Exception { + return unwrap().serve(ctx, req); + } + + @Override + public Set transientServiceOptions() { + return transientServiceOptions; + } +} diff --git a/core/src/main/java/com/linecorp/armeria/server/WrappingTransientRpcService.java b/core/src/main/java/com/linecorp/armeria/server/WrappingTransientRpcService.java new file mode 100644 index 00000000000..87ccb6ca946 --- /dev/null +++ b/core/src/main/java/com/linecorp/armeria/server/WrappingTransientRpcService.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.server; + +import java.util.Set; + +import com.linecorp.armeria.common.RpcRequest; +import com.linecorp.armeria.common.RpcResponse; + +/** + * Decorates a {@link RpcService} to be treated as {@link TransientService} without inheritance. + */ +final class WrappingTransientRpcService extends SimpleDecoratingRpcService implements TransientRpcService { + + private final Set transientServiceOptions; + + WrappingTransientRpcService(RpcService delegate, Set transientServiceOptions) { + super(delegate); + this.transientServiceOptions = transientServiceOptions; + } + + @Override + public RpcResponse serve(ServiceRequestContext ctx, RpcRequest req) throws Exception { + return unwrap().serve(ctx, req); + } + + @Override + public Set transientServiceOptions() { + return transientServiceOptions; + } +} diff --git a/core/src/test/java/com/linecorp/armeria/server/WrappingTransientHttpServiceTest.java b/core/src/test/java/com/linecorp/armeria/server/WrappingTransientHttpServiceTest.java new file mode 100644 index 00000000000..3054bbd0441 --- /dev/null +++ b/core/src/test/java/com/linecorp/armeria/server/WrappingTransientHttpServiceTest.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.server; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import com.linecorp.armeria.common.HttpResponse; + +class WrappingTransientHttpServiceTest { + + static final HttpService fooService = (ctx, req) -> HttpResponse.of("foo"); + + @Test + void extractTransientServiceOptions() { + final HttpService wrapped = fooService.decorate( + TransientHttpService.newDecorator(TransientServiceOption.WITH_ACCESS_LOGGING)); + + @SuppressWarnings("rawtypes") + final TransientService transientService = wrapped.as(TransientService.class); + assertThat(transientService).isNotNull(); + + @SuppressWarnings("unchecked") + final Set transientServiceOptions = + (Set) transientService.transientServiceOptions(); + assertThat(transientServiceOptions).containsExactly(TransientServiceOption.WITH_ACCESS_LOGGING); + } +} diff --git a/core/src/test/java/com/linecorp/armeria/server/WrappingTransientRpcServiceTest.java b/core/src/test/java/com/linecorp/armeria/server/WrappingTransientRpcServiceTest.java new file mode 100644 index 00000000000..4ccbbfb70b2 --- /dev/null +++ b/core/src/test/java/com/linecorp/armeria/server/WrappingTransientRpcServiceTest.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.server; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import com.linecorp.armeria.common.RpcResponse; + +class WrappingTransientRpcServiceTest { + + static final RpcService fooService = (ctx, req) -> RpcResponse.of("foo"); + + @Test + void extractTransientServiceOptions() { + final RpcService wrapped = fooService.decorate( + TransientRpcService.newDecorator(TransientServiceOption.WITH_ACCESS_LOGGING)); + + @SuppressWarnings("rawtypes") + final TransientService transientService = wrapped.as(TransientService.class); + assertThat(transientService).isNotNull(); + + @SuppressWarnings("unchecked") + final Set transientServiceOptions = + (Set) transientService.transientServiceOptions(); + assertThat(transientServiceOptions).containsExactly(TransientServiceOption.WITH_ACCESS_LOGGING); + } +}