Search code examples
javacdiweld

Inner dependency is not being injected (Weld + JavaSE)


I am in the process of refactoring an old module, by adding CDI.

I end with

public interface ApiFactory {
  ...
}

public class ApiFactorySp
    implements ApiFactory {
  @Inject
  UrlProducer urlProducer;  // <-- Does not get injected
  ...
}

and

public interface UrlProducer {
    public String getUrl();
}

@Alternative
public class UrlProducerTest
    implements UrlProducer {

    @Override
    public String getUrl() {
        return "https://myTestEnv.mydomain/myWebApp";
    }
 }

For testing, I create a beans.xml file in META-INF:

<beans
  xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
    http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
  bean-discovery-mode="all">
    <alternatives>
      <class>myOrg.myProject.myPackage.UrlProducerTest</class>
    </alternatives>
</beans>

To test it, I am doing like shown in this blog

public class WeldContext {

    public static final WeldContext INSTANCE = new WeldContext();

    private final Weld weld;
    private final WeldContainer container;

    private WeldContext() {
        this.weld = new Weld();
        this.container = weld.initialize();
        Runtime.getRuntime().addShutdownHook(new Thread() {

            @Override
            public void run() {
                weld.shutdown();
            }
        });
    }

    public <T> T getBean(Class<T> type) {
        return container.instance().select(type).get();
    }
}

and

public class WeldJUnit4Runner extends BlockJUnit4ClassRunner {

    public WeldJUnit4Runner(Class<Object> clazz) throws InitializationError {
        super(clazz);
    }

    @Override
    protected Object createTest() {
        final Class<?> test = getTestClass().getJavaClass();
        return WeldContext.INSTANCE.getBean(test);
    }
}

Now, when I try to test the logic, I do

@RunWith(WeldJUnit4Runner.class)
public class MyTest {
     @Inject
     UrlProducer urlProducer;         

     @Inject
     ApiFactory apiFactory;

     @Test
     public void test() {
         apiFactory.doSomethingThatRequiresUrlProducer();
     }
 }

When I run this, both of the test attributes are inject, but I get NPE because the urlProducer attribute inside of the apiFactory instance has not been assigned a value.

Why is Weld not recognizing the @Inject attribute inside ApiFactory?

JDK 7, Weld 2.2.10, Junit 4.12

UPDATE: After posting the question, started trying with a simpler, brand new project (with just two interfaces and three classes). Using Weld "standalone" did not solve the issue, using CDI-Unit did solve it.

Then I modified my original project to use CDI-Unit, but it did not improve anything. After that I change the injection of UrlProducerTest in ApiFactory from field to constructor (i.e., defining the @Inject ApiFactory(UrlProducer urlProducer) constructor) solved it. I still have not tried this solution with "standalone" Weld (that is for tomorrow), but nonetheless I am still interested in know why field injection is not working.


Solution

  • If UrlProducerTest is an alternative and you want to inject this bean this class should be added to beans.xml into <alternatives> tag.

    EDIT:

    I believe if some bean can't be injected you get exception with 'unsatisfied/ambiguous dependencies' message. Null could be injected if you used CDI producer method that returned null but this is not your scenario.

    So if there are no errors in console I have two assumptions:

    1. Injection doesn't work at all and you get NPE because apiFactory is null

    2. You use urlProducer before injection. For example, from constructor or initialization block (apiFactory.doSomethingThatRequiresUrlProducer() is not provided). So move this logic to some method and annotate it by @PostConstruct