2

I have a Jersey resource with a facade object injected. This is configured in my ResourceConfig and the facade gets injected fine. The facade contains a DAO class which also should be injected and is configured in the same ResourceConfig. Now to my problem; the DAO class is null. Thus, not injected.

@ApplicationPath("/service")
public class SystemSetup extends ResourceConfig {

public SystemSetup() {
    packages(false, "com.foo.bar");
    packages("org.glassfish.jersey.jackson");
    register(JacksonFeature.class);

    final LockManager manager = getLockManager();
    final SessionFactory sessionFactory = getSessionFactory();
    register(new AbstractBinder() {
        @Override
        protected void configure() {
            bindFactory(InjectFactory.getDaoFactory(sessionFactory)).to(Dao.class).in(Singleton.class);
            bindFactory(InjectFactory.getFacadeFactory(manager)).to(Facade.class).in(Singleton.class);
        }
    });
}

 @Path("/")
 @Produces("text/json")
 public class ViewResource {

    @Inject
    private Facade logic;

public class Facade {

    @Inject
    private Dao dao; //Not injected

The factory instances are rather simple. They simply call the constructor and pass the argument to it.

The strange thing is that this worked absolut fine when I used bind(Class object) rather than bindFactory.

EDIT

Factories

class InjectFactory {

    static Factory<Dao> getDaoFactory() {
        return new Factory<Dao>() {
            @Override
            public Dao provide() {
                return new Dao(new Object());
            }

            @Override
            public void dispose(Dao dao) {}
        };
    }

    static Factory<Facade> getFacadeFactory() {
        return new Factory<Facade>() {

            @Override
            public Facade provide() {
                return new Facade();
            }

            @Override
            public void dispose(Facade facade) {}
        };
    }
}
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • 1
    Can you post all the code necessary to reproduce the problem? – Paul Samsotha Jan 05 '16 at 00:33
  • Hi! I created a very basic and small project which reproduces this problem. Please download it at speedy.sh/qbsnJ/Dummy-Project.zip and run it with "mvn clean install tomcat7:run-war" and navigate to localhost:9090/dummy/service/test in a browser. – Roy Adamsson Jan 05 '16 at 18:50
  • Basically, all you need to reproduce is an object with an inject field(A). Object A should contain an other inject field(B). Both A and B are registered as bindFactory(Factory instance). – Roy Adamsson Jan 07 '16 at 10:47

1 Answers1

1

As is the case with most Di frameworks, when you start instantiating things yourself, it's often the case that you are kicking the framework out of the equation. This holds true for the Factory instances, as well as the objects the factory creates. So the Facade instance never gets touch by the framework, except to inject it into the resource class.

You can can a hold of the ServiceLocator, and explicitly inject objects yourself if you want to create them yourself. Here are a couple options.

1) Inject the ServiceLocator into the Factory instance, then inject the Facade instance.

static Factory<Facade> getFacadeFactory() {
    return new Factory<Facade>() {

        @Context
        ServiceLocator locator;

        @Override
        public Facade provide() {
            Facade facade = new Facade();
            locator.inject(facade);
            return facade;
        }

        @Override
        public void dispose(Facade facade) {}
    };
}

@Inject
public SystemSetup(ServiceLocator locator) {
    packages("foo.bar.rest");
    packages("org.glassfish.jersey.jackson");
    register(JacksonFeature.class);

    register(new AbstractBinder() {
        @Override
        protected void configure() {
            bindFactory(InjectFactory.getDaoFactory()).to(Dao.class);

            Factory<Facade> factory = InjectFactory.getFacadeFactory();
            locator.inject(factory);
            bindFactory(factory).to(Facade.class);
        }
    });
}

2) Or bind a Factory class, and let the framework inject the ServiceLocator

public static class FacadeFactory implements Factory<Facade> {

    @Context
    ServiceLocator locator;

    @Override
    public Facade provide() {
        Facade facade = new Facade();
        locator.inject(facade);
        return facade;
    }

    @Override
    public void dispose(Facade facade) {}
}

register(new AbstractBinder() {
    @Override
    protected void configure() {
        bindFactory(InjectFactory.getDaoFactory()).to(Dao.class);
        bindFactory(InjectFactory.FacadeFactory.class).to(Facade.class);
    }
});
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • Thanks for the help. It makes a lot of sense when you explain it. I will read more about ServiceLocator. Got it to work btw by following the first step, with a slight change. I removed the Inject annotation on the SystemSetup constructor(since it didn't work) and added @Context to the parameter instead. – Roy Adamsson Jan 07 '16 at 20:25
  • If you've ever worked with Spring, the `ServiceLocator` is analogous to Spring's `ApplicationContext`. It is the main IoC container. – Paul Samsotha Jan 08 '16 at 00:56
  • This helped solve my issue. I could inject a Service into a resource class with `@Inject`, but when I used `@Inject` from within the service itself, I always got a `NullPointerException`. The issue was that I was creating the Service myself with a Factory, not realizing that no injection would occur from that point on. Spent many hours trying to understand this. Thanks! – Ethan Hohensee Dec 09 '19 at 21:38