Request scope propagation for non-async calls in FT (#2495)

* New test for issue 2480.

Signed-off-by: Santiago Pericasgeertsen <santiago.pericasgeertsen@oracle.com>

* Swithed to new test framework for MP.

Signed-off-by: Santiago Pericasgeertsen <santiago.pericasgeertsen@oracle.com>

* Use a wrapped supplier to propagate request scope even for synchronous calls as some FT primitives (like Retry) use executors and may run code in a different thread. Updated new test that fails without this change and passes with it.

Signed-off-by: Santiago Pericasgeertsen <santiago.pericasgeertsen@oracle.com>

* Renamed variable.

Signed-off-by: Santiago Pericasgeertsen <santiago.pericasgeertsen@oracle.com>

* Fixed copyright problems.

Signed-off-by: Santiago Pericasgeertsen <santiago.pericasgeertsen@oracle.com>

* Dropped fixed port in config.

Signed-off-by: Santiago Pericasgeertsen <santiago.pericasgeertsen@oracle.com>

* Remove redundant main class.

Signed-off-by: Santiago Pericasgeertsen <santiago.pericasgeertsen@oracle.com>
This commit is contained in:
Santiago Pericasgeertsen
2020-11-04 09:20:46 -05:00
committed by GitHub
parent 7425747cc5
commit e7b9b8b8d3
15 changed files with 410 additions and 6 deletions

View File

@@ -577,11 +577,11 @@ class MethodInvoker implements FtSupplier<Object> {
return () -> {
invocationStartNanos = System.nanoTime();
// Wrap supplier with request context setup
FtSupplier wrappedSupplier = requestContextSupplier(supplier);
CompletableFuture<Object> 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<Object> single = Async.create().invoke(() -> {
@@ -634,7 +634,7 @@ class MethodInvoker implements FtSupplier<Object> {
});
} else {
try {
resultFuture.complete(supplier.get());
resultFuture.complete(wrappedSupplier.get());
return resultFuture;
} catch (Throwable t) {
resultFuture.completeExceptionally(t);

View File

@@ -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.
*/

View File

@@ -40,5 +40,6 @@
<module>context-propagation</module>
<module>mp-synthetic-app</module>
<module>mp-compression</module>
<module>request-scope</module>
</modules>
</project>

View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>helidon-tests-functional-project</artifactId>
<groupId>io.helidon.tests.functional</groupId>
<version>2.1.1-SNAPSHOT</version>
</parent>
<artifactId>helidon-tests-functional-request-scope</artifactId>
<name>Helidon Functional Test: Request Scope and Fault Tolerance</name>
<dependencies>
<dependency>
<groupId>io.helidon.microprofile.bundles</groupId>
<artifactId>helidon-microprofile-core</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.config</groupId>
<artifactId>helidon-config-yaml</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.microprofile</groupId>
<artifactId>helidon-microprofile-fault-tolerance</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.microprofile.metrics</groupId>
<artifactId>helidon-microprofile-metrics</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.helidon.microprofile.tests</groupId>
<artifactId>helidon-microprofile-tests-junit5</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

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

View File

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

View File

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

View File

@@ -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();
}
}

View File

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

View File

@@ -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 {
}

View File

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

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
version="2.0"
bean-discovery-mode="annotated">
</beans>

View File

@@ -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

View File

@@ -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()));
}
}

View File

@@ -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