Question

I would like to use a dynamically generated bean as the output for a Jersey REST WS. I don't know at compile time what properties will be requested. I can return a bean containing a list of fields, but I wanted to try returning a bean with dynamically created properties as the output would be a bit nicer.

I'm trying to use cglib to build a bean instance and return it to the Jersey framework to marshall into JSON/XML. I ran into the issue below, if anyone can make a suggestion I'd appreciate it. I don't know if I can use the magic of generics or create a custom handler to solve.

Libraries used: Tomcat 6.0.18, Java 1.6.0_17, Jersey v1.8, cglib v2.2

SearchWS

@Path("/search")
public class SearchWS
{
private static final Logger log = Logger.getLogger(SearchWS.class.getName());

@POST
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public GenericObject getXML(@Context final HttpServletRequest request, @FormParam("query") final String query)
        throws Exception
{
    log.debug("Entering");
    final IDAO dao = new DAOImpl();
    return dao.search(query).getResults().get(0);
}

}

GenericObject bean class

@XmlRootElement(name = "object")
@XmlAccessorType(XmlAccessType.PROPERTY)
public class GenericObject
{

}

DAO creates map of key/value pairs from the query and builds a GenericObject subclass instance

public GenericObject buildGenericBean(final Map<String, String> propertyValues) throws Exception
{
    log.debug("Entering");

    BeanGenerator bg = new BeanGenerator();
    bg.setSuperclass(GenericObject.class);

    bg.setNamingPolicy(new net.sf.cglib.core.NamingPolicy()
    {
        @Override
        public String getClassName(final String prefix, final String source, final Object key, final Predicate names)
        {
            return prefix + "Impl";
        }
    });

    Set<String> keys = propertyValues.keySet();
    for (String nextKey : keys)
    {
        bg.addProperty(nextKey, String.class);
    }
    GenericObject bean = (GenericObject) bg.create();

    for (String nextKey : keys)
    {
        setProperty(bean, nextKey, propertyValues.get(nextKey));
    }

    log.debug("Leaving");
    return bean;
}

When the client requests JSON content ("application/json"), it produces some nice output as expected, based on the SQL query submitted as a form parameter:

{
    "country": "DZ",
    "title": "foo",
    "theme": "foo",
    "subject": "foo",
    "owner_name": "foo",
    "creation_date": "16/05/2013 06:20:10 PM",
    "publication_needed": "1",
    "language_code": "en",
    "modify_date": "21/05/2013 05:08:15 PM",
    "classification": "0"
}

Setting the requested content-type to XML ("application/xml") results in the error below. I had hoped that Jersey would ensure that the output would be equivalent e.g. elements named for the properties with text content as the property values. I'm not expert on XML marshalling though, so something could be missing. How can I get the equivalent output in XML?

Caused by: com.sun.jersey.api.MessageException: A message body writer for Java class xxx.model.GenericObjectImpl, and Java type class xxx.model.GenericObjectImpl, and MIME media type application/xml was not found
Was it helpful?

Solution

cglib creates a subclass in order to create a proxy. This means that Jersey will receive a subclass of GenericObject instead of a direct instance. The annotation XMLRootElement is however not declared as @Inherited such that this annotation will not be present for this proxy subclass even though it is for GenericObject.

Since Jersey does not find this annotation on the actual type, it tells you that it does not know how to handle it. This problem is unfortunately not too easy to solve with cglib. You might want to try another instrumentation library such as javassist or ASM directly.

If you want to solve the problem in Jersey directly. you could however create your own marshaller that checks if the class is a subclass of GenericObject on order to create a default XML output. (Look after Jersey MessageBodyWriter for help.)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top