users@jersey.java.net

Re: [Jersey] Unifying injection

From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
Date: Mon, 26 May 2008 14:48:34 +0200

Hi,

I need to make one final change to the injectable interfaces, as the use
of extend Injectable types to define the scope is essentially
duplicating that of ComponentProvider.Scope:

   public interface InjectableProvider<A extends Annotation, C> {
     ComponentProvider.Scope getScope();

     Injectable getInjectable(InjectableContext ic, A a, C c);
   }

In the past we have discussed renaming the Scope names, given the use in
this context and for ComponentProvider implementations i think we can
rename things from->to:

   WebApplication -> Singleton

   ApplicationDefined -> PerRequest

            Undefined -> Undefined

In the context of an InjectableProvider:

   - a Singleton scope means that the value returned from
     Injectable.getValue is a constant, thus the value can be cached
     when injected on per-request resources.

   - a PerRequest scope requires that the value is dependent on the
     HttpContext of the HTTP request and the value cannot be
     cached.

   - an Undefine scope means that the value is dependent on the
     underlying IoC framework. The value cannot be cached.

I propose to make these changes after the changes to the mavan
restructuring is done.

Paul.

Paul Sandoz wrote:
> Paul Sandoz wrote:
>> Next steps:
>>
>> - plug in parameter-based injectable providers for parameters of
>> methods/constructors. Thus moving fully over to the unified model.
>
> Done (with some minor changes to the Injectable interface).
>
>
>> Only use thread local proxied injectable providers for non-per-request
>> resources (performance enhancement).
>>
>
> TODO.
>
>
>> - allow use of @Provider on InjectableProvider. Thus it is no longer
>> necessary to register via WebApplication. They become components that
>> can also be managed by an IoC.
>>
>
> Done.
>
> Is is now possible to have constructor/method signatures as follows:
>
> @GET
> String get(
> @Inject Foo foo,
> @MyAnnotation Thing t,
> @QueryParam("l") Map<String, String> m);
>
> Where @Inject defers to the ComponentProvider.
>
> Where @MyAnnotation is provided by (say) a developer-declared singleton
> injectable provider.
>
> Where @QueryParam annotated Map<String, String> is provided by (say) a
> developer-declared per-request injectable provider that overrides
> support for @QueryParam thus one can support Java types that don't
> conform to the basic 311 parameter rules. See below for code that is
> used by a unit test in this case.
>
> In this respect i think we have completed our previously enumerated
> injection-based goals we set.
>
> Paul.
>
> @Provider
> public static class QueryParamInjectableProvider implements
> InjectableProvider<QueryParam, Parameter,
> PerRequestInjectable<Map<String, String>>> {
> public PerRequestInjectable<Map<String, String>>
> getInjectable(InjectableContext ic,
> QueryParam a, Parameter c) {
> if (Map.class != c.getParameterClass())
> return null;
>
> final String name = c.getSourceName();
> return new PerRequestInjectable<Map<String, String>>() {
> public Map<String, String> getValue(HttpContext c) {
> String value =
> c.getUriInfo().getQueryParameters().getFirst(name);
>
> Map<String, String> m = new LinkedHashMap<String,
> String>();
> String[] kvs = value.split(",");
> for (String kv : kvs) {
> String[] nv = kv.split("=");
> m.put(nv[0].trim(), nv[1].trim());
> }
>
> return m;
> }
> };
> }
> }
>
> @Path("/")
> public static class MethodInjected {
> @GET
> public String get(@QueryParam("l") Map<String, String> kv)
> throws Exception {
> String v = "";
> for (Map.Entry<String, String> e : kv.entrySet())
> v += e.getKey() + e.getValue();
> return v;
> }
> }
>
> public void testMethodInjected() throws IOException {
> initiateWebApplication(MethodInjected.class,
> QueryParamInjectableProvider.class);
>
> URI u = UriBuilder.fromPath("").
> queryParam("l", "1=2, 3=4, 5=6").build();
> assertEquals("123456", resource("/").uri(u).get(String.class));
> }
>
>> - injection meta class to perform injection to avoid use of reflection
>> each time, a performance enhancement for per-request resources.
>>
>> - support injection onto fields of per-request resources for per-request
>> injectable providers.
>>
>> - support bean setter methods.
>>
>>
>> A nice consequence: when completed one should be able to add their own
>> injectable providers for @*Param that go beyond the existing rules of
>> 311 e.g. using joda date time class. Another possible nice
>> consequence: it could make the support of @FormParam easier :-)
>>
>> Paul.
>>
>>
>>
>> wa.addInjectable(new InjectableProvider<PersistenceUnit, Type,
>> SingletonInjectable>() {
>> public SingletonInjectable<EntityManagerFactory>
>> getInjectable(InjectableContext ic, PersistenceUnit pu, Type c) {
>> if (!c.equals(EntityManagerFactory.class))
>> return null;
>>
>> // TODO localize error message
>> if (!persistenceUnits.containsKey(pu.unitName()))
>> throw new ContainerException("Persistence unit '"+
>> pu.unitName()+
>> "' is not configured as a servlet
>> parameter in web.xml");
>> String jndiName = persistenceUnits.get(pu.unitName());
>> ThreadLocalNamedInvoker<EntityManagerFactory>
>> emfHandler =
>> new
>> ThreadLocalNamedInvoker<EntityManagerFactory>(jndiName);
>> final EntityManagerFactory emf =
>> (EntityManagerFactory) Proxy.newProxyInstance(
>> EntityManagerFactory.class.getClassLoader(),
>> new Class[] {EntityManagerFactory.class },
>> emfHandler);
>>
>> return new SingletonInjectable<EntityManagerFactory>() {
>> public EntityManagerFactory getValue() {
>> return emf;
>> }
>> };
>> }
>> });
>>
>>
>> Paul Sandoz wrote:
>>> Hi,
>>>
>>> Currently the injection support is split between the Injectable
>>> abstract class and hard-wired support for the @*Param.
>>>
>>> There are a number of use-cases we need to support:
>>>
>>> - pluggable injection on parameters of constructors and methods,
>>> so then we can support the inclusion of IoC based parameters as
>>> part of the signature.
>>>
>>> - 311 requirement of using injection with setter methods, for example:
>>>
>>> @QueryParam("name")
>>> public void setName(String name) { ... }
>>>
>>> @Context
>>> public void setUriInfo(UriInfo name) { ... }
>>>
>>> - 311 requirement for injection of @*Param on fields of per-request
>>> resources.
>>>
>>> - as a consequence this should make it easier to optimize the creation
>>> and injection on per-request resources as well as the method
>>> invocation on resource methods and sub-resource locators of
>>> per-request or non-per-request resources (with the long term view of
>>> injectables providing generated byte code "snippets")
>>>
>>> Such cases force the demand for unifying the pluggable injection
>>> model, and abstracting it from where stuff gets injected (field,
>>> parameter, or setter method) and constraining what can be injected
>>> based on the life-cycle of the resource, the life-cycle of what is
>>> injected and the context of injection.
>>>
>>>
>>> More technical details follow....
>>>
>>> I am proposing that there be an InjectableProvider that provides
>>> Injectable instances per the context, annotation and type (same
>>> pattern as the ResourceProvider concept). Concrete Injectable
>>> instances can be either one of SingletonInjectable, where the
>>> injectable value is independent of the request, or
>>> PerRequestInjectable, where the injectable value is dependent on the
>>> request (see below for proposed classes, i am sure they will change
>>> when implementation starts).
>>>
>>> This approach is a little different to the current mechanism of
>>> Injectable, instances of which are essentially singletons. Instead
>>> Jersey will analyze the fields, setter, constructor/method parameters
>>> of resource classes and loop through the InjectableProvider's until
>>> it finds one that returns null. So essentially Jersey will create an
>>> artifact of the runtime model to perform specific injection for a
>>> resource class (this should speed up per-request life-cycle injection
>>> for fields and setters) (such artifacts are already created for
>>> constructor and method invocation).
>>>
>>> For the longer term i envisage a ByteCodeGenerator interface that
>>> could be implemented by Injectable implementations. To generate the
>>> snippet of byte code related to obtaining the injectable value. In
>>> general this would also extend to the actual invocation of resource
>>> methods.
>>>
>>> Paul.
>>>
>>> // Provide an injectable instance for a particular
>>> // annotation and type.
>>> interface InjectableProvider<A extends Annotation,
>>> I extends Injectable> {
>>> enum Context { Field, Parameter, Setter };
>>>
>>> A getAnnotation();
>>>
>>> // return null if not supported
>>> I<A> getInjectable(Context c, A a, Type t)
>>> }
>>>
>>> abstract class Injectable<A extends Annotation> {
>>> public Injectable(A a, Type t) { ... }
>>> }
>>>
>>> class SingletonInjectable<A extends Annotation, T>
>>> extends Injectable {
>>>
>>> public abstract T getValue();
>>> }
>>>
>>> class PerRequestInjectable<A extends Annotation, T>
>>> extends Injectable {
>>>
>>> public abstract T getValue(HttpContext context);
>>> }
>>>
>>
>

-- 
| ? + ? = To question
----------------\
    Paul Sandoz
         x38109
+33-4-76188109