javajsonjax-rsjacksonapache-wink

How do I register the Jackson provider with the Wink client?


I'm trying to set up a toy application (which may turn in to a real application someday). I'm running into a problem with Wink and Jackson. I've got two applications: one runs wink-server on jetty and seems to be providing some JSON data just fine; one runs wink-client on jetty and receives the JSON data just fine. The problem lies in automagically deserializing the JSON data back into my Java bean.

Here's the code I use in my wink client action:

RestClient client = new RestClient();
Resource resource = client.resource("http://localhost:8081/helloworld");
User user = resource.accept(MediaType.APPLICATION_JSON).get(User.class);

Here's the error I receive when I try to run the Struts action:

java.lang.RuntimeException: No javax.ws.rs.ext.MessageBodyReader found for type class my.package.structure.User and media type application/json. Verify that all entity providers are correctly registered.
org.apache.wink.client.internal.handlers.ClientResponseImpl.readEntity(ClientResponseImpl.java:123)
org.apache.wink.client.internal.handlers.ClientResponseImpl.getEntity(ClientResponseImpl.java:65)
org.apache.wink.client.internal.handlers.ClientResponseImpl.getEntity(ClientResponseImpl.java:52)
org.apache.wink.client.internal.ResourceImpl.invoke(ResourceImpl.java:186)
org.apache.wink.client.internal.ResourceImpl.get(ResourceImpl.java:294)
my.package.structure.action.HelloWorldAction.execute(HelloWorldAction.java:29)
...

If I replace the last line in the first code snippet with the following line, everything works fine and dandy.

String message = resource.accept(MediaType.APPLICATION_JSON).get(String.class);
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(message, User.class);

It's clear that the data is getting across just fine, but the problem seems to lie with the fact that the JacksonJsonProvider class is not registered with Wink client. I've seen a lot of ways to register the provider with the Wink server, but not the Wink client.

Is it possible to do make the first code snippet operate properly? If so, how?

(As an aside, the other problem may be that I'm missing annotations on my User class. Right now there aren't any. Maybe I need some...)


Solution

  • Step 1: Create a class that extends javax.ws.rs.core.Application that allows you to set singletons.

    import java.util.Collections;
    import java.util.Set;
    import javax.ws.rs.core.Application;
    
    public class ClientApplication extends Application {
    
        private Set<Object> singletons = Collections.emptySet();
    
        @Override
        public Set<Object> getSingletons() {
            return singletons;
        }
    
        public void setSingletons(final Set<Object> singletons) {
            this.singletons = singletons;
        }
    }
    

    Step 2: In your action, create a org.apache.wink.client.ClientConfig for your org.apache.wink.client.RestClient. This allows you add the org.codehaus.jackson.jaxrs.JacksonJsonProvider to your providers list.

    ClientApplication clientApplication = new ClientApplication();
    Set<Object> s = new HashSet<Object>();
    s.add(new JacksonJsonProvider());
    clientApplication.setSingletons(s);
    ClientConfig clientConfig = new ClientConfig().applications(clientApplication);
    RestClient restClient = new RestClient(clientConfig);
    

    Step 3: Create the org.apache.wink.client.Resource, use the get(Class<T> responseEntity) method and everything will now work as expected.

    Resource resource = client.resource("http://localhost:8081/helloworld");
    User user = resource.accept(MediaType.APPLICATION_JSON).get(User.class);
    

    If you want to be really slick about it, you can use Spring to set up a ClientConfig bean and inject it in to your actions. Then, you can just call new RestClient(clientConfig) every time and not worry about replicating the entire setup.