jsr339-experts@jax-rs-spec.java.net

[jsr339-experts] Re: HEADS-UP: ParamConverter API issues.

From: Bill Burke <bburke_at_redhat.com>
Date: Thu, 11 Oct 2012 12:55:48 -0400

On 10/11/2012 5:23 AM, Sergey Beryozkin wrote:
> Hi,
> On 10/10/12 17:01, Marek Potociar wrote:
>> Hi all,
>>
>> when implementing the support for ParamConverter and
>> ParamConverterProvider we found some unclear points I'd like to discuss
>> with you:
>>
>> *1. HeaderDelegate vs. ParamConverter priority*
>> ParamConverter is documented to work also for header parameters, which
>> clashes with existing JAX-RS HeaderDelegate concept. We may either
>> decide to not support ParamConverters in case of header value conversion
>> or we need to define the resolution priorities. The suggestion is to
>> define following priority-based resolution algorithm for header data
>> conversion:
>>
>> 1. Custom (user supplied) ParamConverter (not null ParamConverter
>> returned from ParamConverterProvider)
>> 2. Implementation specific HeaderDelegate
>> 3. Implementation specific ParamConverter
>>
>
> Is HeaderDelegate supposed to be called directly by the runtime ?
>
> IMHO it would be much simpler to state that if a particular
> HeaderDelegate implementation want to act as ParamConverter then let it
> implement ParamConverterProvider
>
>> Also, a HeaderDelegate could extend from ParamConverter as the APIs are
>> essentially identical.
>> *
>> 2. ParamConverter used in ResponseBuilder (as defined by current
>> javadoc)*
>> The problem is that a ResponseBuilder instance is created via
>> RuntimeDelegate, which is not application scoped, but is shared among
>> all application. As such, the RuntimeDelegate instance has no access to
>> application providers. (Btw. I'm wondering whether any of your
>> implementations does provide access to application providers in your
>> RuntimeDelegate impl.) This means the application level providers cannot
>> be passed to the response builder instance being created. Consequently,
>> when the user calls Response#getStringHeaders() on the built response,
>> some of the passed headers may not be convertible, e.g.:
>>
>> // cannot use ParamConverter to convert MyBean instance.
>> Response.ok().header("response-header", new
>> MyBean("header")).build().getStringHeaders();
>>
>> One solution (although not the most elegant) would be to document that
>> ParamConverter will not convert headers passed into Response (only
>> toString() will be called on them). However, header parameters passed in
>> ContainerResponseFilter or WriterInterceptor will still be convertible.
>> For example, assume a registered ParamConverter<MyBean> on the server
>> (using MyBeanParamConverterProvider) and the following resource method
>>
>> @GET
>> public Response get() {
>> Response response = Response.ok()
>> .header("response-header", new MyBean("header"))
>> .build();
>>
>> // the code bellow would fail.
>> response.getHeaderString("response-header");
>>
>> ...
>>
>> return response;
>> }
>>
>> ...which returns a response processed by a response filter:
>>
>> public static class MyFilter implements ContainerResponseFilter {
>>
>> @Override
>> public void filter(ContainerRequestContext requestContext,
>> ContainerResponseContext responseContext) {
>>
>> // the code bellow would pass.
>> response.getHeaderString("response-header");
>>
>> ...
>> }
>> }
>>
>> The above looks a bit inconsistent which is why I'm mentioning it.
>> Obviously, the any Response instance returned as a result of a the
>> client-side request invocation will have access to application providers
>> and as such the conversion would work fine.
>>
>
> I'd propose to limit the scope of where ParamConverters can be applied,
> specifically they can only be applied when populating the (method)
> request parameters
>
>> *3. WebTarget vs. UriBuilder*
>>
>> The API docs defines that ParamConverters are supported in the Client
>> API. IOW, any parameters passed to WebTarget (headerParam, queryParam,
>> resolveTemplate, ...) but also parameter values passed by
>> resolveTemplate should be convertible using the available
>> ParamConverters configured in the WebTarget's configuration. Note
>> however, that while WebTarget and UriBuilder share similar interface,
>> the parameter conversion would work only in WebTarget instance and not
>> in an arbitrary UriBuilder instance directly as UriBuilder (akin to
>> ResponseBuilder) is created by a RuntimeDelegate instance and as such is
>> not bound to a specific application runtime. Only UriBuilder instances
>> returned by WebTarget may be capable of using ParamConverters (albeit
>> UriBuilder documentation specifically talks only about calling
>> toString() method in these cases).
>>
>> So the question is whether we should support ParamConverters in
>> UriBuilder instances created by a WebTarget, or not:
>>
>> final WebTarget target = /... some web targe/t
>> target.queryParam("a", new MyBean("aaa")); // works using ParamConverters
>>
>> UriBuilder uriBuilder = target.getUriBuilder();
>> uriBuilder.queryParam("b", new MyBean("bbb")).build(); // may work using
>> ParamConverters
>>
>> UriBuilder.fromPath(...)..queryParam("c", new MyBean("ccc")).build() //
>> will only use MyBean.toString()
>>
>> I think the inconsistency illustrated in the example above is obvious,
>> which may be confusing to JAX-RS users. Not sure however how to address
>> it. A possible way would be to try to make the RuntimeDelegate instances
>> bound to applications. But that would be quite complicated. In any case,
>> we need to find a clean solution. If we're not able to find one at the
>> moment, we should perhaps postpone introduction of the ParamConverter
>> API.
>>
> IMHO trying to get ParamConvertes applicable at different API levels
> complicates things a lot.
>

Resteasy has RuntimeDelegates bound to applications. I don't have a
problem with whatever you decide though.

-- 
Bill Burke
JBoss, a division of Red Hat
http://bill.burkecentral.com