You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@cxf.apache.org by Benson Margulies <bi...@gmail.com> on 2009/09/05 19:57:53 UTC
JAX-RS and generics
JAX-RS defines two fundamental interfaces: MessageBodyReader<T> and
MessageBodyWriter<T>, and providers implement.
I claim that GENERIC providers that work for any object (like those
corresponding to data bindings) should, themselves, be GENERIC, and
implement MessageBodyX<T>, not MessageBodyX<Object>.
I claim this because the whole API structure of MessageBodyX assumed this.
It uses Class<T> in a way that requires constant
@SupressWarnings("unchecked") if the base is MessageBodyX<Object>.
To put my money where my mouth is, as it were, I implemented this for the
Aegis providers. When I did this, I discovered that the JAX-RS runtime code
couldn't handle generic type providers. When the provider type is, say,
AegisElementProvider<Book>
then implemented interface comes up as MessageBodyReader<T>, not
MessageBodyReader<Book>. So it is a TypeVariable, not a class or a
ParameterizedType.
I fixed the provider selection code to cope, but I didn't write the
additionally complex code to look at bounds and insist that if there is a
bound the type at hand be within it.
Re: JAX-RS and generics
Posted by Benson Margulies <bi...@gmail.com>.
I agree. I was focussed on the reader. Given that <?> in the writer, it's
clear that you are correct.
On Mon, Sep 7, 2009 at 8:33 AM, Sergey Beryozkin <sb...@progress.com>wrote:
> Hi Benson
>
> In MessageBodyWriter.writeTo() it's actually Class<?> which is in the
> signature. And there's no return value.
>
> We could've implemented just MessageBodyWriter as opposed to
> MessageBodyWriter<Object> but it would stiill cause warning in the user test
> code....
>
> I can agree that implementing MessageBodyWriter<Object> delegates the
> type-safety checks to the actual provider and thus makes that <T> thing
> useless. But for providers choosing to implement MessageBodyWriter<Book> the
> runtime will now ensure the class of the object to be written is assignable
> to Book.class (now that we've implemented the message body provides sorting
> requirement from JAXRS 1.1).
>
> Please don't get me wrong, may be it would be the best option indeed to go
> ahead with passing Object.class MessageBodyWriter<Object> - but I'm afraid
> it will turn the bunch of user providers out there broken...
>
> thanks, Sergey
>
> ----- Original Message ----- From: "Benson Margulies" <
> bimargulies@gmail.com>
> To: <de...@cxf.apache.org>
> Sent: Monday, September 07, 2009 1:06 PM
> Subject: Re: JAX-RS and generics
>
>
> Sergey,
>
> With Java generics, there's a pattern:
>
> <T> public T gloop(Class<T> type, whatever)
>
> That pattern requires that you pass in the class of what you expect to get
> out.
>
> If XXXProvider implements MessageBodyReader<T>, then it must have a
> implement the read API against the same T. You can't, legitimately, cast it
> to MessageBodyReader<Book>.
>
> So, if AegisProvider implements MessageBodyReader<Object>, and you want to
> write clean code that does not get warnings, you have to write:
>
> Object o = p.read(Object.class, ...)
>
> If it implements MessageBodyReader<T>, you then AegisProvider<Book> does
>
> Book b = p.read(Book.class, ...)
>
> Now, if the people who invented JAX-RS have decided to ignore this pattern
> and force us to write code that needs @SuppressWarning("unchecked"), well,
> I'm sad but I'll stop sending email. Since my generic AegisProvider passes
> tests, however, ...
>
>
>
>
> On Mon, Sep 7, 2009 at 5:50 AM, Sergey Beryozkin <sberyozk@progress.com
> >wrote:
>
>
>>
>> https://jsr311.dev.java.net/nonav/javadoc/javax/ws/rs/ext/MessageBodyWriter.html#writeTo(T,%20java.lang.Class,%20java.lang.reflect.Type,%20java.lang.annotation.Annotation[],%20javax.ws.rs.core.MediaType,%20javax.ws.rs.core.MultivaluedMap,%20java.io.OutputStream)<https://jsr311.dev.java.net/nonav/javadoc/javax/ws/rs/ext/MessageBodyWriter.html#writeTo%28T,%20java.lang.Class,%20java.lang.reflect.Type,%20java.lang.annotation.Annotation%5B%5D,%20javax.ws.rs.core.MediaType,%20javax.ws.rs.core.MultivaluedMap,%20java.io.OutputStream%29>
>> <
>> https://jsr311.dev.java.net/nonav/javadoc/javax/ws/rs/ext/MessageBodyWriter.html#writeTo%28T,%20java.lang.Class,%20java.lang.reflect.Type,%20java.lang.annotation.Annotation%5B%5D,%20javax.ws.rs.core.MediaType,%20javax.ws.rs.core.MultivaluedMap,%20java.io.OutputStream%29
>> >
>>
>>
>> type - the class of object that is to be written.
>>
>> So I don't think we should pass Object.class for
>> MessageBodyWriter<Object>.
>> If one would like to avoud doing casts during testing then it should be
>> just
>> MessageBodyWriter<Book> and I'm pretty sure the runtime will pass
>> Book.class.
>>
>> Cheers, Sergey
>>
>>
>>
>> On Sat, Sep 5, 2009 at 1:57 PM, Benson Margulies <bimargulies@gmail.com
>>
>>> >wrote:
>>>
>>> JAX-RS defines two fundamental interfaces: MessageBodyReader<T> and
>>>
>>>> MessageBodyWriter<T>, and providers implement.
>>>>
>>>> I claim that GENERIC providers that work for any object (like those
>>>> corresponding to data bindings) should, themselves, be GENERIC, and
>>>> implement MessageBodyX<T>, not MessageBodyX<Object>.
>>>>
>>>>
>>>> Allow me to modulate this claim. I thought about it some more.
>>>
>>> If you want to define a class as 'implements MessageBodyX<Object>', fine.
>>> However, the right thing to pass to the Class<T> argument will ALWAYS be
>>> Object.class. If you want to cue in the code to the sort of object in
>>> flight, use the Type argument further down the parameter list.
>>>
>>>
>>>
>>> I claim this because the whole API structure of MessageBodyX assumed
>>>> this.
>>>> It uses Class<T> in a way that requires constant
>>>> @SupressWarnings("unchecked") if the base is MessageBodyX<Object>.
>>>>
>>>> To put my money where my mouth is, as it were, I implemented this for
>>>> the
>>>> Aegis providers. When I did this, I discovered that the JAX-RS runtime
>>>> code
>>>> couldn't handle generic type providers. When the provider type is, say,
>>>>
>>>> AegisElementProvider<Book>
>>>>
>>>> then implemented interface comes up as MessageBodyReader<T>, not
>>>> MessageBodyReader<Book>. So it is a TypeVariable, not a class or a
>>>> ParameterizedType.
>>>>
>>>> I fixed the provider selection code to cope, but I didn't write the
>>>> additionally complex code to look at bounds and insist that if there is
>>>> a
>>>> bound the type at hand be within it.
>>>>
>>>>
>>>>
>>>
>>
>
Re: JAX-RS and generics
Posted by Sergey Beryozkin <sb...@progress.com>.
Hi Benson
In MessageBodyWriter.writeTo() it's actually Class<?> which is in the signature. And there's no return value.
We could've implemented just MessageBodyWriter as opposed to MessageBodyWriter<Object> but it would stiill cause warning in the user
test code....
I can agree that implementing MessageBodyWriter<Object> delegates the type-safety checks to the actual provider and thus makes that
<T> thing useless. But for providers choosing to implement MessageBodyWriter<Book> the runtime will now ensure the class of the
object to be written is assignable to Book.class (now that we've implemented the message body provides sorting requirement from
JAXRS 1.1).
Please don't get me wrong, may be it would be the best option indeed to go ahead with passing Object.class
MessageBodyWriter<Object> - but I'm afraid it will turn the bunch of user providers out there broken...
thanks, Sergey
----- Original Message -----
From: "Benson Margulies" <bi...@gmail.com>
To: <de...@cxf.apache.org>
Sent: Monday, September 07, 2009 1:06 PM
Subject: Re: JAX-RS and generics
Sergey,
With Java generics, there's a pattern:
<T> public T gloop(Class<T> type, whatever)
That pattern requires that you pass in the class of what you expect to get
out.
If XXXProvider implements MessageBodyReader<T>, then it must have a
implement the read API against the same T. You can't, legitimately, cast it
to MessageBodyReader<Book>.
So, if AegisProvider implements MessageBodyReader<Object>, and you want to
write clean code that does not get warnings, you have to write:
Object o = p.read(Object.class, ...)
If it implements MessageBodyReader<T>, you then AegisProvider<Book> does
Book b = p.read(Book.class, ...)
Now, if the people who invented JAX-RS have decided to ignore this pattern
and force us to write code that needs @SuppressWarning("unchecked"), well,
I'm sad but I'll stop sending email. Since my generic AegisProvider passes
tests, however, ...
On Mon, Sep 7, 2009 at 5:50 AM, Sergey Beryozkin <sb...@progress.com>wrote:
>
> https://jsr311.dev.java.net/nonav/javadoc/javax/ws/rs/ext/MessageBodyWriter.html#writeTo(T,%20java.lang.Class,%20java.lang.reflect.Type,%20java.lang.annotation.Annotation[],%20javax.ws.rs.core.MediaType,%20javax.ws.rs.core.MultivaluedMap,%20java.io.OutputStream)<https://jsr311.dev.java.net/nonav/javadoc/javax/ws/rs/ext/MessageBodyWriter.html#writeTo%28T,%20java.lang.Class,%20java.lang.reflect.Type,%20java.lang.annotation.Annotation%5B%5D,%20javax.ws.rs.core.MediaType,%20javax.ws.rs.core.MultivaluedMap,%20java.io.OutputStream%29>
>
> type - the class of object that is to be written.
>
> So I don't think we should pass Object.class for MessageBodyWriter<Object>.
> If one would like to avoud doing casts during testing then it should be just
> MessageBodyWriter<Book> and I'm pretty sure the runtime will pass
> Book.class.
>
> Cheers, Sergey
>
>
>
> On Sat, Sep 5, 2009 at 1:57 PM, Benson Margulies <bimargulies@gmail.com
>> >wrote:
>>
>> JAX-RS defines two fundamental interfaces: MessageBodyReader<T> and
>>> MessageBodyWriter<T>, and providers implement.
>>>
>>> I claim that GENERIC providers that work for any object (like those
>>> corresponding to data bindings) should, themselves, be GENERIC, and
>>> implement MessageBodyX<T>, not MessageBodyX<Object>.
>>>
>>>
>> Allow me to modulate this claim. I thought about it some more.
>>
>> If you want to define a class as 'implements MessageBodyX<Object>', fine.
>> However, the right thing to pass to the Class<T> argument will ALWAYS be
>> Object.class. If you want to cue in the code to the sort of object in
>> flight, use the Type argument further down the parameter list.
>>
>>
>>
>>> I claim this because the whole API structure of MessageBodyX assumed
>>> this.
>>> It uses Class<T> in a way that requires constant
>>> @SupressWarnings("unchecked") if the base is MessageBodyX<Object>.
>>>
>>> To put my money where my mouth is, as it were, I implemented this for the
>>> Aegis providers. When I did this, I discovered that the JAX-RS runtime
>>> code
>>> couldn't handle generic type providers. When the provider type is, say,
>>>
>>> AegisElementProvider<Book>
>>>
>>> then implemented interface comes up as MessageBodyReader<T>, not
>>> MessageBodyReader<Book>. So it is a TypeVariable, not a class or a
>>> ParameterizedType.
>>>
>>> I fixed the provider selection code to cope, but I didn't write the
>>> additionally complex code to look at bounds and insist that if there is a
>>> bound the type at hand be within it.
>>>
>>>
>>
>
Re: JAX-RS and generics
Posted by Benson Margulies <bi...@gmail.com>.
Sergey,
With Java generics, there's a pattern:
<T> public T gloop(Class<T> type, whatever)
That pattern requires that you pass in the class of what you expect to get
out.
If XXXProvider implements MessageBodyReader<T>, then it must have a
implement the read API against the same T. You can't, legitimately, cast it
to MessageBodyReader<Book>.
So, if AegisProvider implements MessageBodyReader<Object>, and you want to
write clean code that does not get warnings, you have to write:
Object o = p.read(Object.class, ...)
If it implements MessageBodyReader<T>, you then AegisProvider<Book> does
Book b = p.read(Book.class, ...)
Now, if the people who invented JAX-RS have decided to ignore this pattern
and force us to write code that needs @SuppressWarning("unchecked"), well,
I'm sad but I'll stop sending email. Since my generic AegisProvider passes
tests, however, ...
On Mon, Sep 7, 2009 at 5:50 AM, Sergey Beryozkin <sb...@progress.com>wrote:
>
> https://jsr311.dev.java.net/nonav/javadoc/javax/ws/rs/ext/MessageBodyWriter.html#writeTo(T,%20java.lang.Class,%20java.lang.reflect.Type,%20java.lang.annotation.Annotation[],%20javax.ws.rs.core.MediaType,%20javax.ws.rs.core.MultivaluedMap,%20java.io.OutputStream)<https://jsr311.dev.java.net/nonav/javadoc/javax/ws/rs/ext/MessageBodyWriter.html#writeTo%28T,%20java.lang.Class,%20java.lang.reflect.Type,%20java.lang.annotation.Annotation%5B%5D,%20javax.ws.rs.core.MediaType,%20javax.ws.rs.core.MultivaluedMap,%20java.io.OutputStream%29>
>
> type - the class of object that is to be written.
>
> So I don't think we should pass Object.class for MessageBodyWriter<Object>.
> If one would like to avoud doing casts during testing then it should be just
> MessageBodyWriter<Book> and I'm pretty sure the runtime will pass
> Book.class.
>
> Cheers, Sergey
>
>
>
> On Sat, Sep 5, 2009 at 1:57 PM, Benson Margulies <bimargulies@gmail.com
>> >wrote:
>>
>> JAX-RS defines two fundamental interfaces: MessageBodyReader<T> and
>>> MessageBodyWriter<T>, and providers implement.
>>>
>>> I claim that GENERIC providers that work for any object (like those
>>> corresponding to data bindings) should, themselves, be GENERIC, and
>>> implement MessageBodyX<T>, not MessageBodyX<Object>.
>>>
>>>
>> Allow me to modulate this claim. I thought about it some more.
>>
>> If you want to define a class as 'implements MessageBodyX<Object>', fine.
>> However, the right thing to pass to the Class<T> argument will ALWAYS be
>> Object.class. If you want to cue in the code to the sort of object in
>> flight, use the Type argument further down the parameter list.
>>
>>
>>
>>> I claim this because the whole API structure of MessageBodyX assumed
>>> this.
>>> It uses Class<T> in a way that requires constant
>>> @SupressWarnings("unchecked") if the base is MessageBodyX<Object>.
>>>
>>> To put my money where my mouth is, as it were, I implemented this for the
>>> Aegis providers. When I did this, I discovered that the JAX-RS runtime
>>> code
>>> couldn't handle generic type providers. When the provider type is, say,
>>>
>>> AegisElementProvider<Book>
>>>
>>> then implemented interface comes up as MessageBodyReader<T>, not
>>> MessageBodyReader<Book>. So it is a TypeVariable, not a class or a
>>> ParameterizedType.
>>>
>>> I fixed the provider selection code to cope, but I didn't write the
>>> additionally complex code to look at bounds and insist that if there is a
>>> bound the type at hand be within it.
>>>
>>>
>>
>
Re: JAX-RS and generics
Posted by Sergey Beryozkin <sb...@progress.com>.
https://jsr311.dev.java.net/nonav/javadoc/javax/ws/rs/ext/MessageBodyWriter.html#writeTo(T,%20java.lang.Class,%20java.lang.reflect.Type,%20java.lang.annotation.Annotation[],%20javax.ws.rs.core.MediaType,%20javax.ws.rs.core.MultivaluedMap,%20java.io.OutputStream)
type - the class of object that is to be written.
So I don't think we should pass Object.class for MessageBodyWriter<Object>. If one would like to avoud doing casts during testing
then it should be just MessageBodyWriter<Book> and I'm pretty sure the runtime will pass Book.class.
Cheers, Sergey
> On Sat, Sep 5, 2009 at 1:57 PM, Benson Margulies <bi...@gmail.com>wrote:
>
>> JAX-RS defines two fundamental interfaces: MessageBodyReader<T> and
>> MessageBodyWriter<T>, and providers implement.
>>
>> I claim that GENERIC providers that work for any object (like those
>> corresponding to data bindings) should, themselves, be GENERIC, and
>> implement MessageBodyX<T>, not MessageBodyX<Object>.
>>
>
> Allow me to modulate this claim. I thought about it some more.
>
> If you want to define a class as 'implements MessageBodyX<Object>', fine.
> However, the right thing to pass to the Class<T> argument will ALWAYS be
> Object.class. If you want to cue in the code to the sort of object in
> flight, use the Type argument further down the parameter list.
>
>
>>
>> I claim this because the whole API structure of MessageBodyX assumed this.
>> It uses Class<T> in a way that requires constant
>> @SupressWarnings("unchecked") if the base is MessageBodyX<Object>.
>>
>> To put my money where my mouth is, as it were, I implemented this for the
>> Aegis providers. When I did this, I discovered that the JAX-RS runtime code
>> couldn't handle generic type providers. When the provider type is, say,
>>
>> AegisElementProvider<Book>
>>
>> then implemented interface comes up as MessageBodyReader<T>, not
>> MessageBodyReader<Book>. So it is a TypeVariable, not a class or a
>> ParameterizedType.
>>
>> I fixed the provider selection code to cope, but I didn't write the
>> additionally complex code to look at bounds and insist that if there is a
>> bound the type at hand be within it.
>>
>
Re: JAX-RS and generics
Posted by Benson Margulies <bi...@gmail.com>.
There are two ways to look at this. From the point of view of a 'provider
bean', I can believe that a non-generic class is attractive. Maybe
essential. Maybe my change to the Aegis providers was thus a mistake.
>From the point of view of a clean API callable without suppressing any
warnings, on the other hand, the generic nature of the JAX-RS APIs makes
them ugly to reduce to non-generic types.
On Sun, Sep 6, 2009 at 6:09 PM, Sergey Beryozkin <sb...@progress.com>wrote:
> Hi
>
> I'm concerned it may be not be portable, that is the providers which
> work with other JAXRS implementations will end up being unusable in CXF.
>
> You may be right but I've seen the number of providers which implement
> MessageBodyX<Object> and then cast them internally. For ex to Feed or
> Entry, etc.
>
> Let me ask a question on the jaxrs users list...
> Cheers, Sergey
>
> -----Original Message-----
> From: Benson Margulies [mailto:bimargulies@gmail.com]
> Sent: 06 September 2009 03:15
> To: CXF Dev
> Subject: Re: JAX-RS and generics
>
> On Sat, Sep 5, 2009 at 1:57 PM, Benson Margulies
> <bi...@gmail.com>wrote:
>
> > JAX-RS defines two fundamental interfaces: MessageBodyReader<T> and
> > MessageBodyWriter<T>, and providers implement.
> >
> > I claim that GENERIC providers that work for any object (like those
> > corresponding to data bindings) should, themselves, be GENERIC, and
> > implement MessageBodyX<T>, not MessageBodyX<Object>.
> >
>
> Allow me to modulate this claim. I thought about it some more.
>
> If you want to define a class as 'implements MessageBodyX<Object>',
> fine.
> However, the right thing to pass to the Class<T> argument will ALWAYS be
> Object.class. If you want to cue in the code to the sort of object in
> flight, use the Type argument further down the parameter list.
>
>
> >
> > I claim this because the whole API structure of MessageBodyX assumed
> this.
> > It uses Class<T> in a way that requires constant
> > @SupressWarnings("unchecked") if the base is MessageBodyX<Object>.
> >
> > To put my money where my mouth is, as it were, I implemented this for
> the
> > Aegis providers. When I did this, I discovered that the JAX-RS runtime
> code
> > couldn't handle generic type providers. When the provider type is,
> say,
> >
> > AegisElementProvider<Book>
> >
> > then implemented interface comes up as MessageBodyReader<T>, not
> > MessageBodyReader<Book>. So it is a TypeVariable, not a class or a
> > ParameterizedType.
> >
> > I fixed the provider selection code to cope, but I didn't write the
> > additionally complex code to look at bounds and insist that if there
> is a
> > bound the type at hand be within it.
> >
>
RE: JAX-RS and generics
Posted by Sergey Beryozkin <sb...@progress.com>.
Hi
I'm concerned it may be not be portable, that is the providers which
work with other JAXRS implementations will end up being unusable in CXF.
You may be right but I've seen the number of providers which implement
MessageBodyX<Object> and then cast them internally. For ex to Feed or
Entry, etc.
Let me ask a question on the jaxrs users list...
Cheers, Sergey
-----Original Message-----
From: Benson Margulies [mailto:bimargulies@gmail.com]
Sent: 06 September 2009 03:15
To: CXF Dev
Subject: Re: JAX-RS and generics
On Sat, Sep 5, 2009 at 1:57 PM, Benson Margulies
<bi...@gmail.com>wrote:
> JAX-RS defines two fundamental interfaces: MessageBodyReader<T> and
> MessageBodyWriter<T>, and providers implement.
>
> I claim that GENERIC providers that work for any object (like those
> corresponding to data bindings) should, themselves, be GENERIC, and
> implement MessageBodyX<T>, not MessageBodyX<Object>.
>
Allow me to modulate this claim. I thought about it some more.
If you want to define a class as 'implements MessageBodyX<Object>',
fine.
However, the right thing to pass to the Class<T> argument will ALWAYS be
Object.class. If you want to cue in the code to the sort of object in
flight, use the Type argument further down the parameter list.
>
> I claim this because the whole API structure of MessageBodyX assumed
this.
> It uses Class<T> in a way that requires constant
> @SupressWarnings("unchecked") if the base is MessageBodyX<Object>.
>
> To put my money where my mouth is, as it were, I implemented this for
the
> Aegis providers. When I did this, I discovered that the JAX-RS runtime
code
> couldn't handle generic type providers. When the provider type is,
say,
>
> AegisElementProvider<Book>
>
> then implemented interface comes up as MessageBodyReader<T>, not
> MessageBodyReader<Book>. So it is a TypeVariable, not a class or a
> ParameterizedType.
>
> I fixed the provider selection code to cope, but I didn't write the
> additionally complex code to look at bounds and insist that if there
is a
> bound the type at hand be within it.
>
Re: JAX-RS and generics
Posted by Benson Margulies <bi...@gmail.com>.
On Sat, Sep 5, 2009 at 1:57 PM, Benson Margulies <bi...@gmail.com>wrote:
> JAX-RS defines two fundamental interfaces: MessageBodyReader<T> and
> MessageBodyWriter<T>, and providers implement.
>
> I claim that GENERIC providers that work for any object (like those
> corresponding to data bindings) should, themselves, be GENERIC, and
> implement MessageBodyX<T>, not MessageBodyX<Object>.
>
Allow me to modulate this claim. I thought about it some more.
If you want to define a class as 'implements MessageBodyX<Object>', fine.
However, the right thing to pass to the Class<T> argument will ALWAYS be
Object.class. If you want to cue in the code to the sort of object in
flight, use the Type argument further down the parameter list.
>
> I claim this because the whole API structure of MessageBodyX assumed this.
> It uses Class<T> in a way that requires constant
> @SupressWarnings("unchecked") if the base is MessageBodyX<Object>.
>
> To put my money where my mouth is, as it were, I implemented this for the
> Aegis providers. When I did this, I discovered that the JAX-RS runtime code
> couldn't handle generic type providers. When the provider type is, say,
>
> AegisElementProvider<Book>
>
> then implemented interface comes up as MessageBodyReader<T>, not
> MessageBodyReader<Book>. So it is a TypeVariable, not a class or a
> ParameterizedType.
>
> I fixed the provider selection code to cope, but I didn't write the
> additionally complex code to look at bounds and insist that if there is a
> bound the type at hand be within it.
>