Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Override Jackson Object Mapper properties on Websphere 8.5.5 using Apache Wink

We are using IBM(s) bundled Apache Wink to offer JAXRS endpoints for our application. We are coding towards Websphere 8.5.5. Since we are servlet 3.0 compliant we use the 'programmatic' way of configuring the JaxRS application, meaning no entries in web.xml and we rely on class scanning for annotated jax rs resources. In general it works fine.

   @ApplicationPath("/api/v1/") 
   public class MyApplication  extends Application{

This version of Websphere along with Apache Wink, uses Jackson 1.6.x for JSON de/serialization and in general it works well. We would like though to change some of the default values of the Object Mapper

So we have defined a customer context resolver, where just alter some of the se/deserialzation properties.

@Provider
@Produces(MediaType.APPLICATION_JSON)
public class CustomJackssonConverter implements ContextResolver<ObjectMapper> {

    final ObjectMapper defaultObjectMapper;

    public AibasJackssonConverter() {
        defaultObjectMapper = createDefaultMapper();
    }
   ...       
 mapper.getSerializationConfig().set(SerializationConfig.Feature.INDENT_OUTPUT, true);

During JAX-RS calls we can see that the container registers the new Provider, with no errors

The problem is that , the Configuration is not 'followed', from the logs I can see that the Wink Engine is looking up a WinkJacksonProvider, which in turn..returns a JacksonProvider that is following the Jackson(s) default values?

Is there a way to just change this default value?

I have tried to change the implementation of the Application object as indicated here, in order to configure Providers programmatically, but it did not work.

http://www.ibm.com/developerworks/java/library/wa-aj-jackson/index.html

Any hints or tips?

Many thanks

like image 373
javapapo Avatar asked May 05 '14 08:05

javapapo


2 Answers

I solved this problem by just implementing a MessageBodyWriter class, like this:

import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;

import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;

@Provider
@Produces(MediaType.APPLICATION_JSON)
public class DefaultMessageBodyWriter implements MessageBodyWriter<Object> {

    @Override
    public long getSize(Object object, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return -1;
    }

    @Override
    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return true;
    }

    @Override
    public void writeTo(Object object, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
        mapper.writeValue(entityStream, object);
    }
}

Every time a JSON serialization is requested, this class comes into action and finally its writeTo method is invoked.

Here SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS is turned off, as requested by WebSphere.

like image 121
Giorgio Ferrara Avatar answered Sep 28 '22 07:09

Giorgio Ferrara


I found working solution with ContextResource.

You need Jackson JAX-RS provider dependencies. Maven example:

<dependency>
  <groupId>com.fasterxml.jackson.jaxrs</groupId>
  <artifactId>jackson-jaxrs-json-provider</artifactId>
  <version>2.9.7</version>
</dependency>

Next you can implement ContextResolver

@Provider
public class JacksonConfig implements ContextResolver<ObjectMapper> {

    private final ObjectMapper objectMapper;

    public JacksonConfig() {
        objectMapper = createObjectMapper();
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
        return objectMapper;
    }

    private ObjectMapper createObjectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        // some mapper configurations
        return mapper;
    }

}

And finaly you must register JacksonJaxbJsonProvider and your ContextResolver in your Application class.

public class RestApplicationConfig extends Application {

    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> resources = new java.util.HashSet<>();
        resources.add(JacksonJaxbJsonProvider.class);
        resources.add(JacksonConfig.class);
        // Add other resources
        return resources;
    }
}
like image 36
Saljack Avatar answered Sep 28 '22 07:09

Saljack