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
>