diff --git a/microprofile/fault-tolerance/src/main/java/io/helidon/microprofile/faulttolerance/MethodInvoker.java b/microprofile/fault-tolerance/src/main/java/io/helidon/microprofile/faulttolerance/MethodInvoker.java index 3268183bd..67babc25a 100644 --- a/microprofile/fault-tolerance/src/main/java/io/helidon/microprofile/faulttolerance/MethodInvoker.java +++ b/microprofile/fault-tolerance/src/main/java/io/helidon/microprofile/faulttolerance/MethodInvoker.java @@ -577,11 +577,11 @@ class MethodInvoker implements FtSupplier { return () -> { invocationStartNanos = System.nanoTime(); + // Wrap supplier with request context setup + FtSupplier wrappedSupplier = requestContextSupplier(supplier); + CompletableFuture resultFuture = new CompletableFuture<>(); if (introspector.isAsynchronous()) { - // Wrap supplier with request context setup - FtSupplier wrappedSupplier = requestContextSupplier(supplier); - // Invoke supplier in new thread and propagate ccl for config ClassLoader ccl = Thread.currentThread().getContextClassLoader(); Single single = Async.create().invoke(() -> { @@ -634,7 +634,7 @@ class MethodInvoker implements FtSupplier { }); } else { try { - resultFuture.complete(supplier.get()); + resultFuture.complete(wrappedSupplier.get()); return resultFuture; } catch (Throwable t) { resultFuture.completeExceptionally(t); diff --git a/tests/functional/context-propagation/src/main/java/io/helidon/tests/functional/context/hello/HelloBean.java b/tests/functional/context-propagation/src/main/java/io/helidon/tests/functional/context/hello/HelloBean.java index 4423b3ba5..61ba987d9 100644 --- a/tests/functional/context-propagation/src/main/java/io/helidon/tests/functional/context/hello/HelloBean.java +++ b/tests/functional/context-propagation/src/main/java/io/helidon/tests/functional/context/hello/HelloBean.java @@ -52,7 +52,7 @@ public class HelloBean { } /** - * Runs in Hystrix thread. + * Runs in FT thread. * * @return Hello string. */ @@ -68,7 +68,7 @@ public class HelloBean { } /** - * Runs in Hystrix thread via FT async thread. + * Runs via FT async thread. * * @return Hello string. */ diff --git a/tests/functional/pom.xml b/tests/functional/pom.xml index 0c2173318..7e95b2904 100644 --- a/tests/functional/pom.xml +++ b/tests/functional/pom.xml @@ -40,5 +40,6 @@ context-propagation mp-synthetic-app mp-compression + request-scope diff --git a/tests/functional/request-scope/pom.xml b/tests/functional/request-scope/pom.xml new file mode 100644 index 000000000..cfe0206bd --- /dev/null +++ b/tests/functional/request-scope/pom.xml @@ -0,0 +1,69 @@ + + + + + 4.0.0 + + helidon-tests-functional-project + io.helidon.tests.functional + 2.1.1-SNAPSHOT + + + helidon-tests-functional-request-scope + Helidon Functional Test: Request Scope and Fault Tolerance + + + + io.helidon.microprofile.bundles + helidon-microprofile-core + + + io.helidon.config + helidon-config-yaml + + + io.helidon.microprofile + helidon-microprofile-fault-tolerance + + + io.helidon.microprofile.metrics + helidon-microprofile-metrics + + + org.junit.jupiter + junit-jupiter-api + test + + + org.hamcrest + hamcrest-all + test + + + org.junit.jupiter + junit-jupiter-params + test + + + io.helidon.microprofile.tests + helidon-microprofile-tests-junit5 + test + + + diff --git a/tests/functional/request-scope/src/main/java/io/helidon/tests/functional/requestscope/IllegalTenantException.java b/tests/functional/request-scope/src/main/java/io/helidon/tests/functional/requestscope/IllegalTenantException.java new file mode 100644 index 000000000..df364393f --- /dev/null +++ b/tests/functional/request-scope/src/main/java/io/helidon/tests/functional/requestscope/IllegalTenantException.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 + * + * http://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 io.helidon.tests.functional.requestscope; + +public class IllegalTenantException extends Exception { + + public IllegalTenantException(String message) { + super(message); + } +} diff --git a/tests/functional/request-scope/src/main/java/io/helidon/tests/functional/requestscope/MultiTenantService.java b/tests/functional/request-scope/src/main/java/io/helidon/tests/functional/requestscope/MultiTenantService.java new file mode 100644 index 000000000..b407250af --- /dev/null +++ b/tests/functional/request-scope/src/main/java/io/helidon/tests/functional/requestscope/MultiTenantService.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 + * + * http://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 io.helidon.tests.functional.requestscope; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; + +@RequestScoped +@Path("/test") +public class MultiTenantService { + + @Inject + SomeService someService; + + @GET + public String getTenantResource() { + try { + return someService.test(); + } catch (IllegalTenantException e) { + return "Expected"; + } catch (Exception e) { + // This path implies a CDI exception related to request scope + // See https://github.com/oracle/helidon/issues/2480 + throw new WebApplicationException(e, Response.Status.INTERNAL_SERVER_ERROR); + } + } +} diff --git a/tests/functional/request-scope/src/main/java/io/helidon/tests/functional/requestscope/RequestTestQualifier.java b/tests/functional/request-scope/src/main/java/io/helidon/tests/functional/requestscope/RequestTestQualifier.java new file mode 100644 index 000000000..5dfe3dcb7 --- /dev/null +++ b/tests/functional/request-scope/src/main/java/io/helidon/tests/functional/requestscope/RequestTestQualifier.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 + * + * http://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 io.helidon.tests.functional.requestscope; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; + +@RequestScoped +@TestQualifier +public class RequestTestQualifier { + + @Inject + TenantContext tenantContext; + + public String test() throws Exception { + String tenantId = tenantContext.getTenantId(); + System.out.println("Tenant Context: " + tenantId); + if (tenantId == null) { + throw new IllegalTenantException("No tenant context"); + } + return tenantId; + } +} diff --git a/tests/functional/request-scope/src/main/java/io/helidon/tests/functional/requestscope/SomeService.java b/tests/functional/request-scope/src/main/java/io/helidon/tests/functional/requestscope/SomeService.java new file mode 100644 index 000000000..89df5451f --- /dev/null +++ b/tests/functional/request-scope/src/main/java/io/helidon/tests/functional/requestscope/SomeService.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 + * + * http://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 io.helidon.tests.functional.requestscope; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; + +import org.eclipse.microprofile.faulttolerance.Retry; + +@ApplicationScoped +public class SomeService { + + @Inject + @TestQualifier + RequestTestQualifier requestTestQualifier; + + @Retry + public String test() throws Exception { + return requestTestQualifier.test(); + } +} diff --git a/tests/functional/request-scope/src/main/java/io/helidon/tests/functional/requestscope/TenantContext.java b/tests/functional/request-scope/src/main/java/io/helidon/tests/functional/requestscope/TenantContext.java new file mode 100644 index 000000000..6ec38f186 --- /dev/null +++ b/tests/functional/request-scope/src/main/java/io/helidon/tests/functional/requestscope/TenantContext.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 + * + * http://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 io.helidon.tests.functional.requestscope; + +import javax.enterprise.context.RequestScoped; +import javax.ws.rs.core.Context; + +import io.helidon.webserver.ServerRequest; + +@RequestScoped +public class TenantContext { + + @Context + private ServerRequest request; + + public String getTenantId() { + return request.headers().value("x-tenant-id").orElse(null); + } +} diff --git a/tests/functional/request-scope/src/main/java/io/helidon/tests/functional/requestscope/TestQualifier.java b/tests/functional/request-scope/src/main/java/io/helidon/tests/functional/requestscope/TestQualifier.java new file mode 100644 index 000000000..9b8a7bb4d --- /dev/null +++ b/tests/functional/request-scope/src/main/java/io/helidon/tests/functional/requestscope/TestQualifier.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 + * + * http://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 io.helidon.tests.functional.requestscope; + +import javax.inject.Qualifier; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Qualifier +@Retention(RUNTIME) +@Target({TYPE, METHOD, FIELD, PARAMETER}) +public @interface TestQualifier { +} diff --git a/tests/functional/request-scope/src/main/java/io/helidon/tests/functional/requestscope/package-info.java b/tests/functional/request-scope/src/main/java/io/helidon/tests/functional/requestscope/package-info.java new file mode 100644 index 000000000..cdc3f42f2 --- /dev/null +++ b/tests/functional/request-scope/src/main/java/io/helidon/tests/functional/requestscope/package-info.java @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 + * + * http://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 io.helidon.tests.functional.requestscope; diff --git a/tests/functional/request-scope/src/main/resources/META-INF/beans.xml b/tests/functional/request-scope/src/main/resources/META-INF/beans.xml new file mode 100644 index 000000000..6ea8c3c71 --- /dev/null +++ b/tests/functional/request-scope/src/main/resources/META-INF/beans.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/tests/functional/request-scope/src/main/resources/META-INF/microprofile-config.properties b/tests/functional/request-scope/src/main/resources/META-INF/microprofile-config.properties new file mode 100644 index 000000000..c501c38d4 --- /dev/null +++ b/tests/functional/request-scope/src/main/resources/META-INF/microprofile-config.properties @@ -0,0 +1,17 @@ +# +# Copyright (c) 2020 Oracle and/or its affiliates. +# +# 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 +# +# http://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. +# + +server.host=0.0.0.0 diff --git a/tests/functional/request-scope/src/test/java/io/helidon/tests/functional/requestscope/TenantTest.java b/tests/functional/request-scope/src/test/java/io/helidon/tests/functional/requestscope/TenantTest.java new file mode 100644 index 000000000..dbf92880a --- /dev/null +++ b/tests/functional/request-scope/src/test/java/io/helidon/tests/functional/requestscope/TenantTest.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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 + * + * http://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 io.helidon.tests.functional.requestscope; + +import javax.inject.Inject; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Response; + +import io.helidon.microprofile.tests.junit5.HelidonTest; +import io.netty.handler.codec.http.HttpResponseStatus; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +@HelidonTest +class TenantTest { + + @Inject + private WebTarget baseTarget; + + @Test + public void test() { + Response r = baseTarget.path("test") + .request() + .get(); + assertThat(r.getStatus(), is(HttpResponseStatus.OK.code())); + } +} diff --git a/tests/functional/request-scope/src/test/resources/logging.properties b/tests/functional/request-scope/src/test/resources/logging.properties new file mode 100644 index 000000000..e688e921b --- /dev/null +++ b/tests/functional/request-scope/src/test/resources/logging.properties @@ -0,0 +1,30 @@ +# +# Copyright (c) 2020 Oracle and/or its affiliates. +# +# 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 +# +# http://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. +# + +# Example Logging Configuration File +# For more information see $JAVA_HOME/jre/lib/logging.properties + +# Send messages to the console +handlers=io.helidon.common.HelidonConsoleHandler + +# HelidonConsoleHandler uses a SimpleFormatter subclass that replaces "!thread!" with the current thread +java.util.logging.SimpleFormatter.format=%1$tY.%1$tm.%1$td %1$tH:%1$tM:%1$tS %4$s %3$s !thread!: %5$s%6$s%n + +# Global logging level. Can be overridden by specific loggers +.level=INFO + +# Component specific log levels +AUDIT.level=FINEST