users@jersey.java.net

Re: [Jersey] Guice access to Jersey-created objects

From: James Strachan <james.strachan_at_gmail.com>
Date: Mon, 17 Aug 2009 12:09:54 +0100

2009/8/17 Paul Sandoz <Paul.Sandoz_at_sun.com>:
>
> On Aug 17, 2009, at 10:15 AM, James Strachan wrote:
>>>
>>> Guice's approach to custom injections using a TypeListener has
>>> limitations because it means one cannot integrate cleanly with the
>>> parameters associated with @Inject on constructors, fields or methods.
>>
>> Agreed
>>
>>> I would like to do something like:
>>>  MetaProvider mp = ...
>>>  bind(QueryParam.class).toMetaProvider(mp);
>>> Where a meta provider has access to the injection type, the annotations
>>> on
>>> the type, and the target class. The meta provider is responsible for
>>> creating a provider with the appropriate scope.
>>> I am very very tempted to modify Guice to support this concept.
>>
>> I'd love this too! Basically being able to use the binding annotation
>> as a parameter in a provider method along with the injection point
>> (method/field); so you can have a single provider (class/method) which
>> takes the binding annotation as a parameter - then you can use its
>> attributes to create the value.
>>
>> This came up recently on the guice list though unfortunately didn't
>> seem to get much traction from guice committers
>> http://groups.google.com/group/google-guice/msg/aafaad43d9433bbd
>>
>
> It seems Guice has gone to great lengths to provide plugin points after
> construction but is missing one very obvious sweet spot that could benefit
> Guice developers so they do not have to resort or lower-level approaches
> utilizing reflection. Basically it is the use case of injecting when some
> context of where one is injecting is required.
>
> The example of injecting a Log4J Logger could work "correctly" in the Guice
> way (also one does not have to always key of an annotation for this to be of
> benefit). Thus the example would become:
>
> import org.apache.log4j.Logger;
> import com.publicobject.log4j.InjectLogger;
>
> public class PaymentService {
>  @Inject Logger logger;
>
>  ...
> }
>
>  @Override protected void configure() {
>    bind???(Logger.class, new Log4JMetaProvider());
>  }
>
>
> public class Log4JMetaProvider implements MetaProvider<Logger> {
>  public Provider<Logger> getProvider(final Class target, ...) {
>    return new Provider<Logger>() {
>      public Logger get() {
>        return Logger.getLogger(target);
>      }
>    };
>  }
> }
>
>
> Hmm... perhaps even better one could inject the contextual information for a
> provider e.g.:
>
>  @Provides
>  public Logger providerLogger(InjectionContext ic) {
>        return Logger.getLogger(ic.getTarget());
>  }

Agreed! I like the idea of just passing the binding annotation or the
injection context as a parameter to @Provides methods. (I really like
@Provides methods!)

BTW the spi package already has a nice InjectionPoint class ready to
be passed in to provider methods. Am sure it would be fairly easy to
allow it to be passed into provider methods...



> What does Dhanji mean by:
>
>  http://groups.google.com/group/google-guice/msg/e3dcb7b788247088
>
>  "But you can achieve the same result with a TypeListener +
> InjectionListener. "

I didn't get that either; lemme ask on the list :)


> He may have misunderstanding you because TypeListener + InjectionListener
> cannot be used to do:
>
>  public class Foo {
>  @Inject
>  public Foo(@StringResource("blah") String xyz) {
>
> Or does he mean you can achieve what GuiceFruit does? which is of course
> what GuiceFruit uses :-), but makes it easier for the developer.
>
>
> How about if we could do it like this:
>
>  bindAnnotation(StringResource.class).
>    typedWith(String.class).
>    toProvider(StringResourceProvider.class);
>
>  public class StringResourceProvider implements Provider<String> {
>    @Inject
>    public StringResourceProvider(InjectionContext ic) {
>      StringResource sr = ic.getAnnotation(StringResource.class);
>    }
>
>    ...
>  }
>
>
> Then i might be able to do something for Jersey/JAX-RS like the following:
>
>  bindAnnotation(QueryParam.class).
>    toProvider(QueryParamProvider.class).
>    inScope(RequestScoped.class);
>
>
>  public class QueryParamProvider implements Provider<?> {
>    @Inject
>    public QueryParamProvider(InjectionContext ic) {
>       // Analyze type and annotations, throw exception if it cannot be
> supported.
>       // Store state on the provider
>    }
>
>    ...
>  }

Yeah. The argument crazybob often uses on the list is that you can
enumerate all values of your binding annotations and then bind a
specific value (or set of values) for an annotation to a specific
instance/provider in the binding. But that feels kinda dirty to me!

>> FWIW GuiceyFruit provides something like this; you create a
>> implementation of this interface...
>>
>> http://code.google.com/p/guiceyfruit/source/browse/trunk/guiceyfruit-core/src/main/java/org/guiceyfruit/support/AnnotationMemberProvider.java
>>
>
> OK.
>
> In your experience how hard would it be to modify Guice to support this for
> @Inject?

I don't think it'd be that hard to patch guice to allow the
InjectionPoint to be passed into a @Provides method; though getting
the guice folks to accept a patch is way harder :)

-- 
James
-------
http://macstrac.blogspot.com/
Open Source Integration
http://fusesource.com/