You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@cxf.apache.org by Bernhard Groll <b....@geniusbytes.com> on 2010/07/25 20:25:23 UTC

REST problem: unmarshalling JAXB from multipart attachment

Hello everybody,

I’m very new to developing REST services with CXF and now I’ve run into
a problem involving JAXB unmarshalling from multiparts.

For test purposes I use a very simple bean for the data transfer:

    @XmlRootElement
    public class WrappedString {

        private String testString;

        public String getTestString() {
            return testString;
        }

        public void setTestString(String testString) {
            this.testString = testString;
        }
    }

This is the method on the server:

    @POST
    @Path("test")
    @Consumes("multipart/mixed")
    @Produces("text/plain")
    public String unpackWrappedString(MultipartBody body) {
        Attachment rootAttachment = body.getRootAttachment();
        if (rootAttachment == null) {
            LOGGER.error("No root attachment found");
        }
        Object content = rootAttachment.getObject();
        if (content == null) {
            
            // This is what happens!

            LOGGER.error("Couldn't get an object from the root attachment");
        }
        WrappedString wrappedString = null;
        try {
            wrappedString = (WrappedString) content;
        } catch (ClassCastException e) {
            LOGGER.error("Couldn't cast to WrappedString");
        }
        return wrappedString.getTestString();
    }

This is the test call:

    @Test
    public void testUnpackWrappedString() {
        final String testString = "Hello world";

        WebClient client = WebClient.create(BASE_URL);
        WrappedString wrappedString = new WrappedString();
        wrappedString.setTestString(testString);
        MultipartBody body = new MultipartBody(new Attachment("root",
                "text/xml", wrappedString));

        String responseString = client.path("test").type("multipart/mixed")
                .accept("text/plain").post(body, String.class);

        Assert.assertEquals(testString, responseString);
    }

This is the HTTP message sent:

    POST /test-service/test HTTP/1.1
    Content-Type: multipart/mixed; type="text/xml";
            boundary="uuid:e4e3a133-4fd2-4f9a-a657-1bece83d19e1";
            start="<root>"; start-info="text/xml"
    Accept: text/plain
    User-Agent: Apache CXF 2.2.9
    Cache-Control: no-cache
    Pragma: no-cache
    Host: localhost:8084
    Connection: keep-alive
    Content-Length: 297


    --uuid:e4e3a133-4fd2-4f9a-a657-1bece83d19e1
    Content-Type: text/xml
    Content-Transfer-Encoding: binary
    Content-ID: <root>

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?><wrappedString>
            <testString>Hello world</testString></wrappedString>
    --uuid:e4e3a133-4fd2-4f9a-a657-1bece83d19e1--

As mentioned in the server code above what happens is that the method
getObject() returns null instead of the WrappedString object. This
approach works fine if the attachment contains e.g. a File object (not
using JAXB obviously). Also unmarshalling a WrappedString object works
if it’s the sole HTTP body, i.e. not inside a multipart attachment.

I would be grateful for any help in this matter.

The CXF version I’m using is 2.2.9, the JDK version is 1.6.0_20, the
service is deployed on a Tomcat 6.0.26.

Best regards,
Bernhard

Re: REST problem: unmarshalling JAXB from multipart attachment

Posted by Bernhard Groll <b....@geniusbytes.com>.
Hi Sergey,

you wrote (2010-09-03T16:22:08+0100):

> Just spotted this email, thought I'd update that I added the other day
> getObject(Class<T> cls) to Attachment and getAttachmentObject(String
> partName, Class<T> cls) to MultipartBody, the latter method can be
> handy in that one does not need to worry about the ordering of
> individual parts as long as one knows the required part's id

Very nice! I have avoided the problem by rethinking the design of my
webservice, which is now much cleaner whithout any need for multiparts.
But I bet the new methods will come in handy for me or somebody else in
the future.

Bye,
Bernhard

Re: REST problem: unmarshalling JAXB from multipart attachment

Posted by Sergey Beryozkin <sb...@gmail.com>.
Just spotted this email, thought I'd update that I added the other day
getObject(Class<T> cls) to Attachment and
getAttachmentObject(String partName, Class<T> cls) to MultipartBody, the
latter method can be handy in that one does not need to worry about the
ordering of individual parts as long as one knows the required part's id

thanks, Sergey

On Thu, Jul 29, 2010 at 6:40 PM, Sergey Beryozkin <sb...@gmail.com>wrote:

