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