Search code examples
springcxfjunit4cxf-client

CXF @PUT request "No message body writer has been found" exception


I am completely stuck! spends many hours on this with no progress...

I have a Spring 4 (4.2.3.RELEASE) app with CXF 3 (3.1.4) which I am trying to JUnit test. Everything is working great except for the PUT request. I am getting the following error:

Caused by: org.apache.cxf.interceptor.Fault: No message body writer has been found for class com.someproject.logic.api.data.User, ContentType: application/xml
    at org.apache.cxf.jaxrs.client.WebClient$BodyWriter.doWriteBody(WebClient.java:1222)
    at org.apache.cxf.jaxrs.client.AbstractClient$AbstractBodyWriter.handleMessage(AbstractClient.java:1091)
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)
    at org.apache.cxf.jaxrs.client.AbstractClient.doRunInterceptorChain(AbstractClient.java:649)
    at org.apache.cxf.jaxrs.client.WebClient.doChainedInvocation(WebClient.java:1093)
    ... 49 more

Caused by: javax.ws.rs.ProcessingException: No message body writer has been found for class com.someproject.logic.api.data.User, ContentType: application/xml at org.apache.cxf.jaxrs.client.AbstractClient.reportMessageHandlerProblem(AbstractClient.java:780) at org.apache.cxf.jaxrs.client.AbstractClient.writeBody(AbstractClient.java:494) at org.apache.cxf.jaxrs.client.WebClient$BodyWriter.doWriteBody(WebClient.java:1217) ... 53 more

I also tried with "application/json" and got the same result.

Here is the CXF configuration:

@Bean
@DependsOn("cxf")
public Server jaxRsServer() {
    JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
    List<Object> providers = new ArrayList<Object>();

    // get all the class annotated with @JaxrsService
    List<Object> beans = configUtil.findBeans(JaxrsService.class);

    if (beans.size() > 0) {

        // add all the CXF service classes into the CXF stack
        sf.setServiceBeans(beans);
        sf.setAddress("http://localhost:8080/api");
        sf.setBus(springBus);
        sf.setStart(true);

        // set JSON as the response serializer
        JacksonJsonProvider provider = new JacksonJsonProvider();
        providers.add( provider );

    }

    // add custom providers if any
    sf.setProviders(providers);

    return sf.create();
}

Endpoint:

@Path("/user")
@PUT
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, })
@Produces( MediaType.APPLICATION_JSON )
public User updateUser( User user );

Endpoint impl:

@Override
public User updateUser( User user ) {
    System.out.println( "updateUser: " + user );
    return user;
}

Test:

@Before
public void setUp() throws Exception {

    MockitoAnnotations.initMocks(this);

    webClient = WebClient.create( "http://localhost:8080/api" );
    WebClient.getConfig( webClient ).getRequestContext().put( LocalConduit.DIRECT_DISPATCH, Boolean.TRUE );
    webClient.accept( "application/json" );

}

@Test
public void testPut() {

    String apiUrl = "/user";
    webClient.path( apiUrl );

    User user = createDummyAPIUser();

    try {

        System.out.println( "testUpdateUser: PUT request to " + apiUrl );
        String response = webClient.type( MediaType.APPLICATION_JSON ).put( user, String.class );
        System.out.println( "testUpdateUser: " + apiUrl + " response: " + response );
        //updatedUser = (new ObjectMapper()).readValue( response, User.class );

    } catch( Exception e ) {

        e.printStackTrace();
        fail();
        return;

    }

}

First how do I make sure that the fasterxml Jackson 2 provider is in fact serializing the message body? It do not see anything Jackson in the stacktrace but I do set it in the providers.

I have found this link to demonstrate a custom ContextProvider, is this the only way to get this working? Seem utterly redundant... http://www.blackpepper.co.uk/custom-context-providers-for-cxf-with-the-context-annotation/

Any ideas?

Thank you!!


Solution

  • GEEEEEEZZZ...

    feeling like a dork, forgot to add the same Jackson serializer ( provider ) to the client. Looking again through the stacktrace I noticed that the methods were only from the client, so obviously the client did not know how to consume the POJO I was throwing at it...

    Updated test code:

    @Before
    public void setUp() throws Exception {
    
        MockitoAnnotations.initMocks(this);
    
        final List<Object> providers = new ArrayList<Object>();
        JacksonJaxbJsonProvider jacksonJsonProvider = new JacksonJaxbJsonProvider();
        providers.add( jacksonJsonProvider );
    
        webClient = WebClient.create( "http://localhost:8080/api", providers );
        WebClient.getConfig( webClient ).getRequestContext().put( LocalConduit.DIRECT_DISPATCH, Boolean.TRUE );
        webClient.accept( "application/json" );
    
    }