You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@cxf.apache.org by Dan Check <ch...@catalist.us> on 2009/06/30 18:03:11 UTC

Re: []Re: Handling Collection returns via JAX-RS

Hi Sergey,

Thanks for the response!  I'm glad to hear that this is on roadmap.

...snip...

>> 
>> This works fine -- any java.util.Collection will get wrapped in a
>> <Collection> tag.
> 
> interesting solution...
> 

It was definitely the quickest path for me.  I was impressed that it could
be implemented by overriding a single method, with no additional bad
effects.  Kudos on the code design.

...snip...
 
> I've been thinking about introducing a @CollectionElement annotation and a
> Collections message body writer :
> 
> @CollectionElement(name="persons", ns=http://persons)
> List<Person> getPersons() {}
> 
> MessageBodyWriters get the method annotations passed to it, so :
> 
> Produces("application/xml, text/xml, application/*+xml")
> public class CollectionsProvider implements MessageBodyWriter<Object> {
> 
>    public boolean isWriteable(...) {return true if Collection }
>    public void writeTo(Object obj, Class<?> cls, Type genericType,
> Annotation[] anns, MediaType m,
>           MultivaluedMap<String, Object> headers, OutputStream os)  throws
> IOException {
> 
>           CollectionElement root =
> AnnotationUtils.getAnnotation(CollectionElement.class, anns);
>           os.write(getStartTag(root));
> 
>           // get class of the first element or use genericType
>           Collection<Object> collection = (Collection<Object>)obj;
>           Class<?> elementClass = ...
>           MessageBodyWriter bodyWriter =
> Providers.getMessageBodyWriter(elementClass, ....);
>           for (Object o : collection) {
>                bodyWriter.writeTo(o, ..., os);
>           }
> 
>           os.write(getEndTag(root));
>    }
> 
> }
> 
> I think this should work seamlessly for all XML-based collections, even those
> not relying on JAXB. I was prototyping it when working
> on 0.8 support but for some reasons I decided not to go ahead then - may be
> there's some flaw above but it appears it should work.

That sounds great.  Would this work for JSON providers as well?  Let me know
if you need help testing or anything.

> We can also make the use of @CollectionElement optional - but then we'd need
> to use some default wrapper element like <result> or
> <collection> which may make the actual xml instance fail the validation on the
> other end. Deducing the namespace from the collection
> elements may end up be too brittle/ineffective or unreliable if it's non-JAXB.
> 
> What do you think ? Let me know please...

I agree that the deduced namespacing may be too brittle in a non-JAXB
environment.  The @CollectionElement sounds good to me.  How would all this
work for non-cxf-based clients?  Do you put a Collection of "any" elements
in the xsd and let them figure it out?

Ideally, I'd like it if enunciate could still generate documentation for me.
I recognize that this may not be realistic.

>> (2) Is there a different spot in the chain where I can put this that would
>> change the objects prior to their being consumed by any providers?  The
>> documentation on the JAX-RS filters wasn't totally clear to me on this
>> point.
> 
> Yes, you can do with CXF out interceptors or JAX-RS response filters or even
> with custom invokers, they all have access to the
> response object. With custom invokers you just wrap the response object as you
> need, for ex see :
> http://svn.apache.org/repos/asf/cxf/trunk/systests/src/test/java/org/apache/cx
> f/systest/jaxrs/CustomJAXRSInvoker.java
> 
> so you do
> 
> return new MyWrapper(super.invoke(...));
> 
> with filters you can do :
> 
> public interface MyResponseHandler {
> 
>     Response handleResponse(Message outputMessage,
>                            OperationResourceInfo invokedOperation,
>                            Response response) {
>            // this method is not minimally effective as you can get
> invokedOperation/response from
>            // outputMessage - but you might need to deal directly with the
> message
>            // 1 - using response
>            ResponseBuilder builder = Response.status(response.getStatus());
>            // next, copy response headers into the builder
>            // finally
>            return builder.entity(new MyWrapper(response.getEntity())).build();
> 
>            // 2. Using message - this can be done in CXF out interceptor too
>            MessageContentsList objs =
> MessageContentsList.getContentsList(message);
> if (objs !== null && objs.size() == 1) {
>     Object responseObj = objs.remove(0);
>     obj.add(new MyWrapper(responseObj));
> }
>     }
> 
> }

I will give that a shot.  It looks like the filters will get me JSON support
as well.  Thanks again for the detailed response.

Thanks,
Dan  



-----
CONFIDENTIALITY NOTICE: The information contained in this message
may be privileged and confidential and protected from disclosure.
 If the reader of this message is not the intended recipient, or
responsible for delivering it to the intended recipient, please
be advised that any distribution, dissemination, copying or other
transmission or use of the information contained in this message
or its enclosed attachments is strictly prohibited.  Please
notify us immediately if you have received this message in error
by replying to the sender of the message and deleting it from
your computer.  Thank you. 
 


Re: []Re: Handling Collection returns via JAX-RS

Posted by Sergey Beryozkin <sb...@progress.com>.
Hi Dan

>>
>> @CollectionElement(name="persons", ns=http://persons)
>> List<Person> getPersons() {}
>>
>> MessageBodyWriters get the method annotations passed to it, so :
>>
>> Produces("application/xml, text/xml, application/*+xml")
>> public class CollectionsProvider implements MessageBodyWriter<Object> {
>>
>>    public boolean isWriteable(...) {return true if Collection }
>>    public void writeTo(Object obj, Class<?> cls, Type genericType,
>> Annotation[] anns, MediaType m,
>>           MultivaluedMap<String, Object> headers, OutputStream os)  throws
>> IOException {
>>
>>           CollectionElement root =
>> AnnotationUtils.getAnnotation(CollectionElement.class, anns);
>>           os.write(getStartTag(root));
>>
>>           // get class of the first element or use genericType
>>           Collection<Object> collection = (Collection<Object>)obj;
>>           Class<?> elementClass = ...
>>           MessageBodyWriter bodyWriter =
>> Providers.getMessageBodyWriter(elementClass, ....);
>>           for (Object o : collection) {
>>                bodyWriter.writeTo(o, ..., os);
>>           }
>>
>>           os.write(getEndTag(root));
>>    }
>>
>> }
>>
>> I think this should work seamlessly for all XML-based collections, even those
>> not relying on JAXB. I was prototyping it when working
>> on 0.8 support but for some reasons I decided not to go ahead then - may be
>> there's some flaw above but it appears it should work.
>
> That sounds great.  Would this work for JSON providers as well?  Let me know
> if you need help testing or anything.

Hmm... Probably not. Unless we can support multiple CollectionElement annotations on a method, one per media type, or may be use a 
single one :

@CollectionElement({"{http://people}people", "application/xml"},
                                  "jsonCollectionRoot", "application/json"})

so CollectionsProvider will output the media type specific start/end tags/sequences in its writeTo... It's a bit hairy though in 
that with XMl one might want to say if it's a default namespace or not, and if nor then what prefix to use, so may be we should have 
@CollectionXmlElement for XML based collections and @CollectionRoot for all other ones, so

@CollectionXmlElement({"{http://people}", "people", "prefix:"}
@CollectionRoot({""jsonCollectionStart"", ""jsonCollectionEnd"", ""application/json"",
                            {""textCollectionStart"", ""textCollectionEnd"", "separator", "text/plain""}


>
>> We can also make the use of @CollectionElement optional - but then we'd need
>> to use some default wrapper element like <result> or
>> <collection> which may make the actual xml instance fail the validation on the
>> other end. Deducing the namespace from the collection
>> elements may end up be too brittle/ineffective or unreliable if it's non-JAXB.
>>
>> What do you think ? Let me know please...
>
> I agree that the deduced namespacing may be too brittle in a non-JAXB
> environment.  The @CollectionElement sounds good to me.  How would all this
> work for non-cxf-based clients?  Do you put a Collection of "any" elements
> in the xsd and let them figure it out?

Not sure actually : I think the wadl generation utility may have to be  smart enough to put some information into wadl for clients 
to figure it out...

>
> Ideally, I'd like it if enunciate could still generate documentation for me.
> I recognize that this may not be realistic.

can you explain a bit more ?

>>
>> with filters you can do :
>>
>> public interface MyResponseHandler {
>>
>>     Response handleResponse(Message outputMessage,
>>                            OperationResourceInfo invokedOperation,
>>                            Response response) {
>>            // this method is not minimally effective as you can get
>> invokedOperation/response from
>>            // outputMessage - but you might need to deal directly with the
>> message
>>            // 1 - using response
>>            ResponseBuilder builder = Response.status(response.getStatus());
>>            // next, copy response headers into the builder
>>            // finally
>>            return builder.entity(new MyWrapper(response.getEntity())).build();
>>
>>            // 2. Using message - this can be done in CXF out interceptor too
>>            MessageContentsList objs =
>> MessageContentsList.getContentsList(message);
>> if (objs !== null && objs.size() == 1) {
>>     Object responseObj = objs.remove(0);
>>     obj.add(new MyWrapper(responseObj));
>> }
>>     }
>>
>> }
>
> I will give that a shot.  It looks like the filters will get me JSON support
> as well.  Thanks again for the detailed response.

give a try please

thanks, Sergey

>
> Thanks,
> Dan
>
>
>
> -----
> CONFIDENTIALITY NOTICE: The information contained in this message
> may be privileged and confidential and protected from disclosure.
> If the reader of this message is not the intended recipient, or
> responsible for delivering it to the intended recipient, please
> be advised that any distribution, dissemination, copying or other
> transmission or use of the information contained in this message
> or its enclosed attachments is strictly prohibited.  Please
> notify us immediately if you have received this message in error
> by replying to the sender of the message and deleting it from
> your computer.  Thank you.
>
>