users@jersey.java.net

not-working custom provider in Jersey 1.1 (?)

From: Bruno De Nys <brunodenys_at_gmail.com>
Date: Wed, 24 Jun 2009 16:55:12 +0200

Hi,

I am exploring the possibilities of Jersey and JAX-RS. I want to produce an
XML-representation of my domain-objects. The XML-should contain a
"<xsl-stylesheet..." header.

As suggested at
http://blogs.sun.com/enterprisetechtips/entry/configuring_json_for_restful_web,
I created a custom resolver as follows:

package be.kvvcr.thesauri.rest;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.ws.rs.Produces;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import javax.xml.bind.JAXBContext;

import be.kvvcr.thesauri.Attribute;
import be.kvvcr.thesauri.DescLabel;
import be.kvvcr.thesauri.Descriptor;
import be.kvvcr.thesauri.Domain;
import be.kvvcr.thesauri.Label;
import be.kvvcr.thesauri.MicroThesaurus;
import be.kvvcr.thesauri.MultiLingualConcept;
import be.kvvcr.thesauri.NonDescLabel;
import be.kvvcr.thesauri.Perm;
import be.kvvcr.thesauri.ScopeNote;

@Produces({"text/xml"})
@Provider
public class JAXBXSLResolver implements ContextResolver<JAXBContext> {

    private JAXBContext context;
    private Class[] types = {MultiLingualConcept.class, Descriptor.class,
Label.class, Attribute.class, DescLabel.class, MicroThesaurus.class,
Domain.class, ScopeNote.class, Perm.class, NonDescLabel.class };

    public JAXBXSLResolver() throws Exception {
        Map<String, Object> props = new HashMap<String, Object>();
        System.out.println("instantiating the JAXBXSLResolver");
        this.context = MyJAXBContext.newInstance(types,props);
    }

    @Override
    public JAXBContext getContext(Class<?> objectType) {
        JAXBContext returnCtxt = null;
        List<Class> typesList = Arrays.asList(types);
        System.out.println("testing whether " + objectType + " is
supported...");
        if(typesList.contains(objectType)){
            System.out.println("yes it is...");
            returnCtxt = this.context;
        }
        System.err.println("nope it isn't...");
        return returnCtxt;
    }
}

And this custom JAXBResolver makes use of the context defined in
MyJAXBContext:

package be.kvvcr.thesauri.rest;

import java.util.HashMap;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.Validator;

public final class MyJAXBContext extends JAXBContext{

    private final JAXBContext context;

    public MyJAXBContext(Class[] classes) throws JAXBException{
        Map<String, Object> props = new HashMap<String, Object>();
        props.put("com.sun.xml.bind.xmlHeaders", "<?xml-stylesheet
type='text/xsl' href='foobar.xsl' ?>");
        this.context = JAXBContext.newInstance(classes,props);
    }

    public JAXBContext getContext(){
        return this.context;
    }


    @Override
    public Marshaller createMarshaller() throws JAXBException {
        Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty("com.sun.xml.bind.xmlHeaders",
"<?xml-stylesheet type='text/xsl' href='foobar.xsl' ?>");
        return marshaller;
    }

    @Override
    public Unmarshaller createUnmarshaller() throws JAXBException {
        Unmarshaller unmarshaller = context.createUnmarshaller();
        return unmarshaller;
    }

    @Override
    public Validator createValidator() throws JAXBException {
        return null;
    }
}

Now, when I startup my appserver (Tomcat 6.0), I see encouraging messages in
the console:
24-jun-2009 16:06:58 com.sun.jersey.api.core.ClasspathResourceConfig init
INFO: Provider classes found:
  class be.kvvcr.thesauri.rest.JAXBXSLResolver
24-jun-2009 16:06:58
com.sun.jersey.server.impl.application.WebApplicationImpl initiate
INFO: Initiating Jersey application, version 'Jersey: 1.1.0-ea 04/30/2009
04:46 PM'
instantiating the JAXBXSLResolver

However, I notice that my custom Provider is not used when invoking my
resources... It is the default resolver which is used (as far as a default
resolver exists). If I change the value of the @Produces annotation for my
custom Resolver from "text/xml" into, say, "browser/xml", I get an
errormessage (upon requesting a resource):

24-jun-2009 16:43:33 com.sun.jersey.spi.container.ContainerResponse write
SEVERE: A message body writer for Java type, class
be.kvvcr.thesauri.Descriptor, and MIME media type, browser/xml, was not
found
24-jun-2009 16:43:33
com.sun.jersey.server.impl.application.WebApplicationImpl onException
SEVERE: Internal server error
javax.ws.rs.WebApplicationException
    at
com.sun.jersey.spi.container.ContainerResponse.write(ContainerResponse.java:241)
    at
com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:693)
    at
com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:616)
    at
com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:607)
    at
com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:309)
    at
com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:425)
    at
com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:590)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
    at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
    at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
    at
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
    at
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
    at
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:263)
    at
org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)
    at
org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:584)
    at
org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
    at java.lang.Thread.run(Unknown Source)

I have been looking around with my (eclipse-) debugger in the source code.
I noticed that the "browser/xml" mime-type which I provided in the
annotation of my resolver, could not be found when requesting my
resource... Please note that my Resource-class contains the annotation
@Produces({"text/xml", "browser/xml"}). I guess something went wrong during
the registration of the resolver/provider. But I have no clue what.
Moreover, the api changed slightly during the upgrade to 1.1. Perhaps I
mixed something up?

As a consequence, I'm a bit stuck. I guess I miss something... Should I
explicitly define a new MessageBodyWriter? Should I register something?
I've found a few hints via Google, but none were for Jersey.

regards,
Bruno.