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

[jsr339-experts] Re: [jax-rs-spec users] Re: HEADS-UP: ParamConverter API issues.

From: Marek Potociar <marek.potociar_at_oracle.com>
Date: Thu, 11 Oct 2012 22:58:08 +0200

On Oct 11, 2012, at 6:55 PM, Bill Burke <bburke_at_redhat.com> wrote:

>
>
> 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.
>
I wonder how do you achieve that. Using a thread local variable is the only thing that comes to my mind. Still, I'm not sure how would you deal with a case when someone instantiates e.g. a UriBuilder (which internally uses RuntimeDelegate) from a non-JAX-RS application (e.g. a pure EJB one or such)?

Marek

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