You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@cxf.apache.org by Jeff Wang <je...@plutom.com> on 2012/02/16 03:37:47 UTC

Generic return types

Scenario:
User "owns" contacts via an unidirectional @ManyToOne JPA mapping on
Contact.  Contact subclasses from a @MappedSuperclass  called
UserResource.

What I would like to do:

an Interface called ISubResourceService<T extends UserResource> that
has a CXF annotation of:
public interface ISubResourceService<T extends UserResource> {
	@GET
	@Produces("application/json")
	List<T> getAll(@PathParam("userId") Long userId)
			throws DataNotFoundException;
}

and an Interface called IContactService extends
ISubResourceService<Contact> with contact specific annotations (such
as a class level @Path("/contact")) but no other (relevant to this
discussion) annotations or mappings.

finally, in the ideal world, the implementation:
public abstract class AbstractSubResourceService<T extends
UserResource> implements
		ISubResourceService<T> {
	@Override
	public List<T> getAll(Long userId) throws DataNotFoundException {
<code deleted for brevity>
	}
}

and of course: (ideally, no code other than the @Service for Spring to
pick it up need to go here)
@Service
public class ContactServiceImpl extends AbstractSubResourceService<Contact>
		implements IContactService {
}

The problem with the above implementation is that I get the "No
message body writer has been found for response class ArrayList"
error, which essentially is the problem when returning generic
collections, probably because of type erasure.

The following also return message body writer issues:
2) have AbstractSubResourceService return a Response, using entity(new
GenericEntity(List<T>){}), (we'll call this implementation 2, the base
is implementation 1)
3) have ContactService implement a getAll with the return signature
List<Contact> whose only code is "return super.getAll(userId);"
(implementation 3)
4) have IContactService do an @Override getAll with a return value of
List<Contact> (interface 2, same results vs all 3 implementations)
5) have IContactService copy getAll's declaration (with all CXF
annotations) with a return value of List<Contact> (interface 3, same
results vs all 3 implementations)
6) interface 3, remove the declaration of getAll in
ISubResourceService (call this interface 4), implementation (1 or 2)
AND 3 (see below for 2 without 3)

The following generates a no matching method WebApplicationException:
7) interface 4, implementation 1 or 2 (without 3)

The following works:
8) interface 4, implementation 3.

Based upon the above results:
1) GenericEntity is pointless if you are returning a generic
collection. (implementation 2 never worked, in any form)
2) having a sub interface "override" a super interface does not work
on an annotation basis (interface 2 and 3 does not work) possibly due
to retention when compiling?
3) I'm not quite sure why #7 returned no matching method.  Is there an
annotation retention issue that causes CXF not to "see" that the
getAll in AbstractSubResourceService has applicable annotations in
IContactService? (never mind that it's truly horrendous coding style.)

So anyways, I've got code that works, but isn't particularly ideal.
If anyone can shed light on how they've done similar work, or
suggestions, I'd be happy to try them out. Sergey, could you shed some
light on results 2 and 3?

thanks
Jeff

Re: Generic return types

Posted by Sergey Beryozkin <sb...@gmail.com>.
Hi Jeff - will try to investigate early next week,

Cheers, Sergey
On 16/02/12 02:37, Jeff Wang wrote:
> Scenario:
> User "owns" contacts via an unidirectional @ManyToOne JPA mapping on
> Contact.  Contact subclasses from a @MappedSuperclass  called
> UserResource.
>
> What I would like to do:
>
> an Interface called ISubResourceService<T extends UserResource>  that
> has a CXF annotation of:
> public interface ISubResourceService<T extends UserResource>  {
> 	@GET
> 	@Produces("application/json")
> 	List<T>  getAll(@PathParam("userId") Long userId)
> 			throws DataNotFoundException;
> }
>
> and an Interface called IContactService extends
> ISubResourceService<Contact>  with contact specific annotations (such
> as a class level @Path("/contact")) but no other (relevant to this
> discussion) annotations or mappings.
>
> finally, in the ideal world, the implementation:
> public abstract class AbstractSubResourceService<T extends
> UserResource>  implements
> 		ISubResourceService<T>  {
> 	@Override
> 	public List<T>  getAll(Long userId) throws DataNotFoundException {
> <code deleted for brevity>
> 	}
> }
>
> and of course: (ideally, no code other than the @Service for Spring to
> pick it up need to go here)
> @Service
> public class ContactServiceImpl extends AbstractSubResourceService<Contact>
> 		implements IContactService {
> }
>
> The problem with the above implementation is that I get the "No
> message body writer has been found for response class ArrayList"
> error, which essentially is the problem when returning generic
> collections, probably because of type erasure.
>
> The following also return message body writer issues:
> 2) have AbstractSubResourceService return a Response, using entity(new
> GenericEntity(List<T>){}), (we'll call this implementation 2, the base
> is implementation 1)
> 3) have ContactService implement a getAll with the return signature
> List<Contact>  whose only code is "return super.getAll(userId);"
> (implementation 3)
> 4) have IContactService do an @Override getAll with a return value of
> List<Contact>  (interface 2, same results vs all 3 implementations)
> 5) have IContactService copy getAll's declaration (with all CXF
> annotations) with a return value of List<Contact>  (interface 3, same
> results vs all 3 implementations)
> 6) interface 3, remove the declaration of getAll in
> ISubResourceService (call this interface 4), implementation (1 or 2)
> AND 3 (see below for 2 without 3)
>
> The following generates a no matching method WebApplicationException:
> 7) interface 4, implementation 1 or 2 (without 3)
>
> The following works:
> 8) interface 4, implementation 3.
>
> Based upon the above results:
> 1) GenericEntity is pointless if you are returning a generic
> collection. (implementation 2 never worked, in any form)
> 2) having a sub interface "override" a super interface does not work
> on an annotation basis (interface 2 and 3 does not work) possibly due
> to retention when compiling?
> 3) I'm not quite sure why #7 returned no matching method.  Is there an
> annotation retention issue that causes CXF not to "see" that the
> getAll in AbstractSubResourceService has applicable annotations in
> IContactService? (never mind that it's truly horrendous coding style.)
>
> So anyways, I've got code that works, but isn't particularly ideal.
> If anyone can shed light on how they've done similar work, or
> suggestions, I'd be happy to try them out. Sergey, could you shed some
> light on results 2 and 3?
>
> thanks
> Jeff


Re: Generic return types

Posted by Sergey Beryozkin <sb...@gmail.com>.
I finally managed to look at this case, I guess I was scared a bit with 
all the generics involved and such, :-)
On 16/02/12 02:37, Jeff Wang wrote:
> Scenario:
> User "owns" contacts via an unidirectional @ManyToOne JPA mapping on
> Contact.  Contact subclasses from a @MappedSuperclass  called
> UserResource.
>
> What I would like to do:
>
> an Interface called ISubResourceService<T extends UserResource>  that
> has a CXF annotation of:
> public interface ISubResourceService<T extends UserResource>  {
> 	@GET
> 	@Produces("application/json")
> 	List<T>  getAll(@PathParam("userId") Long userId)
> 			throws DataNotFoundException;
> }

