You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@cxf.apache.org by Simon Chen <si...@gmail.com> on 2011/04/15 22:02:17 UTC

handling polymorphism in @POST

Hi all,

I've been playing with building a REST web service to handle
polymorphism... It's a long post, but bear with me :-)

In particular, I may have:

@Path("/")
class WebStore {
  @POST
  @Path("/customers/")
  Response addCustomer(Customer c) {
    ...
  }
}

But I have a base class of Customer, but also inherited classes of
AwesomeCustomer, and SuperAwesomeCustomer. Things get more complicated
when EMF kicks in, where I actually have a Customer interface and
CustomerImpl class.

The previous declaration doesn't work when I post a customer XML snippet, say:
<customer><name>simon</name></customer>

Because Customer is an interface, so cannot be annotated with
@XmlRootElement. As a result, the error of "no message body reader can
be found" is raised...

If we change the POST function this way, it can work:
  @POST
  @Path("/customers/")
  Response addCustomer(CustomerImpl c) {
    ...
  }

But, this breaks again, when I add the following:
  @POST
  @Path("/customers/")
  Response addCustomer(CustomerImpl c) {
    ...
  }
  @POST
  @Path("/customers/")
  Response addCustomer(AwesomeCustomerImpl c) {
    ...
  }
  @POST
  @Path("/customers/")
  Response addCustomer(SuperAwesomeCustomerImpl c) {
    ...
  }

