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" <[email protected]>
To: <[email protected]>
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" <[email protected]>
To: <[email protected]>
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);
}
}


Reply via email to