4

I'm trying to initialize some components in my Jersey application in the Application constructor (the thing that inherits from ResourceConfig) . It looks like this

public Application(@Context ServletContext context,
                   @Context ServiceLocator locator)...

When I try to use the locator at any point, I still can't create instances of things that I have registered in an AbstractBinder using the locator.create(MyThing.class) method.

I'm certain that they are bound correctly because they are injected properly into my resource classes via the @inject field annotation.

The difference is that the Jersey/HK2 framework is instantiating my resource classes (as expected, since they're in my package scan path), but I can not seem to leverage the ServiceLocator through code.

My ultimate goal is to have other non-jersey classes injected when they have the @Inject attribute, eg. I have a worker class that needs to be injected with the configured database access layer. I want to say

locator.Create(AWorker.class) 

and have it injected.

How do I get the real ServiceLocator that will inject everything I've already registered/bound with my Binder? (Or should I be using something other than ServiceLocator?)

Bill
  • 1,563
  • 1
  • 15
  • 28
  • I would argue that you cannot make full use of hk2 within `Application`. The application provides information on how to set up Jersey. It should also inform about your Jersey related hk2 binder. As long as this binding did not take place it is not available via the service locator. – Rafael Winterhalter Jan 15 '14 at 22:12
  • I'm registering my binder before I try to access the locator. Still not sure what I'm supposed to do =D – Bill Jan 15 '14 at 22:19
  • Are you sure that the binder is not only registered but also binds its instances? – Rafael Winterhalter Jan 15 '14 at 22:21
  • I don't understand the question, but my binder does bind(Concrete.Class).to(Interface.class) in the configure() method – Bill Jan 15 '14 at 23:16
  • Using locator.create does not need the thing being created to have been registered with HK2. It will attempt to go ahead and create an instance of the given class. Further, MyThing.class will NOT be managed by HK2, and so would NOT be available for further injection into other things. If the class "MyThing.class" uses constructor injection then the services it needs for the constructor will need to be found in the ServiceLocator of course. Perhaps you should post the exception you are getting from locator.create(MyThing.class) and possibly the relevant parts of the code for MyThing.class – jwells131313 Jan 16 '14 at 12:04

2 Answers2

4

How are you starting up your container? If you are using ApplicationHandler, you can just call:handler.getServiceLocator(). The ServiceLocator is, indeed, what you want to be using to access your dependencies.

If you are starting up a servlet, I found that the best way to get access to the service locator was to have a Jersey feature set it on my startup class:

    private static final class LocatorSetFeature implements Feature {

    private final ServiceLocator scopedLocator;

    @Inject
    private LocatorSetFeature(ServiceLocator scopedLocator) {
        this.scopedLocator = scopedLocator;
    }

    @Override
    public boolean configure(FeatureContext context) {
        locator = this.scopedLocator; // this would set our member locator variable
        return true;
    }
}

The feature would just be registered with our resource config with config.register(new LocatorSetFeature()).

It would be important to tie in startup of other components based on the lifecycle of your container, so this still feels a bit hacky. You might consider adding those classes as first class dependencies in the HK2 container and simply injecting the appropriate dependencies into your third party classes (using a Binder, for example).

StenaviN
  • 3,687
  • 24
  • 34
Zack
  • 1,181
  • 2
  • 11
  • 26
  • 3
    The code you have included does not compile: locator = scopedLocator does not compile as locator has not been defined, where is this variable? Please provide a correction. – PeterS Jun 03 '16 at 16:23
4

I am going to assume you are starting up a servlet and have a class extending org.glassfish.jersey.server.ResourceConfig and your bindings are correctly registered (e.g. using a Binder and registerInstances). If you then want to access the ServiceLocator in order to perform additional initialization, you have two choices:

One approach is to register a ContainerLifecycleListener (as seen here in this post):

// In Application extends ResourceConfig constructor
register(new ContainerLifecycleListener() {

        @Override
        public void onStartup(final Container container) {
            // access the ServiceLocator here
            final ServiceLocator serviceLocator = container.getApplicationHandler().getInjectionManager().getInstance(ServiceLocator.class);

            // Perform whatever with serviceLocator
        }

        @Override
        public void onReload(final Container container) {
            /* ... */}

        @Override
        public void onShutdown(final Container container) {
            /* ... */}
    });

The second approach is to use a Feature, which can also be auto-discovered using @Provider:

@Provider
public final class StartupListener implements Feature {

    private final ServiceLocator sl;

    @Inject
    public ProvisionStartupListener(final ServiceLocator sl) {
        this.sl = sl;
    }

    @Override
    public boolean configure(final FeatureContext context) {
        // Perform whatever action with serviceLocator
        return true;
    }
sfiss
  • 2,119
  • 13
  • 19