After doing replacing List<T> with List<? extends UserResource> I got 
the JSON provider picking the response object...So try that please next

>
> and an Interface called IContactService extends
> ISubResourceService<Contact>  with contact specific annotations (such
> as a class level @Path("/contact")) but no other (relevant to this
> discussion) annotations or mappings.
>
> finally, in the ideal world, the implementation:
> public abstract class AbstractSubResourceService<T extends
> UserResource>  implements
> 		ISubResourceService<T>  {
> 	@Override
> 	public List<T>  getAll(Long userId) throws DataNotFoundException {
> <code deleted for brevity>
> 	}
> }
>
> and of course: (ideally, no code other than the @Service for Spring to
> pick it up need to go here)
> @Service
> public class ContactServiceImpl extends AbstractSubResourceService<Contact>
> 		implements IContactService {
> }
>
> The problem with the above implementation is that I get the "No
> message body writer has been found for response class ArrayList"
> error, which essentially is the problem when returning generic
> collections, probably because of type erasure.
>
> The following also return message body writer issues:
> 2) have AbstractSubResourceService return a Response, using entity(new
> GenericEntity(List<T>){}), (we'll call this implementation 2, the base
> is implementation 1)
> 3) have ContactService implement a getAll with the return signature
> List<Contact>  whose only code is "return super.getAll(userId);"
> (implementation 3)
> 4) have IContactService do an @Override getAll with a return value of
> List<Contact>  (interface 2, same results vs all 3 implementations)
> 5) have IContactService copy getAll's declaration (with all CXF
> annotations) with a return value of List<Contact>  (interface 3, same
> results vs all 3 implementations)
> 6) interface 3, remove the declaration of getAll in
> ISubResourceService (call this interface 4), implementation (1 or 2)
> AND 3 (see below for 2 without 3)
>
> The following generates a no matching method WebApplicationException:
> 7) interface 4, implementation 1 or 2 (without 3)
>
> The following works:
> 8) interface 4, implementation 3.
>
> Based upon the above results:
> 1) GenericEntity is pointless if you are returning a generic
> collection. (implementation 2 never worked, in any form)
> 2) having a sub interface "override" a super interface does not work
> on an annotation basis (interface 2 and 3 does not work) possibly due
> to retention when compiling?
> 3) I'm not quite sure why #7 returned no matching method.  Is there an
> annotation retention issue that causes CXF not to "see" that the
> getAll in AbstractSubResourceService has applicable annotations in
> IContactService? (never mind that it's truly horrendous coding style.)
>
> So anyways, I've got code that works, but isn't particularly ideal.
> If anyone can shed light on how they've done similar work, or
> suggestions, I'd be happy to try them out. Sergey, could you shed some
> light on results 2 and 3?

Please try as suggested above and see what happens to points 1-8 - it's 
difficult for me to go through this list without seeing the concrete 
code samples. There could be some issues there still when dealing will 
complex hierarchies involving generics - so if you can help me to narrow 
then down by posting a hierarchy example per every issue found then it 
would be super

Thanks Sergey

>
> thanks
> Jeff


-- 
Sergey Beryozkin

Talend Community Coders
http://coders.talend.com/

Blog: http://sberyozkin.blogspot.com