5 Replies Latest reply on Nov 30, 2011 10:59 PM by Andrew Hughes

    Generic Typed Injection by Class at Runtime, how?

    Andrew Hughes Newbie

      Easiest if I exaplain this with a simple example:



      public class MyPlace implements Place { ...}


      public interface PlaceTokenizer<T extends Place> { .... }


      public class MyPlaceTokenizer implements PlaceTokenizer<MyPlace> { ...}


      and I want to use CDI to obtain an instance of PlaceTokenizer<T extends Place> from a given place....


      PlaceTokenizer<T extends Place> getPlaceTokenizer(T thePlace) {
          //HOW can I use CDI to obtain PlaceTokenizer<T extends Place> for 'thePlace'?
      }


      Thanks in advance :)
      --AH

        • 2. Re: Generic Typed Injection by Class at Runtime, how?
          Ales Justin Master


          @Inject Instance<PlaceTokenizer> tokenizers;



          PlaceTokenizer<MyPlace> = tokenizers.select(new TypeLiteral<MyPlace>{}).get();

          • 3. Re: Generic Typed Injection by Class at Runtime, how?
            Andrew Hughes Newbie

            Hi Ales,


            Thank you for your replies. Unfortunately, I don't think your advice will resolve my problem, because the TypeLiteral annonymous class requires MyPlace and not thePlace.getClass(). Effectively, this still leaves me with only a 'compile time' solution.... and I can solve that with @Inject MyPlaceTokenizer<MyPlace>... erasure shows its head again :'( The only solution I can find at the moment is to associate MyPlace to MyPlaceTokenizer via a @PlaceQualifier(name="MyPlace") qualifier. If I want to secure this any more, I will need to write a CDI extension to scan for each of the Bean Types, and see that there is a 1:1 binding/association.


            headache++;




            Ales Justin wrote on Nov 09, 2011 09:47:



            @Inject Instance<PlaceTokenizer> tokenizers;



            PlaceTokenizer<MyPlace> = tokenizers.select(new TypeLiteral<MyPlace>{}).get();




            Click HELP for text formatting instructions. Then edit this text and check the preview.


            I have two vague ideas floating in my head (and I doubt either will work cleanly):
            1. Qualifiers : The problem here is that if I re-use a common qualifier @PlaceQualifier on all Place objects, then

            • 4. Re: Generic Typed Injection by Class at Runtime, how?
              Andrew Hughes Newbie

              whoops, I half deleted what I was going to write at the end... I might as well say it now...


              I have two vague ideas floating in my head (and I doubt either will work cleanly): 1. Qualifiers : The problem here is that if I re-use a common qualifier @PlaceQualifier on all Place objects, then...


              Continued...


              Qualifiers don't help me at compile time. I can't strictly type/bind to MyPlace/MyPlaceTokenizer using qualifiers, because there is nothing from binding YourPlace / MyPlace...


              @PlaceQualifier(placeClass=MyPlace.class)
              public class YourPlaceTokenizer implements PlaceTokenizer<YourPlace> { ... }


              So... tokenizers.select(new PlaceQualifierLiteral(instanceOfMyPlace.getClass())); will return a PlaceTokenizer<YourPlace> and not PlaceTokenizer<MyPlace> This leads to other issues of course :'(


              Other vague idea I had was to try and register the beans with a custom CDI extension. But this is little merit as erasure will again prevent the extension from determining the type of generic arguments at runtime.


              Either way
              headache++;

              • 5. Re: Generic Typed Injection by Class at Runtime, how?
                Andrew Hughes Newbie

                This is the best I can come up with....




                Create a Qualifier (binding by Place class)



                @Qualifier
                @Target({ METHOD, FIELD, PARAMETER, TYPE })
                @Retention(RUNTIME)
                public @interface PlaceTyped {
                     Class<? extends Place> value();
                }
                



                Create a Literal for the Qualifier



                This is used to obtain a PlaceTyped (Qualifier) at runtime for programmatic lookup.


                public class PlaceTypedLiteral extends AnnotationLiteral<PlaceTyped> implements
                          PlaceTyped {
                     private final Class<? extends Place> value;
                
                     public Class<? extends Place> value() {
                          return value;
                     }
                
                     public PlaceTypedLiteral(Class<? extends Place> value) {
                          this.value = value;
                     }
                }
                



                Example Bean Definition



                @PlaceTyped(MyPlace.class)
                public class MyPlaceTokenizer implements PlaceTokenizer<MyPlace> { ...}
                



                Programmatic Lookup of PlaceTokenizer for a given Place object (via .class)



                @Inject
                @Any
                Instance<PlaceTokenizer<Place>> allPlaceTokenizers;
                ...
                //if place is instance of MyPlace the following should get MyPlaceTokenizer...
                allPlaceTokenizers.select(new PlaceTypedLiteral(place.getClass())).get();
                



                The Good and Bad



                This is easy (good), but its not type safe (bad)... I could very well have mixed Your and My place in the annotation and generic type, which would no doubt be a bug...


                @PlaceTyped(YourPlace.class)
                public class MyPlaceTokenizer implements PlaceTokenizer<MyPlace> { ...}