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:
Custom (user supplied) ParamConverter (not null ParamConverter returned from ParamConverterProvider)
Implementation specific HeaderDelegate
Implementation specific ParamConverter
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.
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 target
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.
Any thoughts or ideas?
Thanks,
Marek