Uploaded image for project: 'Weld'
  1. Weld
  2. WELD-2745

Change to getReference may impact custom beans and break dependent object destruction

    XMLWordPrintable

Details

    • Bug
    • Resolution: Done
    • Major
    • 5.1.1.SP1
    • 5.1.1.Final
    • None
    • None

    Description

      (Disclaimer: I don't understand the nuances that led to https://github.com/weld/core/pull/2836.)

      I was trying to fully understand Weld's behavior in regards to CreationalContext and dependent objects. I blundered into a discrepancy between Weld 5.1.0.Final and Weld 5.1.1.Final. Maybe it is deliberate. Maybe it is not.

      While trying to understand all this, I discovered that in Weld 5.1.1.Final in certain cases dependent objects are not destroyed. Perhaps I am doing something wrong, but perhaps not.

      Briefly, a @Dependent-scoped bean "belonging to" a @Singleton-scoped bean is not destroyed when the containing bean is destroyed in certain (common?) circumstances in Weld 5.1.1.Final, where it is destroyed in those circumstances in Weld 5.1.0.Final. The behavior may be correct, or it may not be.

      Test code (JUnit Jupiter) follows; it works (if I've excerpted it right) under Weld 5.1.0.Final and fails under Weld 5.1.1.Final.

      My apologies if I've wasted your time.

      import jakarta.enterprise.context.Dependent;
      import jakarta.enterprise.context.spi.AlterableContext;
      import jakarta.enterprise.context.spi.CreationalContext;
      import jakarta.enterprise.event.Observes;
      import jakarta.enterprise.inject.se.SeContainer;
      import jakarta.enterprise.inject.se.SeContainerInitializer;
      import jakarta.enterprise.inject.spi.AfterBeanDiscovery;
      import jakarta.enterprise.inject.spi.Bean;
      import jakarta.enterprise.inject.spi.BeanManager;
      import jakarta.inject.Singleton;
      import org.jboss.weld.manager.BeanManagerImpl;
      import org.junit.jupiter.api.AfterEach;
      import org.junit.jupiter.api.BeforeEach;
      import org.junit.jupiter.api.Test;
      import static org.junit.jupiter.api.Assertions.assertFalse;
      import static org.junit.jupiter.api.Assertions.assertSame;
      import static org.junit.jupiter.api.Assertions.assertTrue;
      
      final class TestDependentObjects {
      
        private SeContainer container;
      
        private boolean parentCreated;
      
        private CreationalContext<?> parentCc;
      
        private boolean parentDestroyed;
      
        private boolean childCreated;
      
        private boolean childDestroyed;
      
        private TestDependentObjects() {
          super();
        }
      
        @BeforeEach
        final void setup() {
          this.container = SeContainerInitializer.newInstance()
            .disableDiscovery()
            .addBeanClasses(Dummy.class) // sadly needed even though not used
            .addExtensions(new Extension())
            .initialize();
        }
      
        @AfterEach
        final void teardown() {
          this.container.close();
          this.parentCreated = false;
          this.parentCc = null;
          this.parentDestroyed = false;
          this.childCreated = false;
          this.childDestroyed = false;
        }
      
        @Test
        final void testDependentObjectHandlingSimplified() {
          final BeanManager bm = this.container.getBeanManager();
          @SuppressWarnings("unchecked")
          final Bean<Parent> pb = (Bean<Parent>)bm.resolve(bm.getBeans(Parent.class));
          final CreationalContext<Parent> pcc = bm.createCreationalContext(pb);
          final Parent p = (Parent)bm.getReference(pb, Parent.class, pcc);
          assertTrue(this.parentCreated);
          // assertSame(gpcc, this.parentCc); // only true with Weld 5.1.1.Final
          assertTrue(this.childCreated);
          final AlterableContext singletonContext = (AlterableContext)bm.getContext(Singleton.class);
          singletonContext.destroy(pb);
          assertTrue(this.parentDestroyed);
          assertTrue(this.childDestroyed); // fails under Weld 5.1.1.Final
        }
      
        private final Child createChild(final BeanManager bm, final CreationalContext<Child> cc) {
          final Child c = new Child();
          this.childCreated = true;
          return c;
        }
      
        private final void destroyChild(final Child child, final BeanManager bm, final CreationalContext<Child> cc) {
          this.childDestroyed = true;
          cc.release();
        }
      
        private final Parent createParent(final BeanManager bm, final CreationalContext<Parent> cc) {
          final Parent p = new Parent((Child)bm.getReference(bm.resolve(bm.getBeans(Child.class)), Child.class, cc));
          this.parentCreated = true;
          this.parentCc = cc;
          return p;
        }
      
        private final void destroyParent(final Parent parent, final BeanManager bm, final CreationalContext<Parent> cc) {
          this.parentDestroyed = true;
          cc.release();
        }
      
        private class Extension implements jakarta.enterprise.inject.spi.Extension {
      
          Extension() {
            super();
          }
      
          final void addBeans(@Observes final AfterBeanDiscovery event, final BeanManager bm) {
            event.addBean()
              .addTransitiveTypeClosure(Child.class)
              .scope(Dependent.class)
              .createWith((CreationalContext<Child> cc) -> createChild(bm, cc))
              .destroyWith((child, cc) -> destroyChild(child, bm, cc));
            event.addBean()
              .addTransitiveTypeClosure(Parent.class)
              .scope(Singleton.class)
              .createWith((CreationalContext<Parent> cc) -> createParent(bm, cc))
              .destroyWith((parent, cc) -> destroyParent(parent, bm, cc));
          }
      
        }
      
        private static class Parent {
      
          private final Child child;
      
          Parent(final Child child) {
            super();
            this.child = child;
          }
      
        }
      
        private static class Child {
      
          Child() {
            super();
          }
      
        }
      
        private static class Dummy {}
      
      }
      

      StackOverflow cross reference - https://stackoverflow.com/questions/76576756/what-is-the-mental-model-i-should-have-for-creationalcontexts

      Attachments

        Activity

          People

            Unassigned Unassigned
            ljnelson+github@gmail.com Laird Nelson (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: