How can I override the decisions made during JAX-RS Content Negotiation?
java, jax-rs, rest, resteasy
Solution
I'm sure there are several solutions to this problem, but I can only think of one.
Try so set the content-type using a ClientExecutionInterceptor:
public class Interceptor implements ClientExecutionInterceptor {
@Override
public ClientResponse<?> execute(ClientExecutionContext ctx) throws Exception {
final ClientResponse<?> response = ctx.proceed();
response
.getHeaders()
.putSingle(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML);
return response;
}
}
public void getSchools() throws Exception {
ResteasyProviderFactory.getInstance()
.getClientExecutionInterceptorRegistry()
.register( new Interceptor() );
SchoolSearch service =
ProxyFactory.create(SchoolSearch.class, SITE_URL);
}
Problem
I'm using RESTEasy 2.2.1.GA as my JAX-RS implementation to create a client to connect to a third party service provider. (Education.com's REST API if it matters) To make sure I haven't missed an important implementation detail here are code samples: Service Interface ``` @Path("/") public interface SchoolSearch { @GET @Produces({MediaType.APPLICATION_XML}) Collection<SchoolType> getSchoolsByZipCode(@QueryParam("postalcode") int postalCode); } ``` Calling Class ``` public class SimpleSchoolSearch { public static final String SITE_URL = "http://api.education.com/service/service.php?f=schoolSearch&key=****&sn=sf&v=4"; SchoolSearch service = ProxyFactory.create(SchoolSearch.class, SITE_URL); public Collection<SchoolType> getSchools() throws Exception { Collection<SchoolType> schools = new ArrayList<SchoolType>(); Collection<SchoolType> response = service.getSchoolsByZipCode(35803); schools.addAll(response); return schools; } } ``` After setting up tests to make this call, I execute and see the following exception being thrown. ``` org.jboss.resteasy.plugins.providers.jaxb.JAXBUnmarshalException: Unable to find JAXBContext for media type: text/html;charset="UTF-8" ``` From reading the RESTEasy/JAX-RS documentation, as I understand it, when the response is returned to the client, prior to the unmarshaling of the data, a determination is made (Content Negotiation??) about which mechanism to use for unmarshalling. (I think we're talking about a MessageBodyReader here but I'm unsure.) From looking at the body of the response, I see that what is returned is properly formatted XML, but the content negotiation (via HTTP header content-type is indeed text/html;charset ="UTF-8") is not allowing the text to be parsed by JAXB. I think that the implementation is behaving correctly, and it is the service that is in error, however, I don't control the service, but would still like to consume it. So that being said: Am I correct in my understanding of why the exception is thrown? How do I work around it? Is there a simple one line annotation that can force JAXB to unmarshal the data, or will I need to implement a custom MessageBodyReader? (If that is even the correct class to implement). Thanks! Follow Up: I just wanted to post the few changes I made to Eiden's answer. I created a ClientExecutionInterceptor using his code and the information available at Resteasy ClientExecutionInterceptor documentation. My final class looks like ``` @Provider @ClientInterceptor public class SimpleInterceptor implements ClientExecutionInterceptor { @Override public ClientResponse execute(ClientExecutionContext ctx) throws Exception { final ClientResponse response = ctx.proceed(); response.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML); return response; } } ``` The big difference is the addition of the @Provider and @ClientExecutionInterceptor annotations. This should insure that the interceptor is properly registered. Also, just for completeness, I registered the Interceptor slightly differently for my tests. I used: ``` providerFactory.registerProvider(SimpleInterceptor.class); ```