From ab2f72459b5477a733b3dac541ff86eb7b1ef873 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Wed, 3 Oct 2018 11:35:50 +0200 Subject: [PATCH] Arc - request context propagation --- .../arc/runtime/ArcDeploymentTemplate.java | 6 +- .../protean/arc/example/InjectionTest.java | 7 +- .../protean/arc/processor/BeanGenerator.java | 3 +- .../arc/processor/MethodDescriptors.java | 3 + .../protean/arc/AbstractSharedContext.java | 24 ++++--- .../org/jboss/protean/arc/ArcContainer.java | 2 +- .../jboss/protean/arc/ArcContainerImpl.java | 20 ++++-- .../protean/arc/CreationalContextImpl.java | 4 +- .../jboss/protean/arc/InjectableContext.java | 25 +++++++ .../org/jboss/protean/arc/InstanceHandle.java | 18 +++-- .../jboss/protean/arc/InstanceHandleImpl.java | 25 +++++-- .../org/jboss/protean/arc/ManagedContext.java | 38 ++++++++++ .../org/jboss/protean/arc/RequestContext.java | 69 ++++++++++++------ .../interceptors/SimpleInterceptorTest.java | 4 +- .../requestcontext/RequestContextTest.java | 2 +- .../RequestContextPropagationTest.java | 70 +++++++++++++++++++ .../propagation/SuperButton.java | 18 +++++ .../propagation/SuperController.java | 39 +++++++++++ 18 files changed, 316 insertions(+), 61 deletions(-) create mode 100644 ext/arc/runtime/src/main/java/org/jboss/protean/arc/InjectableContext.java create mode 100644 ext/arc/runtime/src/main/java/org/jboss/protean/arc/ManagedContext.java create mode 100644 ext/arc/tests/src/test/java/org/jboss/protean/arc/test/requestcontext/propagation/RequestContextPropagationTest.java create mode 100644 ext/arc/tests/src/test/java/org/jboss/protean/arc/test/requestcontext/propagation/SuperButton.java create mode 100644 ext/arc/tests/src/test/java/org/jboss/protean/arc/test/requestcontext/propagation/SuperController.java diff --git a/arc/runtime/src/main/java/org/jboss/shamrock/arc/runtime/ArcDeploymentTemplate.java b/arc/runtime/src/main/java/org/jboss/shamrock/arc/runtime/ArcDeploymentTemplate.java index 77e6acad58148..02c8968763569 100644 --- a/arc/runtime/src/main/java/org/jboss/shamrock/arc/runtime/ArcDeploymentTemplate.java +++ b/arc/runtime/src/main/java/org/jboss/shamrock/arc/runtime/ArcDeploymentTemplate.java @@ -7,6 +7,7 @@ import org.jboss.protean.arc.Arc; import org.jboss.protean.arc.ArcContainer; import org.jboss.protean.arc.InstanceHandle; +import org.jboss.protean.arc.ManagedContext; import org.jboss.shamrock.runtime.BeanContainer; import org.jboss.shamrock.runtime.ContextObject; import org.jboss.shamrock.runtime.InjectionFactory; @@ -65,11 +66,12 @@ public Action create(Action action) { return new Action() { @Override public T call(HttpServerExchange exchange, C context) throws Exception { - arcContainer.requestContext().activate(); + ManagedContext requestContext = arcContainer.requestContext(); + requestContext.activate(); try { return action.call(exchange, context); } finally { - arcContainer.requestContext().deactivate(); + requestContext.terminate(); } } }; diff --git a/ext/arc/example/src/test/java/org/jboss/protean/arc/example/InjectionTest.java b/ext/arc/example/src/test/java/org/jboss/protean/arc/example/InjectionTest.java index ac4d3a3213ade..5e8f9bcab98ab 100644 --- a/ext/arc/example/src/test/java/org/jboss/protean/arc/example/InjectionTest.java +++ b/ext/arc/example/src/test/java/org/jboss/protean/arc/example/InjectionTest.java @@ -17,18 +17,17 @@ import org.junit.Test; public class InjectionTest { - + @BeforeClass public static void init() { Arc.initialize(); } - + @AfterClass public static void shutdown() { Arc.shutdown(); } - @SuppressWarnings("serial") @Test public void testInjection() { ArcContainer arc = Arc.container(); @@ -41,7 +40,7 @@ public void testInjection() { assertEquals("Lu Foo", foo.get().ping()); assertEquals("Lu Foo", foo.get().lazyPing()); assertEquals(foo.get(), arc.instance(Foo.class, new MyQualifier.OneLiteral()).get()); - foo.release(); + foo.destroy(); } @Test diff --git a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/BeanGenerator.java b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/BeanGenerator.java index 3e82f38024856..0971770aab8a6 100644 --- a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/BeanGenerator.java +++ b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/BeanGenerator.java @@ -997,8 +997,7 @@ protected void implementGet(BeanInfo bean, ClassCreator beanCreator, String prov MethodDescriptor.ofMethod(beanCreator.getClassName(), "create", providerTypeName, CreationalContext.class), get.getThis(), get.getMethodParam(0)); // CreationalContextImpl.addDependencyToParent(this,instance,ctx) - get.invokeStaticMethod(MethodDescriptor.ofMethod(CreationalContextImpl.class, "addDependencyToParent", void.class, InjectableBean.class, - Object.class, CreationalContext.class), get.getThis(), instance, get.getMethodParam(0)); + get.invokeStaticMethod(MethodDescriptors.CREATIONAL_CTX_ADD_DEP_TO_PARENT, get.getThis(), instance, get.getMethodParam(0)); // return instance get.returnValue(instance); } else if (ScopeInfo.SINGLETON.equals(bean.getScope())) { diff --git a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/MethodDescriptors.java b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/MethodDescriptors.java index b1bfc41b47ed7..98ddc8ae62986 100644 --- a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/MethodDescriptors.java +++ b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/MethodDescriptors.java @@ -88,6 +88,9 @@ final class MethodDescriptors { static final MethodDescriptor INVOCATION_CONTEXT_PRE_DESTROY = MethodDescriptor.ofMethod(InvocationContextImpl.class, "preDestroy", InvocationContextImpl.class, Object.class, List.class, Set.class); + static final MethodDescriptor CREATIONAL_CTX_ADD_DEP_TO_PARENT = MethodDescriptor.ofMethod(CreationalContextImpl.class, "addDependencyToParent", void.class, InjectableBean.class, + Object.class, CreationalContext.class); + private MethodDescriptors() { } diff --git a/ext/arc/runtime/src/main/java/org/jboss/protean/arc/AbstractSharedContext.java b/ext/arc/runtime/src/main/java/org/jboss/protean/arc/AbstractSharedContext.java index 975f33a994834..9e6873c074841 100644 --- a/ext/arc/runtime/src/main/java/org/jboss/protean/arc/AbstractSharedContext.java +++ b/ext/arc/runtime/src/main/java/org/jboss/protean/arc/AbstractSharedContext.java @@ -1,10 +1,13 @@ package org.jboss.protean.arc; -import javax.enterprise.context.spi.AlterableContext; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + import javax.enterprise.context.spi.Contextual; import javax.enterprise.context.spi.CreationalContext; -abstract class AbstractSharedContext implements AlterableContext { +abstract class AbstractSharedContext implements InjectableContext { private final ComputingCache, InstanceHandleImpl> instances; @@ -24,6 +27,13 @@ public T get(Contextual contextual) { return get(contextual, null); } + @Override + public Collection> getAll() { + List> all = new ArrayList<>(); + instances.forEachValue(v -> all.add(v)); + return all; + } + @Override public boolean isActive() { return true; @@ -33,15 +43,13 @@ public boolean isActive() { public void destroy(Contextual contextual) { InstanceHandleImpl handle = instances.remove(new Key<>(contextual, null)); if (handle != null) { - handle.destroy(); + handle.destroyInternal(); } } - public void destroy() { - synchronized (this) { - instances.forEachValue(instance -> instance.destroy()); - instances.clear(); - } + public synchronized void destroy() { + instances.forEachValue(instance -> instance.destroyInternal()); + instances.clear(); } @SuppressWarnings({ "unchecked", "rawtypes" }) diff --git a/ext/arc/runtime/src/main/java/org/jboss/protean/arc/ArcContainer.java b/ext/arc/runtime/src/main/java/org/jboss/protean/arc/ArcContainer.java index 367ba9b588715..d5656a7479b54 100644 --- a/ext/arc/runtime/src/main/java/org/jboss/protean/arc/ArcContainer.java +++ b/ext/arc/runtime/src/main/java/org/jboss/protean/arc/ArcContainer.java @@ -26,7 +26,7 @@ public interface ArcContainer { * * @return the context for {@link javax.enterprise.context.RequestScoped} */ - RequestContext requestContext(); + ManagedContext requestContext(); void withinRequest(Runnable action); diff --git a/ext/arc/runtime/src/main/java/org/jboss/protean/arc/ArcContainerImpl.java b/ext/arc/runtime/src/main/java/org/jboss/protean/arc/ArcContainerImpl.java index 8b05956128716..a1607e6d6af20 100644 --- a/ext/arc/runtime/src/main/java/org/jboss/protean/arc/ArcContainerImpl.java +++ b/ext/arc/runtime/src/main/java/org/jboss/protean/arc/ArcContainerImpl.java @@ -15,6 +15,7 @@ import java.util.concurrent.CopyOnWriteArrayList; import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.context.Dependent; import javax.enterprise.context.Initialized; import javax.enterprise.context.RequestScoped; import javax.enterprise.context.spi.Context; @@ -77,17 +78,19 @@ public InstanceHandle instance(TypeLiteral type, Annotation... qualifi } @Override - public RequestContext requestContext() { - return (RequestContext) getContext(RequestScoped.class); + public ManagedContext requestContext() { + return (ManagedContext) getContext(RequestScoped.class); } @Override public void withinRequest(Runnable action) { + ManagedContext requestContext = requestContext(); try { - requestContext().activate(); + requestContext.activate(); action.run(); } finally { - requestContext().deactivate(); + requestContext.destroy(); + requestContext.deactivate(); } } @@ -105,11 +108,14 @@ private InstanceHandle instanceHandle(Type type, Annotation... qualifiers private InstanceHandle instance(InjectableBean bean) { if (bean != null) { - CreationalContextImpl parentContext = new CreationalContextImpl<>(); + CreationalContextImpl parentContext = null; + if (Dependent.class.equals(bean.getScope())) { + parentContext = new CreationalContextImpl<>(); + } + CreationalContextImpl creationalContext = parentContext != null ? parentContext.child() : new CreationalContextImpl<>(); InjectionPoint prev = InjectionPointProvider.CURRENT.get(); InjectionPointProvider.CURRENT.set(CurrentInjectionPointProvider.EMPTY); try { - CreationalContextImpl creationalContext = parentContext.child(); return new InstanceHandleImpl(bean, bean.get(creationalContext), creationalContext, parentContext); } finally { if (prev != null) { @@ -119,7 +125,7 @@ private InstanceHandle instance(InjectableBean bean) { } } } else { - return InstanceHandleImpl.unresolvable(); + return InstanceHandleImpl.unavailable(); } } diff --git a/ext/arc/runtime/src/main/java/org/jboss/protean/arc/CreationalContextImpl.java b/ext/arc/runtime/src/main/java/org/jboss/protean/arc/CreationalContextImpl.java index 3a7fae050b483..c12f0142a596f 100644 --- a/ext/arc/runtime/src/main/java/org/jboss/protean/arc/CreationalContextImpl.java +++ b/ext/arc/runtime/src/main/java/org/jboss/protean/arc/CreationalContextImpl.java @@ -34,7 +34,7 @@ void destroyDependentInstance(Object dependentInstance) { synchronized (dependentInstances) { for (InstanceHandle instanceHandle : dependentInstances) { if (instanceHandle.get() == dependentInstance) { - instanceHandle.release(); + instanceHandle.destroy(); dependentInstances.remove(instanceHandle); break; } @@ -51,7 +51,7 @@ public void push(T incompleteInstance) { public void release() { synchronized (dependentInstances) { for (InstanceHandle instance : dependentInstances) { - instance.release(); + instance.destroy(); } } } diff --git a/ext/arc/runtime/src/main/java/org/jboss/protean/arc/InjectableContext.java b/ext/arc/runtime/src/main/java/org/jboss/protean/arc/InjectableContext.java new file mode 100644 index 0000000000000..e71df32f548e2 --- /dev/null +++ b/ext/arc/runtime/src/main/java/org/jboss/protean/arc/InjectableContext.java @@ -0,0 +1,25 @@ +package org.jboss.protean.arc; + +import java.util.Collection; + +import javax.enterprise.context.spi.AlterableContext; + +/** + * + * @author Martin Kouba + */ +public interface InjectableContext extends AlterableContext { + + /** + * Note that we cannot actually return just a map of contextuals to contextual instances because we need to preserve the + * {@link javax.enterprise.context.spi.CreationalContext} too so that we're able to destroy the dependent objects correctly. + * + * @return all existing contextual instances + */ + Collection> getAll(); + + /** + * Destroy all existing contextual instances. + */ + void destroy(); +} diff --git a/ext/arc/runtime/src/main/java/org/jboss/protean/arc/InstanceHandle.java b/ext/arc/runtime/src/main/java/org/jboss/protean/arc/InstanceHandle.java index 4e56149d7b8f1..77b7bdd213348 100644 --- a/ext/arc/runtime/src/main/java/org/jboss/protean/arc/InstanceHandle.java +++ b/ext/arc/runtime/src/main/java/org/jboss/protean/arc/InstanceHandle.java @@ -17,19 +17,27 @@ public interface InstanceHandle extends AutoCloseable { /** * - * @return an injected instance of {@code T} + * @return an injected instance of {@code T} or {@code null} */ T get(); /** - * Destroys the underlying injected instance. + * Destroys the instance and removes the instance from the underlying context. * - * @see javax.enterprise.context.spi.Contextual#destroy(Object, javax.enterprise.context.spi.CreationalContext) */ - void release(); + void destroy(); + /** + * + * @return the injectable bean + */ + InjectableBean getBean(); + + /** + * Delegates to {@link #destroy()}. + */ default void close() { - release(); + destroy(); } } diff --git a/ext/arc/runtime/src/main/java/org/jboss/protean/arc/InstanceHandleImpl.java b/ext/arc/runtime/src/main/java/org/jboss/protean/arc/InstanceHandleImpl.java index 51299120262e4..37ac0a2f25787 100644 --- a/ext/arc/runtime/src/main/java/org/jboss/protean/arc/InstanceHandleImpl.java +++ b/ext/arc/runtime/src/main/java/org/jboss/protean/arc/InstanceHandleImpl.java @@ -15,11 +15,11 @@ class InstanceHandleImpl implements InstanceHandle { @SuppressWarnings("unchecked") - public static final InstanceHandle unresolvable() { - return (InstanceHandle) UNRESOLVABLE; + public static final InstanceHandle unavailable() { + return (InstanceHandle) UNAVAILABLE; } - static final InstanceHandleImpl UNRESOLVABLE = new InstanceHandleImpl(null, null, null, null); + static final InstanceHandleImpl UNAVAILABLE = new InstanceHandleImpl(null, null, null, null); private final InjectableBean bean; @@ -51,17 +51,22 @@ public T get() { } @Override - public void release() { + public InjectableBean getBean() { + return bean; + } + + @Override + public void destroy() { if (isAvailable()) { if (bean.getScope().equals(ApplicationScoped.class) || bean.getScope().equals(RequestScoped.class) || bean.getScope().equals(Singleton.class)) { ((AlterableContext) Arc.container().getContext(bean.getScope())).destroy(bean); } else { - destroy(); + destroyInternal(); } } } - void destroy() { + public void destroyInternal() { if (parentCreationalContext != null) { parentCreationalContext.release(); } else { @@ -69,4 +74,12 @@ void destroy() { } } + static InstanceHandleImpl unwrap(InstanceHandle handle) { + if (handle instanceof InstanceHandleImpl) { + return (InstanceHandleImpl) handle; + } else { + throw new IllegalArgumentException("Failed to unwrap InstanceHandleImpl: " + handle); + } + } + } diff --git a/ext/arc/runtime/src/main/java/org/jboss/protean/arc/ManagedContext.java b/ext/arc/runtime/src/main/java/org/jboss/protean/arc/ManagedContext.java new file mode 100644 index 0000000000000..28694c132a544 --- /dev/null +++ b/ext/arc/runtime/src/main/java/org/jboss/protean/arc/ManagedContext.java @@ -0,0 +1,38 @@ +package org.jboss.protean.arc; + +import java.util.Collection; + +/** + * + * @author Martin Kouba + */ +public interface ManagedContext extends InjectableContext { + + /** + * Activate the context with no initial state. + */ + default void activate() { + activate(null); + } + + /** + * Activate the context. All instance handles from the initial state must have the same scope as the context, otherwise an {@link IllegalArgumentException} + * is thrown. + * + * @param initialState The initial state, may be {@code null} + */ + void activate(Collection> initialState); + + /** + * Deactivate the context - do not destoy existing contextual instances. + */ + void deactivate(); + + /** + * Destroy and deactivate the context. + */ + default void terminate() { + destroy(); + deactivate(); + } +} diff --git a/ext/arc/runtime/src/main/java/org/jboss/protean/arc/RequestContext.java b/ext/arc/runtime/src/main/java/org/jboss/protean/arc/RequestContext.java index c920ec8c2161f..8a36cf02b7c23 100644 --- a/ext/arc/runtime/src/main/java/org/jboss/protean/arc/RequestContext.java +++ b/ext/arc/runtime/src/main/java/org/jboss/protean/arc/RequestContext.java @@ -1,19 +1,25 @@ package org.jboss.protean.arc; import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import javax.enterprise.context.ContextNotActiveException; import javax.enterprise.context.RequestScoped; -import javax.enterprise.context.spi.AlterableContext; import javax.enterprise.context.spi.Contextual; import javax.enterprise.context.spi.CreationalContext; -public class RequestContext implements AlterableContext { +/** + * + * @author Martin Kouba + */ +class RequestContext implements ManagedContext { // It's a normal scope so there may be no more than one mapped instance per contextual type per thread - private final ThreadLocal, InstanceHandleImpl>> currentContext = new ThreadLocal<>(); + private final ThreadLocal, InstanceHandle>> currentContext = new ThreadLocal<>(); @Override public Class getScope() { @@ -22,11 +28,12 @@ public Class getScope() { @Override public T get(Contextual contextual, CreationalContext creationalContext) { - Map, InstanceHandleImpl> ctx = currentContext.get(); + Map, InstanceHandle> ctx = currentContext.get(); if (ctx == null) { // Thread local not set - context is not active! throw new ContextNotActiveException(); } + @SuppressWarnings("unchecked") InstanceHandleImpl instance = (InstanceHandleImpl) ctx.get(contextual); if (instance == null && creationalContext != null) { // Bean instance does not exist - create one if we have CreationalContext @@ -41,6 +48,15 @@ public T get(Contextual contextual) { return get(contextual, null); } + @Override + public Collection> getAll() { + Map, InstanceHandle> ctx = currentContext.get(); + if (ctx == null) { + return Collections.emptyList(); + } + return new ArrayList<>(ctx.values()); + } + @Override public boolean isActive() { return currentContext.get() != null; @@ -48,40 +64,51 @@ public boolean isActive() { @Override public void destroy(Contextual contextual) { - Map, InstanceHandleImpl> ctx = currentContext.get(); + Map, InstanceHandle> ctx = currentContext.get(); if (ctx == null) { - return; + // Thread local not set - context is not active! + throw new ContextNotActiveException(); } - InstanceHandleImpl instance = ctx.remove(contextual); + InstanceHandle instance = ctx.remove(contextual); if (instance != null) { - instance.destroy(); + InstanceHandleImpl.unwrap(instance).destroyInternal(); } } - public void activate() { - currentContext.set(new HashMap<>()); + @Override + public synchronized void activate(Collection> initialState) { + Map, InstanceHandle> state = new HashMap<>(); + if (initialState != null) { + for (InstanceHandle instanceHandle : initialState) { + if (!instanceHandle.getBean().getScope().equals(getScope())) { + throw new IllegalArgumentException("Invalid bean scope: " + instanceHandle.getBean()); + } + state.put(instanceHandle.getBean(), instanceHandle); + } + } + currentContext.set(state); + } + + @Override + public synchronized void deactivate() { + currentContext.remove(); } - public void invalidate() { - Map, InstanceHandleImpl> ctx = currentContext.get(); + @Override + public void destroy() { + Map, InstanceHandle> ctx = currentContext.get(); if (ctx != null) { synchronized (ctx) { - for (InstanceHandleImpl instance : ctx.values()) { + for (InstanceHandle instance : ctx.values()) { try { - instance.destroy(); + InstanceHandleImpl.unwrap(instance).destroyInternal(); } catch (Exception e) { throw new IllegalStateException("Unable to destroy instance" + instance.get(), e); } } + ctx.clear(); } - ctx.clear(); } } - public void deactivate() { - // TODO maybe change if context propagation is supported - invalidate(); - currentContext.remove(); - } - } diff --git a/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/interceptors/SimpleInterceptorTest.java b/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/interceptors/SimpleInterceptorTest.java index d9d2adfd92580..4d824514dae3d 100644 --- a/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/interceptors/SimpleInterceptorTest.java +++ b/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/interceptors/SimpleInterceptorTest.java @@ -39,11 +39,11 @@ public void testInterception() { assertEquals("oof", simpleBean.bar()); assertEquals(1, counter.get()); assertEquals("foo", LoggingInterceptor.LOG.get()); - + simpleBean.baz(42); assertEquals(2, counter.get()); - handle.release(); + handle.destroy(); assertEquals(1, LifecycleInterceptor.PRE_DESTROYS.size()); assertEquals(simpleBean, LifecycleInterceptor.PRE_DESTROYS.get(0)); } diff --git a/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/requestcontext/RequestContextTest.java b/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/requestcontext/RequestContextTest.java index 14e25e6ba47fb..243e15f03bf20 100644 --- a/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/requestcontext/RequestContextTest.java +++ b/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/requestcontext/RequestContextTest.java @@ -36,7 +36,7 @@ public void testRequestContext() { Controller controller2 = arc.instance(Controller.class).get(); String controller2Id = controller2.getId(); assertEquals(controller1.getId(), controller2Id); - arc.requestContext().deactivate(); + arc.requestContext().terminate(); assertTrue(Controller.DESTROYED.get()); try { diff --git a/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/requestcontext/propagation/RequestContextPropagationTest.java b/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/requestcontext/propagation/RequestContextPropagationTest.java new file mode 100644 index 0000000000000..433a2fb15709b --- /dev/null +++ b/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/requestcontext/propagation/RequestContextPropagationTest.java @@ -0,0 +1,70 @@ +package org.jboss.protean.arc.test.requestcontext.propagation; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.Collection; + +import javax.enterprise.context.ContextNotActiveException; + +import org.jboss.protean.arc.Arc; +import org.jboss.protean.arc.ArcContainer; +import org.jboss.protean.arc.InstanceHandle; +import org.jboss.protean.arc.ManagedContext; +import org.jboss.protean.arc.test.ArcTestContainer; +import org.junit.Rule; +import org.junit.Test; + +public class RequestContextPropagationTest { + + @Rule + public ArcTestContainer container = new ArcTestContainer(SuperController.class, SuperButton.class); + + @Test + public void testPropagation() { + + ArcContainer arc = Arc.container(); + ManagedContext requestContext = arc.requestContext(); + + try { + arc.instance(SuperController.class).get().getId(); + fail(); + } catch (ContextNotActiveException expected) { + } + + requestContext.activate(); + assertFalse(SuperController.DESTROYED.get()); + SuperController controller1 = arc.instance(SuperController.class).get(); + SuperController controller2 = arc.instance(SuperController.class).get(); + String controller2Id = controller2.getId(); + assertEquals(controller1.getId(), controller2Id); + assertNotNull(controller2.getButton()); + assertTrue(controller2.getButton() == controller1.getButton()); + + // Store existing instances + Collection> instances = requestContext.getAll(); + // Deactivate but don't destroy + requestContext.deactivate(); + + assertFalse(SuperController.DESTROYED.get()); + assertFalse(SuperButton.DESTROYED.get()); + + try { + // Proxy should not work + controller1.getId(); + fail(); + } catch (ContextNotActiveException expected) { + } + + requestContext.activate(instances); + assertEquals(arc.instance(SuperController.class).get().getId(), controller2Id); + + requestContext.terminate(); + assertTrue(SuperController.DESTROYED.get()); + assertTrue(SuperButton.DESTROYED.get()); + } + +} diff --git a/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/requestcontext/propagation/SuperButton.java b/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/requestcontext/propagation/SuperButton.java new file mode 100644 index 0000000000000..e3aeaf3cca7b9 --- /dev/null +++ b/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/requestcontext/propagation/SuperButton.java @@ -0,0 +1,18 @@ +package org.jboss.protean.arc.test.requestcontext.propagation; + +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.annotation.PreDestroy; +import javax.enterprise.context.Dependent; + +@Dependent +public class SuperButton { + + static final AtomicBoolean DESTROYED = new AtomicBoolean(); + + @PreDestroy + void destroy() { + DESTROYED.set(true); + } + +} diff --git a/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/requestcontext/propagation/SuperController.java b/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/requestcontext/propagation/SuperController.java new file mode 100644 index 0000000000000..742366f78b3f7 --- /dev/null +++ b/ext/arc/tests/src/test/java/org/jboss/protean/arc/test/requestcontext/propagation/SuperController.java @@ -0,0 +1,39 @@ +package org.jboss.protean.arc.test.requestcontext.propagation; + +import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; + +@RequestScoped +public class SuperController { + + static final AtomicBoolean DESTROYED = new AtomicBoolean(); + + private String id; + + @Inject + SuperButton button; + + @PostConstruct + void init() { + id = UUID.randomUUID().toString(); + } + + @PreDestroy + void destroy() { + DESTROYED.set(true); + } + + String getId() { + return id; + } + + SuperButton getButton() { + return button; + } + +}