Skip to content

Commit

Permalink
New Scenario: RequestScope custom context was removed after `javax.en…
Browse files Browse the repository at this point in the history
…terprise` event propagation

When firing an async CDI Event, the requestScope context from the emitter briefly exists for the observer
and is then terminated. This commit is a reproducer of this scenario that happens on Quarkus 2.13.0.Final
  • Loading branch information
pablo gonzalez granados committed Jan 16, 2023
1 parent 2350b46 commit ef3eed6
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,19 +1,66 @@
package io.quarkus.ts.http.advanced;

import java.time.Duration;
import java.util.Objects;

import javax.enterprise.event.Event;
import javax.enterprise.event.ObservesAsync;
import javax.inject.Inject;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;

@Path("/hello")
public class HelloResource {
private static final String TEMPLATE = "Hello, %s!";
public static final int EVENT_PROPAGATION_WAIT_MS = 250;
private String contextValueAfterEventPropagation;

@Inject
Event<String> event;

@Inject
LocalCustomContext localCustomContext;

@GET
@Produces(MediaType.APPLICATION_JSON)
public Hello get(@QueryParam("name") @DefaultValue("World") String name) {
return new Hello(String.format(TEMPLATE, name));
}

@PUT()
@Path("/local-context/{value}")
public void updateContext(@PathParam("value") String value) {
event.fireAsync(value);
}

@GET()
@Path("/local-context/{value}")
public String retrieveContextValue(@PathParam("value") String value) {
if (Objects.isNull(contextValueAfterEventPropagation)) {
throw new NotFoundException(String.format("Resource %s not found!", value));
}

return contextValueAfterEventPropagation;
}

void dequeueProcessingRequests(@ObservesAsync String value) {
localCustomContext.set(value);
// 'wait' is required in order to reproduce the issue https://github.com/quarkusio/quarkus/issues/29017
wait(Duration.ofMillis(EVENT_PROPAGATION_WAIT_MS));
contextValueAfterEventPropagation = localCustomContext.get();
}

private void wait(Duration timeout) {
try {
Thread.sleep(timeout.toMillis());
} catch (Exception e) {
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.quarkus.ts.http.advanced;

import javax.enterprise.context.RequestScoped;

@RequestScoped
public class LocalCustomContext {

String value;

public void set(String value) {
this.value = value;
}

public String get() {
return value;
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.quarkus.ts.http.advanced;

import static io.quarkus.ts.http.advanced.HelloResource.EVENT_PROPAGATION_WAIT_MS;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
Expand Down Expand Up @@ -212,16 +213,33 @@ public void vertxHttpClientRedirection() throws InterruptedException, URISyntaxE
@Tag("QUARKUS-2004")
public void constraintsExist() throws JsonProcessingException {
io.restassured.response.Response response = getApp().given().get("/q/openapi");
Assertions.assertEquals(HttpStatus.SC_OK, response.statusCode());
assertEquals(HttpStatus.SC_OK, response.statusCode());

ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
JsonNode body = mapper.readTree(response.body().asString());

JsonNode validation = body.get("components").get("schemas").get("Hello").get("properties").get("content");

Assertions.assertEquals(4, validation.get("maxLength").asInt());
Assertions.assertEquals(1, validation.get("minLength").asInt());
Assertions.assertEquals("^[A-Za-z]+$", validation.get("pattern").asText());
assertEquals(4, validation.get("maxLength").asInt());
assertEquals(1, validation.get("minLength").asInt());
assertEquals("^[A-Za-z]+$", validation.get("pattern").asText());
}

@Test
@Tag("QUARKUS-2785")
public void keepRequestScopeValuesAfterEventPropagation() {
final String requestScopeValue = "myValue";
getApp().given().when().put("/api/hello/local-context/" + requestScopeValue).then().statusCode(204);
// Please be sure that awaitTime is greater than 'helloResource.EVENT_PROPAGATION_WAIT_MS'
int awaitTime = EVENT_PROPAGATION_WAIT_MS + EVENT_PROPAGATION_WAIT_MS;
wait(Duration.ofMillis(awaitTime));
io.restassured.response.Response resp = getApp().given().when()
.get("/api/hello/local-context/" + requestScopeValue)
.then().extract().response();

assertEquals(200, resp.statusCode(), "RequestScope custom context has been removed with the event propagation");
Assertions.assertTrue(resp.asString().equalsIgnoreCase(requestScopeValue),
"Unexpected requestScope custom context value");
}

protected Protocol getProtocol() {
Expand All @@ -247,4 +265,11 @@ private String defaultTruststore() throws URISyntaxException {
URL res = getClass().getClassLoader().getResource(KEY_STORE_PATH);
return Paths.get(res.toURI()).toFile().getAbsolutePath();
}

private void wait(Duration timeout) {
try {
Thread.sleep(timeout.toMillis());
} catch (Exception e) {
}
}
}

0 comments on commit ef3eed6

Please sign in to comment.