> Hi
>
> On Thu, Jul 29, 2010 at 2:12 PM, Bernhard Groll <b....@geniusbytes.com>wrote:
>
>> Hi Sergey,
>>
>> thanks for your explanations and please excuse my late reply.
>>
>> no problems - I'm replying with delays myself
>
>
>> You wrote (2010-07-26T18:21+0100):
>> > [...] the JAXRS providers which might be able to deal with
>> > specific parts are not used. [...]
>> > Enhancing Attachement.getObject() so that a suitable provider could
>> > be found if it's not already been done could be a good enhancement
>> > though.
>>
>> My problem obviously was the assumption that it would already work this
>> way and I’m really surprised that’s not the case.
>
>
> Well, by the time the method has been invoked by the JAXRS runtime the
> providers have already been checked, and the job of MultipartProvider is to
> give users access to individual parts and let them parse XML bits if such
> exist, deal with binary parts whichever way they prefer.
>
>
>
>> Handling the bodies of
>> multipart attachments exactly like the body of a non-multipart message
>> would seem the obvious choice to me.
>>
>>
> This is what happening when parameters represent individual parts.
> But as I said, enhancing MultipartProvider a bit would make sense.
>
>
>> Is there a way to manually call a MessageBodyReader to deserialize an
>> attachment?
>>
>> You can have
>
> @Context Providers providers;
> in your class and then ask 'providers' to get you a MessageBodyReader
> capable of reading a given part.
>
>
>
>
>> > If you expect a fixed number of parts, say the root providing some
>> > info about the (2nd) binary part, then you could have say
>> >
>> > unpack(@Multipart("root", text/xml) MixedString str,
>> > @Multipart("2ndPart") InputStream is) {}
>>
>> Alas I have to deal with a varying number of parts so it seems I will
>> have to use MultipartBody for at least some of them.
>>
>> Anyway, seeing that my original approach couldn’t work I can now in good
>> conscience look for a different solution.
>>
>>
> If you have say a single XML part only or any fixed number of XMl parts
> followed by a variable number of
> some other parts, then you can do :
>
> unpack(@Multipart("root", text/xml) MixedString str, List<Attachment> atts)
> {
> }
>
> in the list the 1st entry will represent the already read MixedString, and
> others will point to some binary ones
>
> cheers, Sergey
>
>
>> Thanks again.
>>
>> Bye,
>> Bernhard
>>
>
>

Re: REST problem: unmarshalling JAXB from multipart attachment

Posted by Sergey Beryozkin <sb...@gmail.com>.
Hi

On Thu, Jul 29, 2010 at 2:12 PM, Bernhard Groll <b....@geniusbytes.com>wrote:

> Hi Sergey,
>
> thanks for your explanations and please excuse my late reply.
>
> no problems - I'm replying with delays myself


> You wrote (2010-07-26T18:21+0100):
> > [...] the JAXRS providers which might be able to deal with
> > specific parts are not used. [...]
> > Enhancing Attachement.getObject() so that a suitable provider could
> > be found if it's not already been done could be a good enhancement
> > though.
>
> My problem obviously was the assumption that it would already work this
> way and I’m really surprised that’s not the case.


Well, by the time the method has been invoked by the JAXRS runtime the
providers have already been checked, and the job of MultipartProvider is to
give users access to individual parts and let them parse XML bits if such
exist, deal with binary parts whichever way they prefer.



> Handling the bodies of
> multipart attachments exactly like the body of a non-multipart message
> would seem the obvious choice to me.
>
>
This is what happening when parameters represent individual parts.
But as I said, enhancing MultipartProvider a bit would make sense.


> Is there a way to manually call a MessageBodyReader to deserialize an
> attachment?
>
> You can have

@Context Providers providers;
in your class and then ask 'providers' to get you a MessageBodyReader
capable of reading a given part.




> > If you expect a fixed number of parts, say the root providing some
> > info about the (2nd) binary part, then you could have say
> >
> > unpack(@Multipart("root", text/xml) MixedString str,
> > @Multipart("2ndPart") InputStream is) {}
>
> Alas I have to deal with a varying number of parts so it seems I will
> have to use MultipartBody for at least some of them.
>
> Anyway, seeing that my original approach couldn’t work I can now in good
> conscience look for a different solution.
>
>
If you have say a single XML part only or any fixed number of XMl parts
followed by a variable number of
some other parts, then you can do :

unpack(@Multipart("root", text/xml) MixedString str, List<Attachment> atts)
{
}

in the list the 1st entry will represent the already read MixedString, and
others will point to some binary ones

cheers, Sergey


> Thanks again.
>
> Bye,
> Bernhard
>

Re: REST problem: unmarshalling JAXB from multipart attachment

Posted by Bernhard Groll <b....@geniusbytes.com>.
Hi Sergey,

thanks for your explanations and please excuse my late reply.

You wrote (2010-07-26T18:21+0100):
> [...] the JAXRS providers which might be able to deal with
> specific parts are not used. [...]
> Enhancing Attachement.getObject() so that a suitable provider could
> be found if it's not already been done could be a good enhancement
> though.

My problem obviously was the assumption that it would already work this
way and I’m really surprised that’s not the case. Handling the bodies of
multipart attachments exactly like the body of a non-multipart message
would seem the obvious choice to me.

