forked from quarkusio/quarkus
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ArC: introduce the ActivateSessionContext interceptor binding
- it's only available in tests - fixes quarkusio#45146
- Loading branch information
Showing
8 changed files
with
217 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
71 changes: 71 additions & 0 deletions
71
extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcTestSteps.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package io.quarkus.arc.deployment; | ||
|
||
import java.util.HashSet; | ||
import java.util.Set; | ||
import java.util.function.Predicate; | ||
|
||
import org.jboss.jandex.AnnotationInstance; | ||
import org.jboss.jandex.AnnotationTransformation; | ||
import org.jboss.jandex.DotName; | ||
|
||
import io.quarkus.arc.processor.BeanInfo; | ||
import io.quarkus.arc.runtime.ArcRecorder; | ||
import io.quarkus.arc.runtime.test.ActivateSessionContextInterceptor; | ||
import io.quarkus.arc.runtime.test.PreloadedTestApplicationClassPredicate; | ||
import io.quarkus.deployment.IsTest; | ||
import io.quarkus.deployment.annotations.BuildProducer; | ||
import io.quarkus.deployment.annotations.BuildStep; | ||
import io.quarkus.deployment.annotations.BuildSteps; | ||
import io.quarkus.deployment.annotations.ExecutionTime; | ||
import io.quarkus.deployment.annotations.Record; | ||
import io.quarkus.deployment.builditem.ApplicationClassPredicateBuildItem; | ||
|
||
@BuildSteps(onlyIf = IsTest.class) | ||
public class ArcTestSteps { | ||
|
||
@BuildStep | ||
public void additionalBeans(BuildProducer<AdditionalBeanBuildItem> additionalBeans) { | ||
// We need to register the bean implementation for TestApplicationClassPredicate | ||
// TestApplicationClassPredicate is used programmatically in the ArC recorder when StartupEvent is fired | ||
additionalBeans.produce(AdditionalBeanBuildItem.unremovableOf(PreloadedTestApplicationClassPredicate.class)); | ||
// In tests, register the ActivateSessionContextInterceptor and ActivateSessionContext interceptor binding | ||
additionalBeans.produce(new AdditionalBeanBuildItem(ActivateSessionContextInterceptor.class)); | ||
additionalBeans.produce(new AdditionalBeanBuildItem("io.quarkus.test.ActivateSessionContext")); | ||
} | ||
|
||
@BuildStep | ||
AnnotationsTransformerBuildItem addInterceptorBinding() { | ||
return new AnnotationsTransformerBuildItem( | ||
AnnotationTransformation.forClasses().whenClass(ActivateSessionContextInterceptor.class).transform(tc -> tc.add( | ||
AnnotationInstance.builder(DotName.createSimple("io.quarkus.test.ActivateSessionContext")).build()))); | ||
} | ||
|
||
// For some reason the annotation literal generated for io.quarkus.test.ActivateSessionContext lives in app class loader. | ||
// This predicates ensures that the generated bean is considered an app class too. | ||
// As a consequence, the type and all methods of ActivateSessionContextInterceptor must be public. | ||
@BuildStep | ||
ApplicationClassPredicateBuildItem appClassPredicate() { | ||
return new ApplicationClassPredicateBuildItem(new Predicate<String>() { | ||
|
||
@Override | ||
public boolean test(String name) { | ||
return name.startsWith(ActivateSessionContextInterceptor.class.getName()); | ||
} | ||
}); | ||
} | ||
|
||
@BuildStep | ||
@Record(ExecutionTime.STATIC_INIT) | ||
void initTestApplicationClassPredicateBean(ArcRecorder recorder, BeanContainerBuildItem beanContainer, | ||
BeanDiscoveryFinishedBuildItem beanDiscoveryFinished, | ||
CompletedApplicationClassPredicateBuildItem predicate) { | ||
Set<String> applicationBeanClasses = new HashSet<>(); | ||
for (BeanInfo bean : beanDiscoveryFinished.beanStream().classBeans()) { | ||
if (predicate.test(bean.getBeanClass())) { | ||
applicationBeanClasses.add(bean.getBeanClass().toString()); | ||
} | ||
} | ||
recorder.initTestApplicationClassPredicate(applicationBeanClasses); | ||
} | ||
|
||
} |
32 changes: 32 additions & 0 deletions
32
extensions/arc/deployment/src/test/java/io/quarkus/arc/test/context/session/Client.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package io.quarkus.arc.test.context.session; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
import static org.junit.jupiter.api.Assertions.fail; | ||
|
||
import jakarta.enterprise.context.Dependent; | ||
import jakarta.enterprise.context.SessionScoped; | ||
import jakarta.inject.Inject; | ||
|
||
import io.quarkus.arc.Arc; | ||
import io.quarkus.arc.ClientProxy; | ||
import io.quarkus.test.ActivateSessionContext; | ||
|
||
@Dependent | ||
class Client { | ||
|
||
@Inject | ||
SimpleBean bean; | ||
|
||
@ActivateSessionContext | ||
public boolean ping() { | ||
assertTrue(Arc.container().sessionContext().isActive()); | ||
if (bean instanceof ClientProxy proxy) { | ||
assertEquals(SessionScoped.class, proxy.arc_bean().getScope()); | ||
} else { | ||
fail("Not a client proxy"); | ||
} | ||
return bean.ping(); | ||
} | ||
|
||
} |
31 changes: 31 additions & 0 deletions
31
.../arc/deployment/src/test/java/io/quarkus/arc/test/context/session/SessionContextTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package io.quarkus.arc.test.context.session; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertFalse; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
import jakarta.inject.Inject; | ||
|
||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.RegisterExtension; | ||
|
||
import io.quarkus.arc.Arc; | ||
import io.quarkus.test.QuarkusUnitTest; | ||
|
||
public class SessionContextTest { | ||
|
||
@RegisterExtension | ||
static final QuarkusUnitTest config = new QuarkusUnitTest() | ||
.withApplicationRoot(root -> root | ||
.addClasses(SimpleBean.class, Client.class)); | ||
|
||
@Inject | ||
Client client; | ||
|
||
@Test | ||
public void testContexts() { | ||
assertFalse(Arc.container().sessionContext().isActive()); | ||
assertTrue(client.ping()); | ||
assertTrue(SimpleBean.DESTROYED.get()); | ||
assertFalse(Arc.container().sessionContext().isActive()); | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
extensions/arc/deployment/src/test/java/io/quarkus/arc/test/context/session/SimpleBean.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package io.quarkus.arc.test.context.session; | ||
|
||
import java.util.concurrent.atomic.AtomicBoolean; | ||
|
||
import jakarta.annotation.PreDestroy; | ||
import jakarta.enterprise.context.SessionScoped; | ||
|
||
@SessionScoped | ||
class SimpleBean { | ||
|
||
static final AtomicBoolean DESTROYED = new AtomicBoolean(); | ||
|
||
public boolean ping() { | ||
return true; | ||
} | ||
|
||
@PreDestroy | ||
void destroy() { | ||
DESTROYED.set(true); | ||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
.../runtime/src/main/java/io/quarkus/arc/runtime/test/ActivateSessionContextInterceptor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package io.quarkus.arc.runtime.test; | ||
|
||
import jakarta.annotation.Priority; | ||
import jakarta.interceptor.AroundInvoke; | ||
import jakarta.interceptor.Interceptor; | ||
import jakarta.interceptor.InvocationContext; | ||
|
||
import io.quarkus.arc.Arc; | ||
import io.quarkus.arc.ManagedContext; | ||
|
||
// The @ActivateSessionContext interceptor binding is added by the extension | ||
@Interceptor | ||
@Priority(Interceptor.Priority.PLATFORM_BEFORE + 100) | ||
public class ActivateSessionContextInterceptor { | ||
|
||
@AroundInvoke | ||
public Object aroundInvoke(InvocationContext ctx) throws Exception { | ||
ManagedContext sessionContext = Arc.container().sessionContext(); | ||
if (sessionContext.isActive()) { | ||
return ctx.proceed(); | ||
} | ||
try { | ||
sessionContext.activate(); | ||
return ctx.proceed(); | ||
} finally { | ||
sessionContext.terminate(); | ||
} | ||
} | ||
|
||
} |
30 changes: 30 additions & 0 deletions
30
test-framework/common/src/main/java/io/quarkus/test/ActivateSessionContext.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package io.quarkus.test; | ||
|
||
import static java.lang.annotation.ElementType.METHOD; | ||
import static java.lang.annotation.ElementType.TYPE; | ||
import static java.lang.annotation.RetentionPolicy.RUNTIME; | ||
|
||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.Target; | ||
import java.util.concurrent.CompletionStage; | ||
|
||
import jakarta.interceptor.InterceptorBinding; | ||
|
||
/** | ||
* Activates the session context before the intercepted method is called, and terminates the context when the method invocation | ||
* completes (regardless of any exceptions being thrown). | ||
* <p> | ||
* If the context is already active, it's a noop - the context is neither activated nor deactivated. | ||
* <p> | ||
* Keep in mind that if the method returns an asynchronous type (such as {@link CompletionStage} then the session context is | ||
* still terminate when the invocation completes and not at the time the asynchronous type is completed. Also note that session | ||
* context is not propagated by MicroProfile Context Propagation. | ||
* <p> | ||
* This interceptor binding is only available in tests. | ||
*/ | ||
@InterceptorBinding | ||
@Target({ METHOD, TYPE }) | ||
@Retention(RUNTIME) | ||
public @interface ActivateSessionContext { | ||
|
||
} |