Repository: weld/weld-junit (java)

Issue:
Activation of Scope should fire @Initialized event
Both org.jboss.weld.junit.WeldCDIExtension#activateContexts  and 
org.jboss.weld.junit.WeldCDIExtension#deactivateContexts
currently don't not fire any Events.

It would be nice to have them fire `@Initialized` and `@Destroyed` events as they would in an application server environment.



Pull request:
Fire lifecycle events for activated contexts
- fire @Initialized and @Destroyed, payload is java.lang.Object
- we don't fire @BeforeDestroyed as we're still compatible with CDI 1.2
- resolves #50

Patch:
diff --git a/junit-common/src/main/java/org/jboss/weld/junit/AbstractWeldInitiator.java b/junit-common/src/main/java/org/jboss/weld/junit/AbstractWeldInitiator.java
index 47ff5b8..cea050a 100644
--- a/junit-common/src/main/java/org/jboss/weld/junit/AbstractWeldInitiator.java
+++ b/junit-common/src/main/java/org/jboss/weld/junit/AbstractWeldInitiator.java
@@ -316,7 +316,8 @@ public abstract class AbstractWeldInitiator implements Instance<Object>, Contain
         }
 
         /**
-         * Activate and deactivate contexts for the given normal scopes for each test method execution.
+         * Activate and deactivate contexts for the given normal scopes for the lifetime of the initialized Weld container, by default for each test method
+         * execution.
          * <p>
          * {@link ApplicationScoped} is ignored as it is always active.
          * </p>
diff --git a/junit-common/src/main/java/org/jboss/weld/junit/ContextImpl.java b/junit-common/src/main/java/org/jboss/weld/junit/ContextImpl.java
index 94dfb9d..229d98a 100644
--- a/junit-common/src/main/java/org/jboss/weld/junit/ContextImpl.java
+++ b/junit-common/src/main/java/org/jboss/weld/junit/ContextImpl.java
@@ -19,11 +19,13 @@ package org.jboss.weld.junit;
 import java.lang.annotation.Annotation;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.logging.Logger;
 
 import javax.enterprise.context.ContextNotActiveException;
 import javax.enterprise.context.spi.Context;
 import javax.enterprise.context.spi.Contextual;
 import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.inject.spi.BeanManager;
 
 /**
  *
@@ -31,13 +33,18 @@ import javax.enterprise.context.spi.CreationalContext;
  */
 class ContextImpl implements Context {
 
+    private static final Logger LOGGER = Logger.getLogger(ContextImpl.class.getName());
+
     private final Class<? extends Annotation> scope;
 
+    private final BeanManager beanManager;
+
     // It's a normal scope so there may be no more than one mapped instance per contextual type per thread
     private final ThreadLocal<Map<Contextual<?>, ContextualInstance<?>>> currentContext = new ThreadLocal<>();
 
-    public ContextImpl(Class<? extends Annotation> scope) {
+    ContextImpl(Class<? extends Annotation> scope, BeanManager beanManager) {
         this.scope = scope;
+        this.beanManager = beanManager;
     }
 
     public Class<? extends Annotation> getScope() {
@@ -81,6 +88,7 @@ class ContextImpl implements Context {
 
     public void activate() {
         currentContext.set(new HashMap<Contextual<?>, ContextualInstance<?>>());
+        beanManager.fireEvent(new Object(), InitializedLiteral.of(scope));
     }
 
     public void deactivate() {
@@ -92,11 +100,12 @@ class ContextImpl implements Context {
             try {
                 instance.destroy();
             } catch (Exception e) {
-                // LOGGER.warning("Unable to destroy instance" + instance.get() + " for bean: " + instance.getContextual());
+                LOGGER.warning("Unable to destroy instance" + instance.get() + " for bean: " + instance.getContextual());
             }
         }
         ctx.clear();
         currentContext.remove();
+        beanManager.fireEvent(new Object(), DestroyedLiteral.of(scope));
     }
 
     /**
diff --git a/junit-common/src/main/java/org/jboss/weld/junit/DestroyedLiteral.java b/junit-common/src/main/java/org/jboss/weld/junit/DestroyedLiteral.java
new file mode 100644
index 0000000..a3fddc7
--- /dev/null
+++ b/junit-common/src/main/java/org/jboss/weld/junit/DestroyedLiteral.java
@@ -0,0 +1,46 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2018, Red Hat, Inc., and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * 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 org.jboss.weld.junit;
+
+import java.lang.annotation.Annotation;
+
+import javax.enterprise.context.Destroyed;
+import javax.enterprise.util.AnnotationLiteral;
+
+/**
+ * Supports inline instantiation of the {@link Destroyed} qualifier.
+ *
+ * @author Martin Kouba
+ */
+class DestroyedLiteral extends AnnotationLiteral<Destroyed> implements Destroyed {
+
+    private static final long serialVersionUID = 1L;
+
+    private final Class<? extends Annotation> value;
+
+    public static DestroyedLiteral of(Class<? extends Annotation> value) {
+        return new DestroyedLiteral(value);
+    }
+
+    private DestroyedLiteral(Class<? extends Annotation> value) {
+        this.value = value;
+    }
+
+    public Class<? extends Annotation> value() {
+        return value;
+    }
+}
diff --git a/junit-common/src/main/java/org/jboss/weld/junit/InitializedLiteral.java b/junit-common/src/main/java/org/jboss/weld/junit/InitializedLiteral.java
new file mode 100644
index 0000000..7e9e7b5
--- /dev/null
+++ b/junit-common/src/main/java/org/jboss/weld/junit/InitializedLiteral.java
@@ -0,0 +1,46 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2018, Red Hat, Inc., and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * 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 org.jboss.weld.junit;
+
+import java.lang.annotation.Annotation;
+
+import javax.enterprise.context.Initialized;
+import javax.enterprise.util.AnnotationLiteral;
+
+/**
+ * Supports inline instantiation of the {@link Initialized} qualifier.
+ *
+ * @author Martin Kouba
+ */
+class InitializedLiteral extends AnnotationLiteral<Initialized> implements Initialized {
+
+    private static final long serialVersionUID = 1L;
+
+    private final Class<? extends Annotation> value;
+
+    public static InitializedLiteral of(Class<? extends Annotation> value) {
+        return new InitializedLiteral(value);
+    }
+
+    private InitializedLiteral(Class<? extends Annotation> value) {
+        this.value = value;
+    }
+
+    public Class<? extends Annotation> value() {
+        return value;
+    }
+}
diff --git a/junit-common/src/main/java/org/jboss/weld/junit/WeldCDIExtension.java b/junit-common/src/main/java/org/jboss/weld/junit/WeldCDIExtension.java
index 3ba7a59..f4b1ba6 100644
--- a/junit-common/src/main/java/org/jboss/weld/junit/WeldCDIExtension.java
+++ b/junit-common/src/main/java/org/jboss/weld/junit/WeldCDIExtension.java
@@ -24,6 +24,7 @@ import java.util.Set;
 import javax.enterprise.event.Observes;
 import javax.enterprise.inject.spi.AfterBeanDiscovery;
 import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.inject.spi.BeanManager;
 import javax.enterprise.inject.spi.Extension;
 
 /**
@@ -45,10 +46,10 @@ class WeldCDIExtension implements Extension {
         this.contexts = new ArrayList<>();
     }
 
-    void afterBeandiscovery(@Observes AfterBeanDiscovery event) {
+    void afterBeandiscovery(@Observes AfterBeanDiscovery event, BeanManager beanManager) {
         if (scopesToActivate != null) {
             for (Class<? extends Annotation> scope : scopesToActivate) {
-                ContextImpl ctx = new ContextImpl(scope);
+                ContextImpl ctx = new ContextImpl(scope, beanManager);
                 contexts.add(ctx);
                 event.addContext(ctx);
             }
diff --git a/junit4/src/main/java/org/jboss/weld/junit4/WeldInitiator.java b/junit4/src/main/java/org/jboss/weld/junit4/WeldInitiator.java
index 38f5ec2..5c0bb7f 100644
--- a/junit4/src/main/java/org/jboss/weld/junit4/WeldInitiator.java
+++ b/junit4/src/main/java/org/jboss/weld/junit4/WeldInitiator.java
@@ -37,6 +37,8 @@ import org.junit.runners.model.Statement;
  * JUnit 4 initiator - test rule which starts a Weld container per each test method execution:
  *
  * <pre>
+ * import org.junit.Rule;
+ *
  * public class SimpleTest {
  *
  *     &#64;Rule
@@ -52,6 +54,32 @@ import org.junit.runners.model.Statement;
  * </pre>
  *
  * <p>
+ * Alternatively, the container can be shared accross all test methods:
+ * </p>
+ *
+ * <pre>
+ * import org.junit.ClassRule;
+ *
+ * public class ClassRuleTest {
+ *
+ *     &#64;ClassRule
+ *     public WeldInitiator weld = WeldInitiator.of(Foo.class);
+ *
+ *     &#64;Test
+ *     public void test1() {
+ *         // Weld container is started automatically
+ *         // WeldInitiator can be used to perform programmatic lookup of beans
+ *         assertEquals("baz", weld.select(Foo.class).get().getBaz());
+ *     }
+ *
+ *     &#64;Test
+ *     public void test2() {
+ *         // This test method is using the same Weld container as test1()
+ *     }
+ * }
+ * </pre>
+ *
+ * <p>
  * {@link WeldInitiator} implements {@link Instance} and therefore might be used to perform programmatic lookup of bean
  * instances.
  * </p>


Test patch:
diff --git a/junit4/src/test/java/org/jboss/weld/junit4/contexts/events/ContextLifecycleEventsObserver.java b/junit4/src/test/java/org/jboss/weld/junit4/contexts/events/ContextLifecycleEventsObserver.java
new file mode 100644
index 0000000..79a8687
--- /dev/null
+++ b/junit4/src/test/java/org/jboss/weld/junit4/contexts/events/ContextLifecycleEventsObserver.java
@@ -0,0 +1,50 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2018, Red Hat, Inc., and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * 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 org.jboss.weld.junit4.contexts.events;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.context.Destroyed;
+import javax.enterprise.context.Initialized;
+import javax.enterprise.context.RequestScoped;
+import javax.enterprise.context.SessionScoped;
+import javax.enterprise.event.Observes;
+
+@ApplicationScoped
+public class ContextLifecycleEventsObserver {
+
+    static final List<String> EVENTS = new CopyOnWriteArrayList<>();
+
+    void onRequestContextInit(@Observes @Initialized(RequestScoped.class) Object event) {
+        EVENTS.add(Initialized.class.getName() + RequestScoped.class.getName());
+    }
+
+    void onRequestContextDestroy(@Observes @Destroyed(RequestScoped.class) Object event) {
+        EVENTS.add(Destroyed.class.getName() + RequestScoped.class.getName());
+    }
+
+    void onSessionContextInit(@Observes @Initialized(SessionScoped.class) Object event) {
+        EVENTS.add(Initialized.class.getName() + SessionScoped.class.getName());
+    }
+
+    void onSessionContextDestroy(@Observes @Destroyed(SessionScoped.class) Object event) {
+        EVENTS.add(Destroyed.class.getName() + SessionScoped.class.getName());
+    }
+
+}
diff --git a/junit4/src/test/java/org/jboss/weld/junit4/contexts/events/ContextLifecycleEventsTest.java b/junit4/src/test/java/org/jboss/weld/junit4/contexts/events/ContextLifecycleEventsTest.java
new file mode 100644
index 0000000..63f5110
--- /dev/null
+++ b/junit4/src/test/java/org/jboss/weld/junit4/contexts/events/ContextLifecycleEventsTest.java
@@ -0,0 +1,54 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2017, Red Hat, Inc., and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * 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 org.jboss.weld.junit4.contexts.events;
+
+import static org.junit.Assert.assertTrue;
+
+import javax.enterprise.context.Destroyed;
+import javax.enterprise.context.Initialized;
+import javax.enterprise.context.RequestScoped;
+import javax.enterprise.context.SessionScoped;
+
+import org.jboss.weld.junit4.WeldInitiator;
+import org.junit.AfterClass;
+import org.junit.Rule;
+import org.junit.Test;
+
+/**
+ *
+ * @author Martin Kouba
+ */
+public class ContextLifecycleEventsTest {
+
+    @Rule
+    public WeldInitiator initiator = WeldInitiator.from(ContextLifecycleEventsObserver.class).activate(RequestScoped.class, SessionScoped.class).build();
+
+    @Test
+    public void testInitFired() {
+        // At this time @Initialized should be fired
+        assertTrue(ContextLifecycleEventsObserver.EVENTS.contains(Initialized.class.getName() + RequestScoped.class.getName()));
+        assertTrue(ContextLifecycleEventsObserver.EVENTS.contains(Initialized.class.getName() + SessionScoped.class.getName()));
+    }
+
+    @AfterClass
+    public static void afterAll() {
+        // At this time @Destroyed from the previous test method should be fired
+        assertTrue(ContextLifecycleEventsObserver.EVENTS.contains(Destroyed.class.getName() + RequestScoped.class.getName()));
+        assertTrue(ContextLifecycleEventsObserver.EVENTS.contains(Destroyed.class.getName() + SessionScoped.class.getName()));
+    }
+
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/contexts/events/ContextLifecycleEventsObserver.java b/junit5/src/test/java/org/jboss/weld/junit5/contexts/events/ContextLifecycleEventsObserver.java
new file mode 100644
index 0000000..aad63f7
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/contexts/events/ContextLifecycleEventsObserver.java
@@ -0,0 +1,50 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2018, Red Hat, Inc., and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * 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 org.jboss.weld.junit5.contexts.events;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.context.Destroyed;
+import javax.enterprise.context.Initialized;
+import javax.enterprise.context.RequestScoped;
+import javax.enterprise.context.SessionScoped;
+import javax.enterprise.event.Observes;
+
+@ApplicationScoped
+public class ContextLifecycleEventsObserver {
+
+    static final List<String> EVENTS = new CopyOnWriteArrayList<>();
+
+    void onRequestContextInit(@Observes @Initialized(RequestScoped.class) Object event) {
+        EVENTS.add(Initialized.class.getName() + RequestScoped.class.getName());
+    }
+
+    void onRequestContextDestroy(@Observes @Destroyed(RequestScoped.class) Object event) {
+        EVENTS.add(Destroyed.class.getName() + RequestScoped.class.getName());
+    }
+
+    void onSessionContextInit(@Observes @Initialized(SessionScoped.class) Object event) {
+        EVENTS.add(Initialized.class.getName() + SessionScoped.class.getName());
+    }
+
+    void onSessionContextDestroy(@Observes @Destroyed(SessionScoped.class) Object event) {
+        EVENTS.add(Destroyed.class.getName() + SessionScoped.class.getName());
+    }
+
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/contexts/events/ContextLifecycleEventsTest.java b/junit5/src/test/java/org/jboss/weld/junit5/contexts/events/ContextLifecycleEventsTest.java
new file mode 100644
index 0000000..9a5e7db
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/contexts/events/ContextLifecycleEventsTest.java
@@ -0,0 +1,56 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2018, Red Hat, Inc., and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * 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 org.jboss.weld.junit5.contexts.events;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import javax.enterprise.context.Destroyed;
+import javax.enterprise.context.Initialized;
+import javax.enterprise.context.RequestScoped;
+import javax.enterprise.context.SessionScoped;
+
+import org.jboss.weld.junit5.EnableWeld;
+import org.jboss.weld.junit5.WeldInitiator;
+import org.jboss.weld.junit5.WeldSetup;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Test;
+
+/**
+ *
+ * @author Martin Kouba
+ */
+@EnableWeld
+public class ContextLifecycleEventsTest {
+
+    @WeldSetup
+    public WeldInitiator weld = WeldInitiator.from(ContextLifecycleEventsObserver.class).activate(RequestScoped.class, SessionScoped.class).build();
+
+    @Test
+    public void testInitFired() {
+        // At this time @Initialized should be fired
+        assertTrue(ContextLifecycleEventsObserver.EVENTS.contains(Initialized.class.getName() + RequestScoped.class.getName()));
+        assertTrue(ContextLifecycleEventsObserver.EVENTS.contains(Initialized.class.getName() + SessionScoped.class.getName()));
+    }
+
+    @AfterAll
+    public static void afterAll() {
+        // At this time @Destroyed from the previous test method should be fired
+        assertTrue(ContextLifecycleEventsObserver.EVENTS.contains(Destroyed.class.getName() + RequestScoped.class.getName()));
+        assertTrue(ContextLifecycleEventsObserver.EVENTS.contains(Destroyed.class.getName() + SessionScoped.class.getName()));
+    }
+
+}