Skip to content
Advertisement

Two implementations of one interface in Jersey/HK2, reuse first in other

I have an interface with a naive implementation, say

 interface MyService { 
    void doIt();
 } 

 class MyServicePlain implements MyService{

    @Inject
    SomeOtherInterface

    public void doIt () { 
    }
 }

I want to make a cache that caches the doIt, so I wrote a wrapper :

 class CachingMyService implements MyService {

       @Inject 
       MyService inner;

       int cacheThingie;

       public int doIt() {
               if (cached) ... {
                   return cacheThingie;
               } 
               else {
                   result = inner.doIt();
                   addToCache(result);
                   return result;
               }
       }
 }

Then, I add both implementations to my Binder:

   public class ApplicationBinder extends AbstractBinder {

        protected void configure() {
        this.bind(MyServicePlain.class).to(MyService.class).in(Singleton.class).ranked(2);
        this.bind(CachingMyService.class).to(MyService.class).in(Singleton.class).ranked(2);
        }
   }

I get errors complaining about:

org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=MyService,parent=CachingMyService},position=-1,optional=false,self=false,unqualified=null,1102650897)

I trief using Qualitifaction like this:

 @Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface NonCached {
}


 @NonCached
 class MyServicePlain implements MyService{
 }

And using that:

  class CachingMyService implements MyService {

       @Inject @NonCached 
       MyService inner;

But that does not work either.

What is the proper way to wrap a caching service like this? And how can I make hk2 choose the proper implementations?

Advertisement

Answer

You need to use the qualifiedBy(Annotation) method when you want to qualify different types. You also need to annotate each injection point with a different qualifier annotation.

First you need the annotations and have a way to get an instance of them (the qualifiedBy method requires an annotation instance)

@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface NonCached {

    class Literal extends AnnotationLiteral<NonCached> implements NonCached {

        public static final NonCached INSTANCE = new Literal();

        private Literal() {
        }
    }
}

@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Cached {

    class Literal extends AnnotationLiteral<Cached> implements Cached {

        public static final Cached INSTANCE = new Literal();

        private Literal() {
        }
    }
}

Then when you bind them used qualifiedBy

this.bind(MyServicePlain.class).to(MyService.class)
    .in(Singleton.class).qualifiedBy(NonCached.Literal.INSTANCE);    
this.bind(CachingMyService.class).to(MyService.class)
    .in(Singleton.class).qualifiedBy(Cached.Literal.INSTANCE);

Then when you inject them, add the applicable qualifier

@Inject
@NonCached
MyService service;

@Inject
@Cached
MyService service;
User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement