2

hi i am facing problem while moving the below code from jersey 1.x to jersey 2.x

@Provider
public class LocaleProvider
      extends AbstractHttpContextInjectable<Locale>
      implements InjectableProvider<Context, Type> {

    @Override
    public Injectable<E> getInjectable(ComponentContext ic, Context a, Type c) {
        if (c.equals(Locale.class)) {
            return this;
        }

        return null;
    }

    @Override
    public ComponentScope getScope() {
        return ComponentScope.PerRequest;
    }

    @Override
    public Locale getValue(HttpContext c) {
        final Locales locales = c.getRequest().getAcceptableLanguages();
        if (locales.isEmpty()) {
          return Locale.US;
        }
        return locales.get(0);
    }
}

I also understand that HK2 is available in Jersey 2.0, but I cannot seem to find docs that help with Jersey 2.0 integration.

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
rahul
  • 239
  • 1
  • 2
  • 10

1 Answers1

2

If you're going to use the @Context annotation, really all you need to do is implement a Factory<T> parameterized by the type you want to inject. You can even inject other standard injectable objects into the Factory, for instance HttpServletRequest, ContainerRequestContext, HttpHeaders among others. For example, to match what's going on in your example above

import java.util.List;
import java.util.Locale;
import javax.inject.Inject;
import javax.ws.rs.core.HttpHeaders;
import org.glassfish.hk2.api.Factory;

public class LocaleFactory implements Factory<Locale> {

    private final HttpHeaders headers;

    @Inject
    public LocaleFactory(HttpHeaders headers) {
        this.headers = headers;
    }

    @Override
    public Locale provide() {
        List<Locale> acceptableLanguges = headers.getAcceptableLanguages();

        if (acceptableLanguges.isEmpty()) {
            return Locale.US;
        }

        if ("*".equals(acceptableLanguges.get(0).toString())) {
            return Locale.US;
        }

        return acceptableLanguges.get(0);
    }

    @Override
    public void dispose(Locale t) { /* Do nothing */ }
}

Then you need to bind the factory. For example in your ResourceConfig. You can set the scope there, as in the getScope() in your example. There is currently support for Singleton, RequestScoped and PerLookup (which is default if not specified)

@ApplicationPath("/api")
public class AppConfig extends ResourceConfig {

    public AppConfig() {
        packages("packages.to.scan");

        register(new AbstractBinder(){
            @Override
            public void configure() {
                bindFactory(LocaleFactory.class).to(Locale.class)
                                         .in(RequestScoped.class);
            }
        });
    }
}

If you are using a web.xml, then you can create a Feature and register the AbstractBinder there, as seen here

After this, you can just inject it

@GET
public Response getLocale(@Context Locale locale) {

If you want to use a custom annotation, then you will need to implement an InjectionResolver for the custom annotation. You can see a complete example here, and read more in the Jersey documentation - Defining Custom Injection Annotation

Community
  • 1
  • 1
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • it is not able to autowired the headers bean in the above code – rahul May 08 '15 at 11:28
  • What makes you say that? I've tested the above code before posting. I used Jersey 2.17 (the latest) to test. Did you do anything different from the above code? – Paul Samsotha May 08 '15 at 11:30
  • actually i am using dropwizard 0.8.1 version – rahul May 08 '15 at 11:40
  • Shouldn't be a problem. Did you `env.jersey().register(new AbstractBinder(){...});`? – Paul Samsotha May 08 '15 at 11:43
  • yes i have done env.jersey().getResourceConfig().register(new AbstractBinder(){ @Override public void configure() { bindFactory(LocaleFactory.class).to(Locale.class) .in(RequestScoped.class); } }); – rahul May 08 '15 at 11:46
  • Are you getting a NPE? Let me test it. Actually I'l test in a bit. but first can you try to use `javax.inject.Provider headers`. Then when you retrieve it use `headers.get()` to get the actual `HttpHeaders` – Paul Samsotha May 08 '15 at 11:47
  • i am not getting any error while building the code .. it was coming on intellij .. thanks :) – rahul May 08 '15 at 11:57
  • I don't know It works fine for me the first way without `Provider` (I thought it might be a scope issue). What makes you say it is not working? – Paul Samsotha May 08 '15 at 12:09
  • no it is working with the first way only .. intellij was showing the error . but it was building fine. – rahul May 08 '15 at 12:12
  • its not working .. while i am hitting the request its not coming to provide methold. – rahul May 15 '15 at 07:13
  • And what if I want to inject it to a field like this? `@Context protected HttpServletRequest request;` – sclausen Jan 12 '16 at 10:11
  • @Phosphoros what's stopping you? You can post another question if something is not working for you – Paul Samsotha Jan 12 '16 at 10:23
  • I need my resource to not be a singleton, because I have to pass objects for the constructor. So I can't register with `jersey.register(ResourceName.class);` and I don't know how to satisfy the request scope and the objects I have to pass for instantiation. I followed the more detailed example here http://stackoverflow.com/questions/30629282/dropwizard-jersey-not-inside-a-request-scope-when-creating-custom-annotati?lq=1 – sclausen Jan 12 '16 at 10:54
  • @Phosphoros You can inject it as `javax.inject.Provider` and just call `get()` to lazily retrieve it on each request. Or you can make bind the `Factory` as proxiable. – Paul Samsotha Jan 12 '16 at 11:03
  • @Phosphoros would you mind posting another question. There's more I can elaborate on this, but I'd rather not do it in comments – Paul Samsotha Jan 12 '16 at 11:11