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);
}
}