Here, if I post a SuperAwesomeCustomerImpl object to "/customers", the
ws would find all the functions that can handle "POST to /customers",
which will include all three functions above. However, in
"src/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java",
public static OperationResourceInfo findTargetMethod(), we have:

        if (!candidateList.isEmpty()) {
            Map.Entry<OperationResourceInfo, MultivaluedMap<String,
String>> firstEntry =
                candidateList.entrySet().iterator().next();
            //---------> This only looks at the first function that
matches, while not look at the class hierarchy...
            values.clear();
            values.putAll(firstEntry.getValue());
            OperationResourceInfo ori = firstEntry.getKey();


Hopefully, I am not overwhelming everyone with too much information.
But is there a fix to this problem? I guess maybe add some class
hierarchy-awareness to findTargetMethod()?

Thanks!
-Simon

Re: handling polymorphism in @POST

Posted by Simon Chen <si...@gmail.com>.
This is what EMF generates:

public class AwesomeCustomerImpl extends CustomerImpl implements
AwesomeCustomer {...}
public class CustomerImpl extends EObjectImpl implements Customer {...}

public interface AwesomeCustomer extends Customer {...}
public interface Customer extends EObject {...}

On Mon, Apr 18, 2011 at 9:32 AM, Sergey Beryozkin <sb...@gmail.com> wrote:
> Simon, does AwesomeCustomerImpl extends CustomerImpl, or do you have both
> AwesomeCustomerImpl and CustomerImpl implementing Customer ?
>
> If it is the former then returning and accepting CustomerImpl should work
> but in the method accepting CustomerImpl you'll need to further cast to
> AwesomeCustomerImpl or SuperAwesomeCustomerImpl. CXF will add xsi:type to
> serialized data, see
> http://cxf.apache.org/docs/jax-rs-data-bindings.html#JAX-RSDataBindings-AutomaticJAXBElementconversionduringserialization
>
> Cheers, Sergey
>
> On Mon, Apr 18, 2011 at 1:53 PM, Sergey Beryozkin <sb...@gmail.com>wrote:
>
>> Hi
>>
>>   On Sat, Apr 16, 2011 at 4:40 AM, joerg@kiegeland.com <
>> joerg@kiegeland.com> wrote:
>>
>>>
>>>> Can you send me a pointer to some examples?
>>>>
>>>
>>>
>>>  @Path("/")
>>>  class WebStore {
>>>    @POST
>>>    @Path("/customers/")
>>>    Response addCustomer(@XmlJavaTypeAdapter(CustomerImplAdapter.class)
>>> Customer c) {
>>>      ...
>>>    }
>>>  }
>>>
>>>
>>>
>>>
>>>
>>> public class CustomerImplAdapter extends XmlAdapter<CustomerImpl,
>>> Customer> {
>>>
>>>        @Override
>>>        public Customer unmarshal(CustomerImpl v) throws Exception {
>>>                return v;
>>>        }
>>>
>>>        @Override
>>>        public CustomerImpl marshal(Customer v) throws Exception {
>>>                return (CustomerImpl) v;
>>>        }
>>> }
>>>
>>>
>>> ... and annotate CustomerImpl with @XmlRootElement
>>>
>>> Alternatively, instead of adding
>>> @XmlJavaTypeAdapter(CustomerImplAdapter.class) to the parameter, you can add
>>> it to Customer inteface directly.
>>>
>>>
>> But how can we deal here with returning not only CustomerImpl but also
>> AwesomeCustomerImpl ?
>> Usually we can rely on xsi:type but this info is not available at the
>> adapter level
>>
>> Cheers, Sergey
>>
>>
>>> Cheers,
>>> Joerg
>>>
>>
>

Re: handling polymorphism in @POST

Posted by Sergey Beryozkin <sb...@gmail.com>.
Simon, does AwesomeCustomerImpl extends CustomerImpl, or do you have both
AwesomeCustomerImpl and CustomerImpl implementing Customer ?

If it is the former then returning and accepting CustomerImpl should work
but in the method accepting CustomerImpl you'll need to further cast to
AwesomeCustomerImpl or SuperAwesomeCustomerImpl. CXF will add xsi:type to
serialized data, see
http://cxf.apache.org/docs/jax-rs-data-bindings.html#JAX-RSDataBindings-AutomaticJAXBElementconversionduringserialization

Cheers, Sergey

On Mon, Apr 18, 2011 at 1:53 PM, Sergey Beryozkin <sb...@gmail.com>wrote:

> Hi
>
>   On Sat, Apr 16, 2011 at 4:40 AM, joerg@kiegeland.com <
> joerg@kiegeland.com> wrote:
>
>>
>>> Can you send me a pointer to some examples?
>>>
>>
>>
>>  @Path("/")
>>  class WebStore {
>>    @POST
>>    @Path("/customers/")
>>    Response addCustomer(@XmlJavaTypeAdapter(CustomerImplAdapter.class)
>> Customer c) {
>>      ...
>>    }
>>  }
>>
>>
>>
>>
>>
>> public class CustomerImplAdapter extends XmlAdapter<CustomerImpl,
>> Customer> {
>>
>>        @Override
>>        public Customer unmarshal(CustomerImpl v) throws Exception {
>>                return v;
>>        }
>>
>>        @Override
>>        public CustomerImpl marshal(Customer v) throws Exception {
>>                return (CustomerImpl) v;
>>        }
>> }
>>
>>
>> ... and annotate CustomerImpl with @XmlRootElement
>>
>> Alternatively, instead of adding
>> @XmlJavaTypeAdapter(CustomerImplAdapter.class) to the parameter, you can add
>> it to Customer inteface directly.
>>
>>
> But how can we deal here with returning not only CustomerImpl but also
> AwesomeCustomerImpl ?
> Usually we can rely on xsi:type but this info is not available at the
> adapter level
>
> Cheers, Sergey
>
>
>> Cheers,
>> Joerg
>>
>

Re: handling polymorphism in @POST

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

On Sat, Apr 16, 2011 at 4:40 AM, joerg@kiegeland.com <jo...@kiegeland.com>wrote:

>
>> Can you send me a pointer to some examples?
>>
>
>
>  @Path("/")
>  class WebStore {
>    @POST
>    @Path("/customers/")
>    Response addCustomer(@XmlJavaTypeAdapter(CustomerImplAdapter.class)
> Customer c) {
>      ...
>    }
>  }
>
>
>
>
>
> public class CustomerImplAdapter extends XmlAdapter<CustomerImpl, Customer>
> {
>
>        @Override
>        public Customer unmarshal(CustomerImpl v) throws Exception {
>                return v;
>        }
>
>        @Override
>        public CustomerImpl marshal(Customer v) throws Exception {
>                return (CustomerImpl) v;
>        }
> }
>
>
> ... and annotate CustomerImpl with @XmlRootElement
>
> Alternatively, instead of adding
> @XmlJavaTypeAdapter(CustomerImplAdapter.class) to the parameter, you can add
> it to Customer inteface directly.
>
>
But how can we deal here with returning not only CustomerImpl but also
AwesomeCustomerImpl ?
Usually we can rely on xsi:type but this info is not available at the
adapter level

Cheers, Sergey


> Cheers,
> Joerg
>

Re: handling polymorphism in @POST

Posted by "joerg@kiegeland.com" <jo...@kiegeland.com>.
>
> Can you send me a pointer to some examples?


  @Path("/")
  class WebStore {
     @POST
     @Path("/customers/")
     Response addCustomer(@XmlJavaTypeAdapter(CustomerImplAdapter.class) 
Customer c) {
       ...
     }
  }





public class CustomerImplAdapter extends XmlAdapter<CustomerImpl, 
Customer> {

	@Override
	public Customer unmarshal(CustomerImpl v) throws Exception {
		return v;
	}

	@Override
	public CustomerImpl marshal(Customer v) throws Exception {
		return (CustomerImpl) v;
	}
}


... and annotate CustomerImpl with @XmlRootElement

Alternatively, instead of adding 
@XmlJavaTypeAdapter(CustomerImplAdapter.class) to the parameter, you can 
add it to Customer inteface directly.

Cheers,
Joerg

Re: handling polymorphism in @POST

Posted by Simon Chen <si...@gmail.com>.
Hi Joerg,

Can you send me a pointer to some examples?

Thanks.
-Simon

On Friday, April 15, 2011, joerg@kiegeland.com <jo...@kiegeland.com> wrote:
> Hi Simon,
>
>
> Because Customer is an interface, so cannot be annotated with
> @XmlRootElement. As a result, the error of "no message body reader can
> be found" is raised...
>
>
> By using XmlAdapters, you can cast an EMF interface to the EMF impl class. So actually none of your REST method signatures will ever need to refer to some Impl class.
>
> Cheers,
> Joerg
>

Re: handling polymorphism in @POST

Posted by "joerg@kiegeland.com" <jo...@kiegeland.com>.
Hi Simon,

> Because Customer is an interface, so cannot be annotated with
> @XmlRootElement. As a result, the error of "no message body reader can
> be found" is raised...

By using XmlAdapters, you can cast an EMF interface to the EMF impl 
class. So actually none of your REST method signatures will ever need to 
refer to some Impl class.

Cheers,
Joerg

Re: handling polymorphism in @POST

Posted by Simon Chen <si...@gmail.com>.
It has been a few dreadful days :-(

I eventually get everything working. The first thing I did was to get
rid of the interfaces in EMF genmodel. Essentially, you can change a
flag to suppress separating interfaces and implementation classes.

In addition to that, using @XmlSeeAlso helps not only in marshalling
but also unmarshalling.

Finally, do not use @XmlMixed together with @XmlElement and
@XmlElements. I added this flag accidentally, and it causes my program
to crash without meaningful debug/output messages --- took me at least
a full day to realize...

Haven't got a chance to try XmlJavaTypeAdapters, will do so later...

Thanks everyone!
-Simon

On Tue, Apr 19, 2011 at 7:15 AM, Sergey Beryozkin <sb...@gmail.com> wrote:
> HI Simon
>
> On Tue, Apr 19, 2011 at 1:26 AM, Simon Chen <si...@gmail.com> wrote:
>> (Sorry Sergey for the dup message, I forgot to reply to all last time...)
>>
>
> No problems, we'd just update the list aftewards otherwise:-)
>
>> It seems that I get all sorts of weird issues with EMF+JAXB.
>>
>> For example, if I have a base class of Customer and two inherited
>> classes of GoodCustomer and BadCustomer.
>> Then I have a WebStore as a
>> container for a list of Customer objects. The mind-twisting (for me at
>> least) problem is that if Customer is not defined as abstract in the
>> ecore model, I cannot even create the JAXBContext for WebStore (well,
>> actually WebStoreImpl for EMF), with a NoSuchElementException. If I
>> change Customer to be abstract, then at least it works in this aspect.
>>
>> I tried plain java classes (hand-written, without interface/impl,
>> without proxy), it works no matter Customer is abstract or not...
>
> What is WebStore, is it a root resource class or JAXB bean which your
> JAX-RS service returns/updates ?
> May be you have to add @XmlJavaTypeAdapter to WebStore field which
> keeps the list of Customer interface impls...
>
> Cheers, Sergey
>
>>
>> -Simon
>>
>> On Mon, Apr 18, 2011 at 11:02 AM, Sergey Beryozkin <sb...@gmail.com> wrote:
>>> Sorry for the noise, I think I've got totally confused - no @XmlSeeAlso has
>>> to be added to subclasses. But, I have a test where
>>>
>>> SuperBook extends Book, Book has @XmlSeeAlso pointing to SuperBook, and
>>> without using JAXBElement (internally), xsi:type is not written when Book is
>>> returned, however, no extra classes or jaxb.index is used, so that might
>>> explain why...
>>>
>>> Cheers, Sergey
>>>
>>>
>>>
>>> On Mon, Apr 18, 2011 at 3:57 PM, Sergey Beryozkin <sb...@gmail.com>
>>> wrote:
>>>>
>>>> That should work with adapters too, with @XmlSeeAlso.
>>>>
>>>> Actually, I remember now what adding a jaxbElementClassNames property
>>>> (containing a CustomerImpl full class name only in this particular case) can
>>>> do, it may help with avoiding adding @XmlSeeAlso to subclasses, I see a test
>>>> where only a base class has @XmlSeeAlso - somehow JAXBElement figures it out
>>>> that xsi:type has to be added...
>>>> Adding jaxb.index  extra classes for JAXBContext to include them should
>>>> also help...
>>>>
>>>> thanks, Sergey
>>>>
>>>> On Mon, Apr 18, 2011 at 3:36 PM, Daniel Kulp <dk...@apache.org> wrote:
>>>>>
>>>>> When dealing with polymorphism with JAXB, one thing that is often
>>>>> required is
>>>>> to add XmlSeeAlso annotations all over the place.  In particular, on the
>>>>> base
>>>>> class, it's  useful to have XmlSeeAlso point at the potential subclasses.
>>>>> This allows the jaxb runtime to find the subclasses to instantiate and
>>>>> will
>>>>> usually result in the proper xsi:type attributes written out and such.
>>>>>
>>>>> If you haven't already tried it, I'd definitely suggest adding the
>>>>> annotation
>>>>> and seeing if that helps.
>>>>>
>>>>> Dan
>>>>>
>>>>>
>>>>> On Friday 15 April 2011 4:02:17 PM Simon Chen wrote:
>>>>> > Hi all,
>>>>> >
>>>>> > I've been playing with building a REST web service to handle
>>>>> > polymorphism... It's a long post, but bear with me :-)
>>>>> >
>>>>> > In particular, I may have:
>>>>> >
>>>>> > @Path("/")
>>>>> > class WebStore {
>>>>> >   @POST
>>>>> >   @Path("/customers/")
>>>>> >   Response addCustomer(Customer c) {
>>>>> >     ...
>>>>> >   }
>>>>> > }
>>>>> >
>>>>> > But I have a base class of Customer, but also inherited classes of
>>>>> > AwesomeCustomer, and SuperAwesomeCustomer. Things get more complicated
>>>>> > when EMF kicks in, where I actually have a Customer interface and
>>>>> > CustomerImpl class.
>>>>> >
>>>>> > The previous declaration doesn't work when I post a customer XML
>>>>> > snippet,
>>>>> > say: <customer><name>simon</name></customer>
>>>>> >
>>>>> > Because Customer is an interface, so cannot be annotated with
>>>>> > @XmlRootElement. As a result, the error of "no message body reader can
>>>>> > be found" is raised...
>>>>> >
>>>>> > If we change the POST function this way, it can work:
>>>>> >   @POST
>>>>> >   @Path("/customers/")
>>>>> >   Response addCustomer(CustomerImpl c) {
>>>>> >     ...
>>>>> >   }
>>>>> >
>>>>> > But, this breaks again, when I add the following:
>>>>> >   @POST
>>>>> >   @Path("/customers/")
>>>>> >   Response addCustomer(CustomerImpl c) {
>>>>> >     ...
>>>>> >   }
>>>>> >   @POST
>>>>> >   @Path("/customers/")
>>>>> >   Response addCustomer(AwesomeCustomerImpl c) {
>>>>> >     ...
>>>>> >   }
>>>>> >   @POST
>>>>> >   @Path("/customers/")
>>>>> >   Response addCustomer(SuperAwesomeCustomerImpl c) {
>>>>> >     ...
>>>>> >   }
>>>>> >
>>>>> > Here, if I post a SuperAwesomeCustomerImpl object to "/customers", the
>>>>> > ws would find all the functions that can handle "POST to /customers",
>>>>> > which will include all three functions above. However, in
>>>>> >
>>>>> > "src/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.
>>>>> > java", public static OperationResourceInfo findTargetMethod(), we have:
>>>>> >
>>>>> >         if (!candidateList.isEmpty()) {
>>>>> >             Map.Entry<OperationResourceInfo, MultivaluedMap<String,
>>>>> > String>> firstEntry =
>>>>> >                 candidateList.entrySet().iterator().next();
>>>>> >             //---------> This only looks at the first function that
>>>>> > matches, while not look at the class hierarchy...
>>>>> >             values.clear();
>>>>> >             values.putAll(firstEntry.getValue());
>>>>> >             OperationResourceInfo ori = firstEntry.getKey();
>>>>> >
>>>>> >
>>>>> > Hopefully, I am not overwhelming everyone with too much information.
>>>>> > But is there a fix to this problem? I guess maybe add some class
>>>>> > hierarchy-awareness to findTargetMethod()?
>>>>> >
>>>>> > Thanks!
>>>>> > -Simon
>>>>>
>>>>> --
>>>>> Daniel Kulp
>>>>> dkulp@apache.org
>>>>> http://dankulp.com/blog
>>>>> Talend - http://www.talend.com
>>>>
>>>>
>>>> Application Integration Division of Talend
>>>> http://sberyozkin.blogspot.com
>>>
>>>
>>>
>>> --
>>> Sergey Beryozkin
>>>
>>> Application Integration Division of Talend
>>> http://sberyozkin.blogspot.com
>>>
>>
>

Re: handling polymorphism in @POST

Posted by Sergey Beryozkin <sb...@gmail.com>.
HI Simon

On Tue, Apr 19, 2011 at 1:26 AM, Simon Chen <si...@gmail.com> wrote:
> (Sorry Sergey for the dup message, I forgot to reply to all last time...)
>

No problems, we'd just update the list aftewards otherwise:-)

> It seems that I get all sorts of weird issues with EMF+JAXB.
>
> For example, if I have a base class of Customer and two inherited
> classes of GoodCustomer and BadCustomer.
> Then I have a WebStore as a
> container for a list of Customer objects. The mind-twisting (for me at
> least) problem is that if Customer is not defined as abstract in the
> ecore model, I cannot even create the JAXBContext for WebStore (well,
> actually WebStoreImpl for EMF), with a NoSuchElementException. If I
> change Customer to be abstract, then at least it works in this aspect.
>
> I tried plain java classes (hand-written, without interface/impl,
> without proxy), it works no matter Customer is abstract or not...

What is WebStore, is it a root resource class or JAXB bean which your
JAX-RS service returns/updates ?
May be you have to add @XmlJavaTypeAdapter to WebStore field which
keeps the list of Customer interface impls...

Cheers, Sergey

>
> -Simon
>
> On Mon, Apr 18, 2011 at 11:02 AM, Sergey Beryozkin <sb...@gmail.com> wrote:
>> Sorry for the noise, I think I've got totally confused - no @XmlSeeAlso has
>> to be added to subclasses. But, I have a test where
>>
>> SuperBook extends Book, Book has @XmlSeeAlso pointing to SuperBook, and
>> without using JAXBElement (internally), xsi:type is not written when Book is
>> returned, however, no extra classes or jaxb.index is used, so that might
>> explain why...
>>
>> Cheers, Sergey
>>
>>
>>
>> On Mon, Apr 18, 2011 at 3:57 PM, Sergey Beryozkin <sb...@gmail.com>
>> wrote:
>>>
>>> That should work with adapters too, with @XmlSeeAlso.
>>>
>>> Actually, I remember now what adding a jaxbElementClassNames property
>>> (containing a CustomerImpl full class name only in this particular case) can
>>> do, it may help with avoiding adding @XmlSeeAlso to subclasses, I see a test
>>> where only a base class has @XmlSeeAlso - somehow JAXBElement figures it out
>>> that xsi:type has to be added...
>>> Adding jaxb.index  extra classes for JAXBContext to include them should
>>> also help...
>>>
>>> thanks, Sergey
>>>
>>> On Mon, Apr 18, 2011 at 3:36 PM, Daniel Kulp <dk...@apache.org> wrote:
>>>>
>>>> When dealing with polymorphism with JAXB, one thing that is often
>>>> required is
>>>> to add XmlSeeAlso annotations all over the place.  In particular, on the
>>>> base
>>>> class, it's  useful to have XmlSeeAlso point at the potential subclasses.
>>>> This allows the jaxb runtime to find the subclasses to instantiate and
>>>> will
>>>> usually result in the proper xsi:type attributes written out and such.
>>>>
>>>> If you haven't already tried it, I'd definitely suggest adding the
>>>> annotation
>>>> and seeing if that helps.
>>>>
>>>> Dan
>>>>
>>>>
>>>> On Friday 15 April 2011 4:02:17 PM Simon Chen wrote:
>>>> > Hi all,
>>>> >
>>>> > I've been playing with building a REST web service to handle
>>>> > polymorphism... It's a long post, but bear with me :-)
>>>> >
>>>> > In particular, I may have:
>>>> >
>>>> > @Path("/")
>>>> > class WebStore {
>>>> >   @POST
>>>> >   @Path("/customers/")
>>>> >   Response addCustomer(Customer c) {
>>>> >     ...
>>>> >   }
>>>> > }
>>>> >
>>>> > But I have a base class of Customer, but also inherited classes of
>>>> > AwesomeCustomer, and SuperAwesomeCustomer. Things get more complicated
>>>> > when EMF kicks in, where I actually have a Customer interface and
>>>> > CustomerImpl class.
>>>> >
>>>> > The previous declaration doesn't work when I post a customer XML
>>>> > snippet,
>>>> > say: <customer><name>simon</name></customer>
>>>> >
>>>> > Because Customer is an interface, so cannot be annotated with
>>>> > @XmlRootElement. As a result, the error of "no message body reader can
>>>> > be found" is raised...
>>>> >
>>>> > If we change the POST function this way, it can work:
>>>> >   @POST
>>>> >   @Path("/customers/")
>>>> >   Response addCustomer(CustomerImpl c) {
>>>> >     ...
>>>> >   }
>>>> >
>>>> > But, this breaks again, when I add the following:
>>>> >   @POST
>>>> >   @Path("/customers/")
>>>> >   Response addCustomer(CustomerImpl c) {
>>>> >     ...
>>>> >   }
>>>> >   @POST
>>>> >   @Path("/customers/")
>>>> >   Response addCustomer(AwesomeCustomerImpl c) {
>>>> >     ...
>>>> >   }
>>>> >   @POST
>>>> >   @Path("/customers/")
>>>> >   Response addCustomer(SuperAwesomeCustomerImpl c) {
>>>> >     ...
>>>> >   }
>>>> >
>>>> > Here, if I post a SuperAwesomeCustomerImpl object to "/customers", the
>>>> > ws would find all the functions that can handle "POST to /customers",
>>>> > which will include all three functions above. However, in
>>>> >
>>>> > "src/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.
>>>> > java", public static OperationResourceInfo findTargetMethod(), we have:
>>>> >
>>>> >         if (!candidateList.isEmpty()) {
>>>> >             Map.Entry<OperationResourceInfo, MultivaluedMap<String,
>>>> > String>> firstEntry =
>>>> >                 candidateList.entrySet().iterator().next();
>>>> >             //---------> This only looks at the first function that
>>>> > matches, while not look at the class hierarchy...
>>>> >             values.clear();
>>>> >             values.putAll(firstEntry.getValue());
>>>> >             OperationResourceInfo ori = firstEntry.getKey();
>>>> >
>>>> >
>>>> > Hopefully, I am not overwhelming everyone with too much information.
>>>> > But is there a fix to this problem? I guess maybe add some class
>>>> > hierarchy-awareness to findTargetMethod()?
>>>> >
>>>> > Thanks!
>>>> > -Simon
>>>>
>>>> --
>>>> Daniel Kulp
>>>> dkulp@apache.org
>>>> http://dankulp.com/blog
>>>> Talend - http://www.talend.com
>>>
>>>
>>> Application Integration Division of Talend
>>> http://sberyozkin.blogspot.com
>>
>>
>>
>> --
>> Sergey Beryozkin
>>
>> Application Integration Division of Talend
>> http://sberyozkin.blogspot.com
>>
>

Re: handling polymorphism in @POST

Posted by Simon Chen <si...@gmail.com>.
(Sorry Sergey for the dup message, I forgot to reply to all last time...)

It seems that I get all sorts of weird issues with EMF+JAXB.

For example, if I have a base class of Customer and two inherited
classes of GoodCustomer and BadCustomer. Then I have a WebStore as a
container for a list of Customer objects. The mind-twisting (for me at
least) problem is that if Customer is not defined as abstract in the
ecore model, I cannot even create the JAXBContext for WebStore (well,
actually WebStoreImpl for EMF), with a NoSuchElementException. If I
change Customer to be abstract, then at least it works in this aspect.

I tried plain java classes (hand-written, without interface/impl,
without proxy), it works no matter Customer is abstract or not...

-Simon

On Mon, Apr 18, 2011 at 11:02 AM, Sergey Beryozkin <sb...@gmail.com> wrote:
> Sorry for the noise, I think I've got totally confused - no @XmlSeeAlso has
> to be added to subclasses. But, I have a test where
>
> SuperBook extends Book, Book has @XmlSeeAlso pointing to SuperBook, and
> without using JAXBElement (internally), xsi:type is not written when Book is
> returned, however, no extra classes or jaxb.index is used, so that might
> explain why...
>
> Cheers, Sergey
>
>
>
> On Mon, Apr 18, 2011 at 3:57 PM, Sergey Beryozkin <sb...@gmail.com>
> wrote:
>>
>> That should work with adapters too, with @XmlSeeAlso.
>>
>> Actually, I remember now what adding a jaxbElementClassNames property
>> (containing a CustomerImpl full class name only in this particular case) can
>> do, it may help with avoiding adding @XmlSeeAlso to subclasses, I see a test
>> where only a base class has @XmlSeeAlso - somehow JAXBElement figures it out
>> that xsi:type has to be added...
>> Adding jaxb.index  extra classes for JAXBContext to include them should
>> also help...
>>
>> thanks, Sergey
>>
>> On Mon, Apr 18, 2011 at 3:36 PM, Daniel Kulp <dk...@apache.org> wrote:
>>>
>>> When dealing with polymorphism with JAXB, one thing that is often
>>> required is
>>> to add XmlSeeAlso annotations all over the place.  In particular, on the
>>> base
>>> class, it's  useful to have XmlSeeAlso point at the potential subclasses.
>>> This allows the jaxb runtime to find the subclasses to instantiate and
>>> will
>>> usually result in the proper xsi:type attributes written out and such.
>>>
>>> If you haven't already tried it, I'd definitely suggest adding the
>>> annotation
>>> and seeing if that helps.
>>>
>>> Dan
>>>
>>>
>>> On Friday 15 April 2011 4:02:17 PM Simon Chen wrote:
>>> > Hi all,
>>> >
>>> > I've been playing with building a REST web service to handle
>>> > polymorphism... It's a long post, but bear with me :-)
>>> >
>>> > In particular, I may have:
>>> >
>>> > @Path("/")
>>> > class WebStore {
>>> >   @POST
>>> >   @Path("/customers/")
>>> >   Response addCustomer(Customer c) {
>>> >     ...
>>> >   }
>>> > }
>>> >
>>> > But I have a base class of Customer, but also inherited classes of
>>> > AwesomeCustomer, and SuperAwesomeCustomer. Things get more complicated
>>> > when EMF kicks in, where I actually have a Customer interface and
>>> > CustomerImpl class.
>>> >
>>> > The previous declaration doesn't work when I post a customer XML
>>> > snippet,
>>> > say: <customer><name>simon</name></customer>
>>> >
>>> > Because Customer is an interface, so cannot be annotated with
>>> > @XmlRootElement. As a result, the error of "no message body reader can
>>> > be found" is raised...
>>> >
>>> > If we change the POST function this way, it can work:
>>> >   @POST
>>> >   @Path("/customers/")
>>> >   Response addCustomer(CustomerImpl c) {
>>> >     ...
>>> >   }
>>> >
>>> > But, this breaks again, when I add the following:
>>> >   @POST
>>> >   @Path("/customers/")
>>> >   Response addCustomer(CustomerImpl c) {
>>> >     ...
>>> >   }
>>> >   @POST
>>> >   @Path("/customers/")
>>> >   Response addCustomer(AwesomeCustomerImpl c) {
>>> >     ...
>>> >   }
>>> >   @POST
>>> >   @Path("/customers/")
>>> >   Response addCustomer(SuperAwesomeCustomerImpl c) {
>>> >     ...
>>> >   }
>>> >
>>> > Here, if I post a SuperAwesomeCustomerImpl object to "/customers", the
>>> > ws would find all the functions that can handle "POST to /customers",
>>> > which will include all three functions above. However, in
>>> >
>>> > "src/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.
>>> > java", public static OperationResourceInfo findTargetMethod(), we have:
>>> >
>>> >         if (!candidateList.isEmpty()) {
>>> >             Map.Entry<OperationResourceInfo, MultivaluedMap<String,
>>> > String>> firstEntry =
>>> >                 candidateList.entrySet().iterator().next();
>>> >             //---------> This only looks at the first function that
>>> > matches, while not look at the class hierarchy...
>>> >             values.clear();
>>> >             values.putAll(firstEntry.getValue());
>>> >             OperationResourceInfo ori = firstEntry.getKey();
>>> >
>>> >
>>> > Hopefully, I am not overwhelming everyone with too much information.
>>> > But is there a fix to this problem? I guess maybe add some class
>>> > hierarchy-awareness to findTargetMethod()?
>>> >
>>> > Thanks!
>>> > -Simon
>>>
>>> --
>>> Daniel Kulp
>>> dkulp@apache.org
>>> http://dankulp.com/blog
>>> Talend - http://www.talend.com
>>
>>
>> Application Integration Division of Talend
>> http://sberyozkin.blogspot.com
>
>
>
> --
> Sergey Beryozkin
>
> Application Integration Division of Talend
> http://sberyozkin.blogspot.com
>

Re: handling polymorphism in @POST

Posted by Sergey Beryozkin <sb...@gmail.com>.
Sorry for the noise, I think I've got totally confused - no @XmlSeeAlso has
to be added to subclasses. But, I have a test where

SuperBook extends Book, Book has @XmlSeeAlso pointing to SuperBook, and
without using JAXBElement (internally), xsi:type is not written when Book is
returned, however, no extra classes or jaxb.index is used, so that might
explain why...

Cheers, Sergey



On Mon, Apr 18, 2011 at 3:57 PM, Sergey Beryozkin <sb...@gmail.com>wrote:

> That should work with adapters too, with @XmlSeeAlso.
>
> Actually, I remember now what adding a jaxbElementClassNames property
> (containing a CustomerImpl full class name only in this particular case) can
> do, it may help with avoiding adding @XmlSeeAlso to subclasses, I see a test
> where only a base class has @XmlSeeAlso - somehow JAXBElement figures it out
> that xsi:type has to be added...
> Adding jaxb.index  extra classes for JAXBContext to include them should
> also help...
>
> thanks, Sergey
>
>   On Mon, Apr 18, 2011 at 3:36 PM, Daniel Kulp <dk...@apache.org> wrote:
>
>>
>> When dealing with polymorphism with JAXB, one thing that is often required
>> is
>> to add XmlSeeAlso annotations all over the place.  In particular, on the
>> base
>> class, it's  useful to have XmlSeeAlso point at the potential subclasses.
>> This allows the jaxb runtime to find the subclasses to instantiate and
>> will
>> usually result in the proper xsi:type attributes written out and such.
>>
>> If you haven't already tried it, I'd definitely suggest adding the
>> annotation
>> and seeing if that helps.
>>
>> Dan
>>
>>
>> On Friday 15 April 2011 4:02:17 PM Simon Chen wrote:
>> > Hi all,
>> >
>> > I've been playing with building a REST web service to handle
>> > polymorphism... It's a long post, but bear with me :-)
>> >
>> > In particular, I may have:
>> >
>> > @Path("/")
>> > class WebStore {
>> >   @POST
>> >   @Path("/customers/")
>> >   Response addCustomer(Customer c) {
>> >     ...
>> >   }
>> > }
>> >
>> > But I have a base class of Customer, but also inherited classes of
>> > AwesomeCustomer, and SuperAwesomeCustomer. Things get more complicated
>> > when EMF kicks in, where I actually have a Customer interface and
>> > CustomerImpl class.
>> >
>> > The previous declaration doesn't work when I post a customer XML
>> snippet,
>> > say: <customer><name>simon</name></customer>
>> >
>> > Because Customer is an interface, so cannot be annotated with
>> > @XmlRootElement. As a result, the error of "no message body reader can
>> > be found" is raised...
>> >
>> > If we change the POST function this way, it can work:
>> >   @POST
>> >   @Path("/customers/")
>> >   Response addCustomer(CustomerImpl c) {
>> >     ...
>> >   }
>> >
>> > But, this breaks again, when I add the following:
>> >   @POST
>> >   @Path("/customers/")
>> >   Response addCustomer(CustomerImpl c) {
>> >     ...
>> >   }
>> >   @POST
>> >   @Path("/customers/")
>> >   Response addCustomer(AwesomeCustomerImpl c) {
>> >     ...
>> >   }
>> >   @POST
>> >   @Path("/customers/")
>> >   Response addCustomer(SuperAwesomeCustomerImpl c) {
>> >     ...
>> >   }
>> >
>> > Here, if I post a SuperAwesomeCustomerImpl object to "/customers", the
>> > ws would find all the functions that can handle "POST to /customers",
>> > which will include all three functions above. However, in
>> >
>> "src/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.
>> > java", public static OperationResourceInfo findTargetMethod(), we have:
>> >
>> >         if (!candidateList.isEmpty()) {
>> >             Map.Entry<OperationResourceInfo, MultivaluedMap<String,
>> > String>> firstEntry =
>> >                 candidateList.entrySet().iterator().next();
>> >             //---------> This only looks at the first function that
>> > matches, while not look at the class hierarchy...
>> >             values.clear();
>> >             values.putAll(firstEntry.getValue());
>> >             OperationResourceInfo ori = firstEntry.getKey();
>> >
>> >
>> > Hopefully, I am not overwhelming everyone with too much information.
>> > But is there a fix to this problem? I guess maybe add some class
>> > hierarchy-awareness to findTargetMethod()?
>> >
>> > Thanks!
>> > -Simon
>>
>> --
>> Daniel Kulp
>> dkulp@apache.org
>> http://dankulp.com/blog
>> Talend - http://www.talend.com
>>
>
>
>  Application Integration Division of Talend <http://www.talend.com/>
> http://sberyozkin.blogspot.com
>



-- 
Sergey Beryozkin

Application Integration Division of Talend <http://www.talend.com/>
http://sberyozkin.blogspot.com

Re: handling polymorphism in @POST

Posted by Sergey Beryozkin <sb...@gmail.com>.
That should work with adapters too, with @XmlSeeAlso.

Actually, I remember now what adding a jaxbElementClassNames property
(containing a CustomerImpl full class name only in this particular case) can
do, it may help with avoiding adding @XmlSeeAlso to subclasses, I see a test
where only a base class has @XmlSeeAlso - somehow JAXBElement figures it out
that xsi:type has to be added...
Adding jaxb.index  extra classes for JAXBContext to include them should also
help...

thanks, Sergey

On Mon, Apr 18, 2011 at 3:36 PM, Daniel Kulp <dk...@apache.org> wrote:

>
> When dealing with polymorphism with JAXB, one thing that is often required
> is
> to add XmlSeeAlso annotations all over the place.  In particular, on the
> base
> class, it's  useful to have XmlSeeAlso point at the potential subclasses.
> This allows the jaxb runtime to find the subclasses to instantiate and will
> usually result in the proper xsi:type attributes written out and such.
>
> If you haven't already tried it, I'd definitely suggest adding the
> annotation
> and seeing if that helps.
>
> Dan
>
>
> On Friday 15 April 2011 4:02:17 PM Simon Chen wrote:
> > Hi all,
> >
> > I've been playing with building a REST web service to handle
> > polymorphism... It's a long post, but bear with me :-)
> >
> > In particular, I may have:
> >
> > @Path("/")
> > class WebStore {
> >   @POST
> >   @Path("/customers/")
> >   Response addCustomer(Customer c) {
> >     ...
> >   }
> > }
> >
> > But I have a base class of Customer, but also inherited classes of
> > AwesomeCustomer, and SuperAwesomeCustomer. Things get more complicated
> > when EMF kicks in, where I actually have a Customer interface and
> > CustomerImpl class.
> >
> > The previous declaration doesn't work when I post a customer XML snippet,
> > say: <customer><name>simon</name></customer>
> >
> > Because Customer is an interface, so cannot be annotated with
> > @XmlRootElement. As a result, the error of "no message body reader can
> > be found" is raised...
> >
> > If we change the POST function this way, it can work:
> >   @POST
> >   @Path("/customers/")
> >   Response addCustomer(CustomerImpl c) {
> >     ...
> >   }
> >
> > But, this breaks again, when I add the following:
> >   @POST
> >   @Path("/customers/")
> >   Response addCustomer(CustomerImpl c) {
> >     ...
> >   }
> >   @POST
> >   @Path("/customers/")
> >   Response addCustomer(AwesomeCustomerImpl c) {
> >     ...
> >   }
> >   @POST
> >   @Path("/customers/")
> >   Response addCustomer(SuperAwesomeCustomerImpl c) {
> >     ...
> >   }
> >
> > Here, if I post a SuperAwesomeCustomerImpl object to "/customers", the
> > ws would find all the functions that can handle "POST to /customers",
> > which will include all three functions above. However, in
> >
> "src/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.
> > java", public static OperationResourceInfo findTargetMethod(), we have:
> >
> >         if (!candidateList.isEmpty()) {
> >             Map.Entry<OperationResourceInfo, MultivaluedMap<String,
> > String>> firstEntry =
> >                 candidateList.entrySet().iterator().next();
> >             //---------> This only looks at the first function that
> > matches, while not look at the class hierarchy...
> >             values.clear();
> >             values.putAll(firstEntry.getValue());
> >             OperationResourceInfo ori = firstEntry.getKey();
> >
> >
> > Hopefully, I am not overwhelming everyone with too much information.
> > But is there a fix to this problem? I guess maybe add some class
> > hierarchy-awareness to findTargetMethod()?
> >
> > Thanks!
> > -Simon
>
> --
> Daniel Kulp
> dkulp@apache.org
> http://dankulp.com/blog
> Talend - http://www.talend.com
>


Application Integration Division of Talend <http://www.talend.com/>
http://sberyozkin.blogspot.com

Re: handling polymorphism in @POST

Posted by Daniel Kulp <dk...@apache.org>.
When dealing with polymorphism with JAXB, one thing that is often required is 
to add XmlSeeAlso annotations all over the place.  In particular, on the base 
class, it's  useful to have XmlSeeAlso point at the potential subclasses.   
This allows the jaxb runtime to find the subclasses to instantiate and will 
usually result in the proper xsi:type attributes written out and such.   

If you haven't already tried it, I'd definitely suggest adding the annotation 
and seeing if that helps.

Dan


On Friday 15 April 2011 4:02:17 PM Simon Chen wrote:
> Hi all,
> 
> I've been playing with building a REST web service to handle
> polymorphism... It's a long post, but bear with me :-)
> 
> In particular, I may have:
> 
> @Path("/")
> class WebStore {
>   @POST
>   @Path("/customers/")
>   Response addCustomer(Customer c) {
>     ...
>   }
> }
> 
> But I have a base class of Customer, but also inherited classes of
> AwesomeCustomer, and SuperAwesomeCustomer. Things get more complicated
> when EMF kicks in, where I actually have a Customer interface and
> CustomerImpl class.
> 
> The previous declaration doesn't work when I post a customer XML snippet,
> say: <customer><name>simon</name></customer>
> 
> Because Customer is an interface, so cannot be annotated with
> @XmlRootElement. As a result, the error of "no message body reader can
> be found" is raised...
> 
> If we change the POST function this way, it can work:
>   @POST
>   @Path("/customers/")
>   Response addCustomer(CustomerImpl c) {
>     ...
>   }
> 
> But, this breaks again, when I add the following:
>   @POST
>   @Path("/customers/")
>   Response addCustomer(CustomerImpl c) {
>     ...
>   }
>   @POST
>   @Path("/customers/")
>   Response addCustomer(AwesomeCustomerImpl c) {
>     ...
>   }
>   @POST
>   @Path("/customers/")
>   Response addCustomer(SuperAwesomeCustomerImpl c) {
>     ...
>   }
> 
> Here, if I post a SuperAwesomeCustomerImpl object to "/customers", the
> ws would find all the functions that can handle "POST to /customers",
> which will include all three functions above. However, in
> "src/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.
> java", public static OperationResourceInfo findTargetMethod(), we have:
> 
>         if (!candidateList.isEmpty()) {
>             Map.Entry<OperationResourceInfo, MultivaluedMap<String,
> String>> firstEntry =
>                 candidateList.entrySet().iterator().next();
>             //---------> This only looks at the first function that
> matches, while not look at the class hierarchy...
>             values.clear();
>             values.putAll(firstEntry.getValue());
>             OperationResourceInfo ori = firstEntry.getKey();
> 
> 
> Hopefully, I am not overwhelming everyone with too much information.
> But is there a fix to this problem? I guess maybe add some class
> hierarchy-awareness to findTargetMethod()?
> 
> Thanks!
> -Simon

-- 
Daniel Kulp
dkulp@apache.org
http://dankulp.com/blog
Talend - http://www.talend.com