Is there a way to manually call a MessageBodyReader to deserialize an
attachment?

> If you expect a fixed number of parts, say the root providing some
> info about the (2nd) binary part, then you could have say
> 
> unpack(@Multipart("root", text/xml) MixedString str,
> @Multipart("2ndPart") InputStream is) {}

Alas I have to deal with a varying number of parts so it seems I will
have to use MultipartBody for at least some of them.

Anyway, seeing that my original approach couldn’t work I can now in good
conscience look for a different solution.

Thanks again.

Bye,
Bernhard

Re: REST problem: unmarshalling JAXB from multipart attachment

Posted by Sergey Beryozkin <sb...@gmail.com>.
Hi

thanks for the detailed explanation of the issue you're seeing.
At the moment, using MultipartBody/Attachments explicitly makes sense in
cases when users would like to deserialize the individual parts manually,
the JAXRS providers which might be able to deal with specific parts are not
used.

If you add a WrappedString directly into a signature, instead of
MultipartBody, then it should work...
If you expect a fixed number of parts, say the root providing some info
about the (2nd) binary part, then you could have say

unpack(@Multipart("root", text/xml) MixedString str, @Multipart("2ndPart")
InputStream is) {}

Enhancing Attachement.getObject() so that a suitable provider could be found
if it's not already been done could be a good enhancement though.


cheers, Sergey

On Sun, Jul 25, 2010 at 7:25 PM, Bernhard Groll <b....@geniusbytes.com>wrote:

> Hello everybody,
>
> I’m very new to developing REST services with CXF and now I’ve run into
> a problem involving JAXB unmarshalling from multiparts.
>
> For test purposes I use a very simple bean for the data transfer:
>
>    @XmlRootElement
>    public class WrappedString {
>
>        private String testString;
>
>        public String getTestString() {
>            return testString;
>        }
>
>        public void setTestString(String testString) {
>            this.testString = testString;
>        }
>    }
>
> This is the method on the server:
>
>    @POST
>    @Path("test")
>    @Consumes("multipart/mixed")
>    @Produces("text/plain")
>    public String unpackWrappedString(MultipartBody body) {
>        Attachment rootAttachment = body.getRootAttachment();
>        if (rootAttachment == null) {
>            LOGGER.error("No root attachment found");
>        }
>        Object content = rootAttachment.getObject();
>        if (content == null) {
>
>            // This is what happens!
>
>            LOGGER.error("Couldn't get an object from the root attachment");
>        }
>        WrappedString wrappedString = null;
>        try {
>            wrappedString = (WrappedString) content;
>        } catch (ClassCastException e) {
>            LOGGER.error("Couldn't cast to WrappedString");
>        }
>        return wrappedString.getTestString();
>    }
>
> This is the test call:
>
>    @Test
>    public void testUnpackWrappedString() {
>        final String testString = "Hello world";
>
>        WebClient client = WebClient.create(BASE_URL);
>        WrappedString wrappedString = new WrappedString();
>        wrappedString.setTestString(testString);
>        MultipartBody body = new MultipartBody(new Attachment("root",
>                "text/xml", wrappedString));
>
>        String responseString = client.path("test").type("multipart/mixed")
>                .accept("text/plain").post(body, String.class);
>
>        Assert.assertEquals(testString, responseString);
>    }
>
> This is the HTTP message sent:
>
>    POST /test-service/test HTTP/1.1
>    Content-Type: multipart/mixed; type="text/xml";
>            boundary="uuid:e4e3a133-4fd2-4f9a-a657-1bece83d19e1";
>            start="<root>"; start-info="text/xml"
>    Accept: text/plain
>    User-Agent: Apache CXF 2.2.9
>    Cache-Control: no-cache
>    Pragma: no-cache
>    Host: localhost:8084
>    Connection: keep-alive
>    Content-Length: 297
>
>
>    --uuid:e4e3a133-4fd2-4f9a-a657-1bece83d19e1
>    Content-Type: text/xml
>    Content-Transfer-Encoding: binary
>    Content-ID: <root>
>
>    <?xml version="1.0" encoding="UTF-8" standalone="yes"?><wrappedString>
>            <testString>Hello world</testString></wrappedString>
>    --uuid:e4e3a133-4fd2-4f9a-a657-1bece83d19e1--
>
> As mentioned in the server code above what happens is that the method
> getObject() returns null instead of the WrappedString object. This
> approach works fine if the attachment contains e.g. a File object (not
> using JAXB obviously). Also unmarshalling a WrappedString object works
> if it’s the sole HTTP body, i.e. not inside a multipart attachment.
>
> I would be grateful for any help in this matter.
>
> The CXF version I’m using is 2.2.9, the JDK version is 1.6.0_20, the
> service is deployed on a Tomcat 6.0.26.
>
> Best regards,
> Bernhard
>