View Javadoc
1   package com.github.jonasrutishauser.transactional.event.quarkus.deployment.it;
2   
3   import static java.util.concurrent.TimeUnit.SECONDS;
4   import static org.hamcrest.Matchers.is;
5   import static org.junit.jupiter.api.Assertions.assertFalse;
6   
7   import java.util.concurrent.CountDownLatch;
8   
9   import org.jboss.shrinkwrap.api.asset.EmptyAsset;
10  import org.junit.jupiter.api.Test;
11  import org.junit.jupiter.api.extension.RegisterExtension;
12  
13  import com.github.jonasrutishauser.transactional.event.api.EventPublisher;
14  import com.github.jonasrutishauser.transactional.event.api.handler.EventHandler;
15  
16  import io.quarkus.test.QuarkusUnitTest;
17  import io.quarkus.vertx.core.runtime.context.VertxContextSafetyToggle;
18  import io.restassured.RestAssured;
19  import jakarta.enterprise.context.ApplicationScoped;
20  import jakarta.enterprise.context.Dependent;
21  import jakarta.inject.Inject;
22  import jakarta.transaction.Transactional;
23  import jakarta.ws.rs.POST;
24  import jakarta.ws.rs.Path;
25  import jakarta.ws.rs.Produces;
26  import jakarta.ws.rs.core.MediaType;
27  
28  class RequestContextIsolationIT {
29  
30      @RegisterExtension
31      static final QuarkusUnitTest config = new QuarkusUnitTest() //
32              .setFlatClassPath(true) // needed for invoker
33              .withApplicationRoot(archive -> archive //
34                      .addClasses(RequestContextProbe.class, Rendezvous.class, PublishingService.class,
35                              PublishingResource.class, RendezvousHandler.class) //
36                      .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml") //
37              );
38  
39      @Inject
40      Rendezvous rendezvous;
41  
42      @Test
43      void testPublishingRequestKeepsItsRequestContext() {
44          RestAssured.when() //
45                  .post("/request-context") //
46                  .then() //
47                  .assertThat() //
48                  .statusCode(200) //
49                  .body(is("request context kept"));
50  
51          assertFalse(rendezvous.isRequestContextSeenByHandler());
52      }
53  
54      @Path("/request-context")
55      @Dependent
56      public static class PublishingResource {
57  
58          @Inject
59          private RequestContextProbe requestContextProbe;
60  
61          @Inject
62          private PublishingService service;
63  
64          @Inject
65          private Rendezvous rendezvous;
66  
67          @POST
68          @Produces(MediaType.TEXT_PLAIN)
69          public String publish() {
70              requestContextProbe.mark();
71              service.publish();
72              try {
73                  rendezvous.awaitHandlerStarted();
74                  return requestContextProbe.isMarked() ? "request context kept" : "request context lost";
75              } finally {
76                  rendezvous.requestChecked();
77              }
78          }
79  
80      }
81  
82      @Dependent
83      public static class PublishingService {
84  
85          private final EventPublisher publisher;
86  
87          @Inject
88          PublishingService(EventPublisher publisher) {
89              this.publisher = publisher;
90          }
91  
92          @Transactional
93          public void publish() {
94              publisher.publish("rendezvous");
95          }
96  
97      }
98  
99      @Dependent
100     public static class RendezvousHandler {
101 
102         private final RequestContextProbe requestContextProbe;
103         private final Rendezvous rendezvous;
104 
105         @Inject
106         RendezvousHandler(RequestContextProbe requestContextProbe, Rendezvous rendezvous) {
107             this.requestContextProbe = requestContextProbe;
108             this.rendezvous = rendezvous;
109         }
110 
111         @EventHandler
112         void handle(String event) {
113             VertxContextSafetyToggle.validateContextIfExists("processing context is marked unsafe",
114                     "processing context is not marked safe");
115             rendezvous.handlerStarted(requestContextProbe.isMarked());
116         }
117 
118     }
119 
120     @ApplicationScoped
121     public static class Rendezvous {
122 
123         private final CountDownLatch handlerStarted = new CountDownLatch(1);
124         private final CountDownLatch requestChecked = new CountDownLatch(1);
125         private volatile boolean requestContextSeenByHandler;
126 
127         void handlerStarted(boolean requestContextSeen) {
128             requestContextSeenByHandler = requestContextSeen;
129             handlerStarted.countDown();
130             await(requestChecked);
131         }
132 
133         void awaitHandlerStarted() {
134             await(handlerStarted);
135         }
136 
137         void requestChecked() {
138             requestChecked.countDown();
139         }
140 
141         boolean isRequestContextSeenByHandler() {
142             return requestContextSeenByHandler;
143         }
144 
145         private static void await(CountDownLatch latch) {
146             try {
147                 if (!latch.await(10, SECONDS)) {
148                     throw new IllegalStateException("rendezvous timed out");
149                 }
150             } catch (InterruptedException e) {
151                 Thread.currentThread().interrupt();
152                 throw new IllegalStateException("rendezvous interrupted", e);
153             }
154         }
155 
156     }
157 
158 }