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

[jsr339-experts] Re: General rule for exposing/manipulating header values

From: Bill Burke <bburke_at_redhat.com>
Date: Tue, 28 Feb 2012 11:03:54 -0500

On 2/28/12 4:53 AM, Marek Potociar wrote:
>
>
> On 02/24/2012 10:01 PM, Bill Burke wrote:
>> This is related to my last comment on:
>>
>> http://java.net/jira/browse/JAX_RS_SPEC-176
>>
>>
>> Any interface that manipulates or exposed header values the client and server side and within Filter or Interceptors has
>> to follow certain guidelines. You have to be extremely careful on whether the header manipulation api deals with header
>> values that are strings or java.lang.Object.
>>
>> A ReaderInterceptorContext the header values can ONLY be strings as they are ALWAYS provided from an HTTP message
>> (request on server side, response on client side). Vice-versa for WriterInterceptorContext. Header values may be actual
>> Objects rather than strings as they are provided by user-code. This is specifically what I was referring to in previous
>> emails.
>>
>> Both RequestHeader and ResponseHeader instances can either be populated through an HTTP message or from user code since
>> they can be used on both client and server. Therefore their interfaces that manipulate or expose header values must be
>> more generic and return java.lang.Object.
>>
>> Furthermore, header values must not be marshalled into string values until you are about to send the bits on the wire.
>> And the following must be true:
>>
>> FooHeader fh = new FooHeader();
>>
>> Request/Response r = builder.header("foo", fh).build();
>> Object value = r.getReXXXXHeaders().getHeader("foo");
>>
>> value == fh
>>
>> Same reference equality must be true if the header value was set via the Invocation.Builder interface. Why is this
>> important? Well, consider a digital signature header like DKIM. You really can't complete the header until you
>> calculate the signature of the entity and you might need information contained in the header object to calculate the
>> signature (i.e. a private key).
>
> You can store the extra information needed for producing a signature in the request properties and then add the header
> only once it's fully calculated. Or am I missing something?
>
> In general, I lean towards the idea of exposing headers for reading only as strings (which is in line with your earlier
> request on this mailing list) as I assume that the most prevalent use-cases for manipulating headers are "set-once" and
> "read-once".
>

What you are suggesting is akin to marshalling the request before it is
finished being built.

You have a point about using properties, but app developers will still
want to be able to use a Java type to set header values in client
request filters, message body writers, writer interceptors, and server
response filters. Plus my intuition tells me that you will want to
access the Java types instead of marshalling them into strings. I can
see many cases where parsing/unparsing is a performance hit, or just
burdensome on the developer.



> Also, for the other use cases, I'm worried about the end-user experience and ability to write correct code.

I find it ironic that in this specific case you are worried about the
end-user experience. The current filter/interceptor/request/response
API is *littered* with these awkward end-user experiences. Please be
consistent.


> Since you
> can set the value of the header using an arbitrary Java type, it is hard to make sure that the Object you're getting
> from the map is of the type that you expect. In cases where the headers are used or modified by multiple filters the
> correct defensive coding boilerplate is simply too much imho:
>
> Object h = request.getHeader("Foo");
> Foo foo;
> if (h instanceof Foo) {
> foo = (Foo) h;
> } else {
> hdOld = RuntimeDelegate.getInstance().createHeaderDelegate(h.getClass());
> hdFoo = RuntimeDelegate.getInstance().createHeaderDelegate(Foo.class);
> foo = hdFoo.fromString(hdOld.toString(h));
> }
>

But how is the requirement of parsing and unparsing a String to
access/modify a header better when the object is available? The only
thing you are really saving is the first 3 lines of code.

Actually this is VERY USEFUL information. If the header value is a
String, you know that the header value was created by the user. If it
is a java type, you know that the user expects JAX-RS to handle the
marshalling.

> At last, this would once again open a problem in unified request/response. Note that having unified req/resp is not just
> for the sake of nice API. It's a practical thing that lets users write common filters for the client and server side.
>

Well, you still have these exact same semantics in MessageBodyWriters
where the headers are expressed as <String, Object>. So, I don't
understand what your beef is. Be consistent with the semantics of
MessageBodyWrtiers, IMO. If header values could be set with Java types,
make those Java types available.

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