cxf-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Sergey Beryozkin" <sbery...@progress.com>
Subject Re: Inheriting methods
Date Fri, 15 May 2009 10:48:27 GMT
Hi Tom

I believe it has to work now, just applied some fixes.
Note that the (JAXRS) method inheritance used to work anyway (from abstract classes or interfaces),
it was the fact that you 
introduced type variables that did 'upset' the runtime a bit...

So you should be able to do either on interface or on abstract class :

class AbstractResource<T, E> {

    @POST
    E post(T) {};
}


By the way, I also fixed the super fields injection issue, so you can now get rid of the SevletContext
method setter if you wish...
It all has to be in snapshots starting from tomorrow - please experiment with the inheritance
fixes whenever you get a chance and 
let me know how it all works,

There could be some generics subtleties there which are still not supported - will fix if
some problems are discovered

Cheers, Sergey


----- Original Message ----- 
From: "Tom" <tbee@tbee.org>
To: <users@cxf.apache.org>
Sent: Thursday, May 14, 2009 2:14 PM
Subject: Re: Inheriting methods


> Attached my UserService classes. If I take out the two methods in the
> UserServiceImpl and uncomment the annotions in the AbstractServiceImpl I
> get the exception. You can maybe try this code as a basis for your test
> and replace the FEST reflection code with test code. If you like I can
> provide some test code for the methods.
>
> What also may be a factor, I do not know, is that my User entity is a
> three layer inheritance entity (custom class -> generated class ->
> generic bean stuff class). But as said; it is marshalled by JAXB, all
> works fine, except when introducing this abstract service thing.
>
> Tom
>
>
>
> Sergey Beryozkin wrote:
>> Hi Tom
>> I added this test code :
>>
>> public class AbstractEntity<T> {
>>  private T entity;
>>  @POST
>>  public void postEntity(T object) {
>>     entity = object;
>>  }
>>
>>  public T getEntity() {
>>    return entity;
>>  }
>> }
>>
>> @Path("/books")
>> public class BookEntity extends AbstractEntity<Book> {
>> }
>>
>> AbstractEntity#postEntity is being resolved for requests like "POST
>> /books <Books/>" in my test and a JAXB provider is invoked (I see a
>> diff kind of issue there though), I'm setting text/xml as content type.
>> What am I missing ? I've seen your code implementing UserService -
>> does it have JAX-RS methods ? I'm also seen in your post
>> Cheers, Sergey
>>
>>
>>
>>> Hey Sergey,
>>>
>>> I do not know what the reason is, all I know is that if I have:
>>>
>>>    @POST public void add(User user) {...}
>>>
>>> In the regular class, it works, but if I move the code to the
>>> abstract class and make it generic, like so:
>>>
>>>    @POST public void add(T user) {...}
>>>
>>> then I get the exception. But it works ok for @GET list(), or @GET
>>> find(long id).
>>>
>>> Tom
>>>
>>> PS: Please note my typo in the initial post that the abstract class
>>> does not know "User" but only the generic "T".
>>>
>>>
>>>
>>>
>>> Sergey Beryozkin wrote:
>>>> Hi,
>>>>
>>>> So just to confirm, for the purpose of writing a test,
>>>>
>>>> @Path(...)
>>>> @Produces(...)
>>>> UserServiceImpl extends AbstractServiceImpl<User>
>>>> implements UserService
>>>> {
>>>> }
>>>>
>>>>
>>>> abstract class AbstractServiceImpl<T> {
>>>> {
>>>>    @POST
>>>>    public void add(User user) {...// reflection ...//}
>>>> }
>>>>
>>>> can not be matched with 'User' add parameter being confused for
>>>> Object ?
>>>> If no then please correct me...
>>>> I'll do a test shortly
>>>>
>>>> Cheers, Sergey
>>>>
>>>>
>>>>
>>>>
>>>> ----- Original Message ----- From: "Tom" <tbee@tbee.org>
>>>> To: <users@cxf.apache.org>
>>>> Sent: Thursday, May 14, 2009 10:52 AM
>>>> Subject: Inheriting methods
>>>>
>>>>
>>>>> I have to implement basic CRUD operations on a serious number of
>>>>> Entities. The to-be-executed code is identical for all classes,
>>>>> except that different DAO implementating classes are involved. So I
>>>>> decided to go for reflection, so instead of:
>>>>>
>>>>> /@Path(...)
>>>>> @Produces(...)
>>>>> implements UserService
>>>>> UserServiceImpl
>>>>> {
>>>>>    @GET
>>>>>    public void list() {... same code over and over again... }
>>>>>
>>>>>    @POST
>>>>>    public void add(User user) {... //same code over and over
>>>>> again... //}
>>>>> }
>>>>> /
>>>>>
>>>>> I have
>>>>>
>>>>>
>>>>> /@Path(...)
>>>>> @Produces(...)
>>>>> UserServiceImpl extends AbstractServiceImpl<User>
>>>>> implements UserService
>>>>> {
>>>>> }
>>>>>
>>>>>
>>>>> abstract AbstractServiceImpl<T>
>>>>> {
>>>>>    @GET
>>>>>    public void list() {... reflection ...}
>>>>>
>>>>>    @POST
>>>>>    public void add(User user) {...// reflection ...//}
>>>>> }
>>>>> /
>>>>>
>>>>> This works fine for REST-GET but not for REST-POST, there I get a
>>>>> "No message body reader found for request class : Object,
>>>>> ContentType : text/xml". This most definitely has to do with the
>>>>> resolving of the @POST, because if I add the code below the the
>>>>> UserServiceImpl, then it all works ok.
>>>>>
>>>>> /    @POST public void add(User user) { super.add(user); }
>>>>>
>>>>> /Any suggestions on why?
>>>>>
>>>>> Tom
>>>>>
>>>>>
>>>>
>>>>
>>>
>>
>>
>>
>
>


--------------------------------------------------------------------------------


> /*
> * Copyright: (c) Innovation Investments
> * Version:   $Revision: 1.1 $
> * Modified:  $Date: 2009/05/14 12:02:05 $
> * By:        $Author: toeukps $
> */
> package nl.knowledgeplaza.profiler.services;
>
>
> /**
> *
> * @author User
> *
> */
> @javax.jws.WebService
> public interface AbstractService<T, Ts>
> {
> /**
> *
> * @return Users object containing a collection of User
> * @exception REST: sends a 500 on any error
> */
> public Ts list();
>
> /**
> *
> * @param id
> * @return User object
> * @exception REST: sends a 500 on any error
> */
> public T find(long id);
>
> /**
> * @param entity
> * @return REST: sends a 204 (NoContent) on success
> * @exception REST: sends a 500 on any error
> */
> public void add(T entity);
>
> /**
> *
> * @param id
> * @param entity
> * @return REST: sends a 204 (NoContent) on success
> * @exception REST: sends a 500 on any error
> */
> public void update(long id, T entity);
>
> /**
> *
> * @param id
> * @return REST: sends a 204 (NoContent) on success
> * @exception REST: sends a 500 on any error
> */
> public void delete(long id);
> }
>


--------------------------------------------------------------------------------


> /*
> * Copyright: (C) Innovation Investments
> * Version:   $Revision: 1.3 $
> * Modified:  $Date: 2009/05/14 12:02:05 $
> * By: $Author: toeukps $
> *
> * CXF http://cwiki.apache.org/CXF20DOC/index.html
> *     http://cwiki.apache.org/CXF20DOC/jax-rs.html#JAX-RS-Dealingwithcontexts
> *     JAX-RS http://wikis.sun.com/display/Jersey/Overview+of+JAX-RS+1.0+Features
> * JAXB https://jaxb.dev.java.net/tutorial/
> */
> package nl.knowledgeplaza.profiler.services;
>
> import java.math.BigInteger;
> import java.util.List;
>
> import javax.persistence.EntityManager;
>
> import org.fest.reflect.reference.TypeRef;
> import org.tbee.util.ExceptionUtil;
> import org.tbee.util.jpa.EntityManagerFinder;
>
> /**
> *
> * @author toeu
> *
> */
> abstract public class AbstractServiceImpl<T, Ts>
> {
> // logger (http://www.slf4j.org/faq.html#declared_static)
> transient final protected org.slf4j.Logger slf4j = org.slf4j.LoggerFactory.getLogger(this.getClass());
>
> // =======================================================================
> // CONSTRUCTOR
>
> /**
> *
> */
> public AbstractServiceImpl(Class entityClass, Class collectionClass)
> {
> iEntityClass = entityClass;
> iCollectionClass = collectionClass;
> }
> private Class iEntityClass = null;
> private Class iCollectionClass = null;
>
> // =======================================================================
> // SERVICES
>
> /**
> * list all
> */
> @javax.ws.rs.GET
> public Ts list()
> {
> try
> {
> if (slf4j.isDebugEnabled()) slf4j.debug("list {}", iEntityClass.getSimpleName());
>
> // find all
> // example: List<T> lList = Entity.findAll();
> List<T> lList = org.fest.reflect.core.Reflection.staticMethod("findAll") // 
> http://docs.codehaus.org/display/FEST/Invoking+Static+Methods
>                                                 .withReturnType( new TypeRef<List<T>>(){}
) // 
> http://docs.codehaus.org/display/FEST/Overcoming+Type+Erasure
>                                                 .in(iEntityClass)
>                                                 .invoke()
>                                                 ;
> if (slf4j.isDebugEnabled()) slf4j.debug("list {} size={}", iEntityClass.getSimpleName(),
lList.size());
>
> // create a collection so JAXB knows what to do
> // example: Entitys lTs = new Entitys(lList);
> Ts lTs = (Ts)org.fest.reflect.core.Reflection.constructor() // http://docs.codehaus.org/display/FEST/Invoking+Constructors
>                                              .withParameterTypes(java.util.Collection.class)
// Due to type erasure, there can 
> only be one constructor with a List as the parameter type (http://java.sun.com/docs/books/tutorial/java/generics/erasure.html)
>                                              .in(iCollectionClass) // TODO: why is TypeRef
not possible here?
>                                              .newInstance(lList)
>                                              ;
>
> // done
> return lTs;
> }
> catch (RuntimeException e)
> {
> slf4j.error(ExceptionUtil.describe(e), e);
> throw e;
> }
> }
>
> /**
> * find one
> */
> @javax.ws.rs.GET
> @javax.ws.rs.Path("/{id}")
> public T find(@javax.ws.rs.PathParam("id") long id)
> {
> try
> {
> if (slf4j.isDebugEnabled()) slf4j.debug("find {}={}", iEntityClass.getSimpleName(), id);
> // example: Entity lEntity = Entity.findByPK( BigInteger.valueOf(id) );
> T lT = (T)org.fest.reflect.core.Reflection.staticMethod("findByPK") // 
> http://docs.codehaus.org/display/FEST/Invoking+Static+Methods
>                                                   .withReturnType( iEntityClass ) //
 why does this not work: new TypeRef<T>(){} 
> // http://docs.codehaus.org/display/FEST/Overcoming+Type+Erasure
>                                                   .withParameterTypes( BigInteger.class
)
>                                                   .in(iEntityClass)
>                                                   .invoke( BigInteger.valueOf(id) )
>                                                   ;
> if (slf4j.isDebugEnabled()) slf4j.debug("find {}={}", iEntityClass.getSimpleName(), lT);
> return lT;
> }
> catch (RuntimeException e)
> {
> slf4j.error(ExceptionUtil.describe(e), e);
> throw e;
> }
> }
>
> /**
> * Insert new entity
> * @param entity
> */
> // we get an exception if this code is placed here and not in the inheriting class
> // @javax.ws.rs.POST
> public void add(T entity)
> {
> try
> {
> if (slf4j.isDebugEnabled()) slf4j.debug("add {}={}", iEntityClass.getSimpleName(), entity);
> EntityManager lEntityManager = EntityManagerFinder.find();
> try
> {
> lEntityManager.getTransaction().begin();
> lEntityManager.persist(entity);
> lEntityManager.getTransaction().commit();
> }
> catch (org.eclipse.persistence.exceptions.DatabaseException e)
> {
> slf4j.error(ExceptionUtil.describe(e), e);
> throw new RuntimeException(e);
> }
> finally
> {
> // rollback if still active
> if (lEntityManager != null && lEntityManager.getTransaction().isActive()) lEntityManager.getTransaction().rollback();
> }
> }
> catch (RuntimeException e)
> {
> slf4j.error(ExceptionUtil.describe(e), e);
> throw e;
> }
> }
>
> /**
> * Update an entity
> * @param id
> * @param entity
> */
> // we get an exception if this code is placed here and not in the inheriting class
> // @javax.ws.rs.PUT
> // @javax.ws.rs.Path("/{id}")
> // public void update(@javax.ws.rs.PathParam("id") long id, T entity)
> public void update(long id, T entity)
> {
> try
> {
> if (slf4j.isDebugEnabled()) slf4j.debug("update {} id={}", iEntityClass, id);
> EntityManager lEntityManager = EntityManagerFinder.find();
> try
> {
> lEntityManager.getTransaction().begin();
> // example: Entity lEntity = Entity.findByPK( BigInteger.valueOf(id) );
> T lT = (T)org.fest.reflect.core.Reflection.staticMethod("findByPK") // 
> http://docs.codehaus.org/display/FEST/Invoking+Static+Methods
>                                                          .withReturnType( iEntityClass
) //  why does this not work: new 
> TypeRef<T>(){}     // http://docs.codehaus.org/display/FEST/Overcoming+Type+Erasure
>                                                          .withParameterTypes( BigInteger.class
)
>                                                          .in(iEntityClass)
>                                                          .invoke( BigInteger.valueOf(id)
)
>                                                          ;
> if (lT == null) throw new IllegalArgumentException(iEntityClass.getSimpleName() + " "
+ id + " does not exist");
> // example: Entity.shallowCopy(entity, lEntity);
> org.fest.reflect.core.Reflection.staticMethod("shallowCopy") // http://docs.codehaus.org/display/FEST/Invoking+Static+Methods
>                                                .withParameterTypes( iEntityClass, iEntityClass
)
>                                                .in(iEntityClass)
>                                                .invoke( entity, lT )
>                                                ;
> lEntityManager.getTransaction().commit();
> }
> finally
> {
> // rollback if still active
> if (lEntityManager != null && lEntityManager.getTransaction().isActive()) lEntityManager.getTransaction().rollback();
> }
> }
> catch (RuntimeException e)
> {
> slf4j.error(ExceptionUtil.describe(e), e);
> throw e;
> }
> }
>
> /**
> * Delete an entity
> * @param id
> */
> @javax.ws.rs.DELETE
> @javax.ws.rs.Path("/{id}")
> public void delete(@javax.ws.rs.PathParam("id") long id)
> {
> try
> {
> if (slf4j.isDebugEnabled()) slf4j.debug("delete, id={}", id);
> EntityManager lEntityManager = EntityManagerFinder.find();
> try
> {
> lEntityManager.getTransaction().begin();
> // example: Entity lEntity = Entity.findByPK( BigInteger.valueOf(id) );
> T lT = (T)org.fest.reflect.core.Reflection.staticMethod("findByPK") // 
> http://docs.codehaus.org/display/FEST/Invoking+Static+Methods
>                                                          .withReturnType( iEntityClass
) //  why does this not work: new 
> TypeRef<T>(){}     // http://docs.codehaus.org/display/FEST/Overcoming+Type+Erasure
>                                                          .withParameterTypes( BigInteger.class
)
>                                                          .in(iEntityClass)
>                                                          .invoke( BigInteger.valueOf(id)
)
>                                                          ;
> if (lT == null) throw new IllegalArgumentException(iEntityClass.getSimpleName() + " "
+ id + " does not exist");
> lEntityManager.remove(lT);
> lEntityManager.getTransaction().commit();
> }
> finally
> {
> // rollback if still active
> if (lEntityManager != null && lEntityManager.getTransaction().isActive()) lEntityManager.getTransaction().rollback();
> }
> }
> catch (RuntimeException e)
> {
> slf4j.error(ExceptionUtil.describe(e), e);
> throw e;
> }
> }
>
> // =======================================================================
> // SESSION
>
> // Moving this code to this class result in that SOAP works, but REST returns NULL
>
> // // Get context for SOAP and REST
> // // SOAP context: http://cwiki.apache.org/CXF20DOC/webservicecontext.html
> // @javax.annotation.Resource private javax.xml.ws.WebServiceContext iSoapWebServiceContext;
> // // REST context: http://cwiki.apache.org/CXF20DOC/jax-rs.html#JAX-RS-Contextannotations
> // @javax.ws.rs.core.Context private javax.servlet.ServletContext iRestServletContext;
> // //@javax.annotation.Resource private org.apache.cxf.jaxrs.ext.MessageContext iRestMessageContext;
> // // combining SOAP and REST: http://cwiki.apache.org/CXF20DOC/jax-rs.html#JAX-RS-Dealingwithcontexts
> //
> // protected javax.servlet.ServletContext getServletContext()
> // throws javax.ws.rs.WebApplicationException
> // {
> // if (iSoapWebServiceContext != null)
> // {
> // // soap invocation
> // return 
> (javax.servlet.ServletContext)iSoapWebServiceContext.getMessageContext().get(javax.xml.ws.handler.MessageContext.SERVLET_CONTEXT);
> // }
> // else
> // {
> // // http-only jaxrs one
> // return iRestServletContext;//(javax.servlet.ServletContext)iRestMessageContext.get(MessageContext.SERVLET_CONTEXT);
> // }
> // }
>
> }
>


--------------------------------------------------------------------------------


> /*
> * Copyright: (c) Innovation Investments
> * Version:   $Revision: 1.3 $
> * Modified:  $Date: 2009/05/14 12:02:05 $
> * By:        $Author: toeukps $
> */
> package nl.knowledgeplaza.profiler.services;
>
> import nl.knowledgeplaza.profiler.engine.model.User;
> import nl.knowledgeplaza.profiler.services.support.Users;
>
> /**
> *
> * @author User
> *
> */
> @javax.jws.WebService
> public interface UserService// SOAP via CXF throws an exception: extends AbstractService<User,
Users>
> {
> /**
> *
> * @return Users object containing a collection of User
> * @exception REST: sends a 500 on any error
> */
> public Users list();
>
> /**
> *
> * @param id
> * @return User object
> * @exception REST: sends a 500 on any error
> */
> public User find(long id);
>
> /**
> * @param user
> * @return REST: sends a 204 (NoContent) on success
> * @exception REST: sends a 500 on any error
> */
> public void add(User user);
>
> /**
> *
> * @param id
> * @param user
> * @return REST: sends a 204 (NoContent) on success
> * @exception REST: sends a 500 on any error
> */
> public void update(long id, User user);
>
> /**
> *
> * @param id
> * @return REST: sends a 204 (NoContent) on success
> * @exception REST: sends a 500 on any error
> */
> public void delete(long id);
> }
>


--------------------------------------------------------------------------------


> /*
> * Copyright: (C) Innovation Investments
> * Version:   $Revision: 1.4 $
> * Modified:  $Date: 2009/05/14 12:02:05 $
> * By: $Author: toeukps $
> */
> package nl.knowledgeplaza.profiler.services;
>
> import nl.knowledgeplaza.profiler.engine.model.User;
> import nl.knowledgeplaza.profiler.services.support.Users;
>
> /**
> *
> * @author toeu
> *
> */
> @javax.jws.WebService(endpointInterface = "nl.knowledgeplaza.profiler.services.UserService",
serviceName = "users") // the "ws" 
> prefix is handled by the container
> @javax.ws.rs.Path("/users") // the "rest" prefix is handled by the container
> @javax.ws.rs.Produces("application/xml")
> public class UserServiceImpl extends AbstractServiceImpl<User, Users>
> implements UserService
> {
> // =======================================================================
> // CONSTRUCTOR
>
> /**
> *
> */
> public UserServiceImpl()
> {
> super(User.class, Users.class);
> }
>
> // =======================================================================
> // SERVICES
>
> // TODO: we get an exception if this code is placed in the abstract class, CXF is looking
into it
> @javax.ws.rs.POST
> @Override public void add(User user)
> {
> super.add(user);
> }
>
> // TODO: we get an exception if this code is placed in the abstract class, CXF is looking
into it
> @javax.ws.rs.PUT
> @javax.ws.rs.Path("/{id}")
> @Override public void update(@javax.ws.rs.PathParam("id") long id, User user)
> {
> super.update(id, user);
> }
> }
> 


Mime
View raw message