Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Configure Wildfly 10 to use Jackson (as JSON provider)

I have an application that has webservices built with Jersey and Jackson as JSON provider, all this in a Tomcat application server.

I need to make this application working on Wildfly 10 and everything is working fine, beside the webservice response that is not taking in consideration the Jackson annotations. From what I read Wildfly was using Jettison as default and in the newer version Jackson2 is used.

The preferred solution would be to make RestEasy (from Wildfly 10) to use Jackson and for this I tried to exclude Jackson2 and Jettison and make a dependency to Jackson in (META-INF\jboss-deployment-structure.xml), as below:

<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
<deployment>
    <exclusions>
        <module name="org.jboss.resteasy.resteasy-jackson2-provider"/>
        <module name="org.jboss.resteasy.resteasy-jettison-provider"/>
    </exclusions>
    <dependencies>
        <module name="org.jboss.resteasy.resteasy-jackson-provider" services="import"/>
    </dependencies>
</deployment>
</jboss-deployment-structure>

Apparently this is not enough, since is behaving as before. What else should I try?

UPDATE:

Since my application should work the same on both Tomcat (using Jersey) and Wildfly (using RestEasy), I cannot rely on using jackson from resteasy inside my application, hence I'm importing org.codehaus.jackson.

So, I register my application like this:

import javax.ws.rs.core.Application;
public class RestApplication extends Application
{
   @Override
   public Set<Class<?>> getClasses() {
       Set<Class<?>> classes = new HashSet<Class<?>>();

       classes.add(RestObjectMapperProvider.class);
       classes.add(GeneratedService.class);

       return classes;
   }
}

And rest object mapper provider:

import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import org.codehaus.jackson.map.ObjectMapper;
@Provider
public class RestObjectMapperProvider implements ContextResolver<ObjectMapper>
{
   private final ObjectMapper objectMapper;

   public RestObjectMapperProvider()
   {
       this.objectMapper = new ObjectMapper();

       this.objectMapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
   }

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

I'm building my app with Gradle and below is the Jackson dependency:

compile group: 'org.codehaus.jackson', name: 'jackson-jaxrs', version: '1.9.+'

Since under Tomcat (Jersey) the annotation are considered, my guess would be that in Wildfly my exclusions are not considered. Is there any way to check which JSON Provider was considered, beside checking the response?

like image 825
Alin Avatar asked Feb 23 '17 12:02

Alin


2 Answers

I'm curious why you are using Jackson 1.x instead of 2.x? The instructions are similar for both but the Jackson Configuration would have to change. Below are instructions for 1.x.

jboss-deployment-structure.xml (same as yours):

<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
<deployment>
    <exclusions>
        <module name="org.jboss.resteasy.resteasy-jackson2-provider"/>
        <module name="org.jboss.resteasy.resteasy-jettison-provider"/>
    </exclusions>
    <dependencies>
        <module name="org.jboss.resteasy.resteasy-jackson-provider" services="import"/>
    </dependencies>
</deployment>
</jboss-deployment-structure>

Register your application with @Application:

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

    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> classes = new HashSet<Class<?>>();

        classes.add(JacksonConfigurationProvider.class);
        classes.add(TestResource.class);

        return classes;
    }

}

Use a jackson configuration provider like:

@Provider
@Consumes({MediaType.APPLICATION_JSON, "text/json"})
@Produces({MediaType.APPLICATION_JSON, "text/json"})
public class JacksonConfigurationProvider extends ResteasyJacksonProvider {

    private static final Logger LOGGER = LoggerFactory.getLogger(JacksonConfigurationProvider.class);

    public JacksonConfigurationProvider() {
        super();

        LOGGER.info("loading jackson configurator");

        JacksonObjectMapper mapper = JacksonObjectMapper.get();
        setMapper(mapper);

    }
}

Then, in your JacksonObjectMapper is where you tell it to read the annotations:

public class JacksonObjectMapper extends ObjectMapper {
    public static JacksonObjectMapper get() {
        JacksonObjectMapper mapper = new JacksonObjectMapper();

        mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
        mapper.configure(SerializationConfig.Feature.WRAP_ROOT_VALUE, true);

        JacksonAnnotationIntrospector jacksonAnnotationIntrospector = new JacksonAnnotationIntrospector();
        mapper.setDeserializationConfig(mapper.getDeserializationConfig().withAnnotationIntrospector
                (jacksonAnnotationIntrospector));
        mapper.setSerializationConfig(mapper.getSerializationConfig().withAnnotationIntrospector
                (jacksonAnnotationIntrospector));

        return mapper;
    }
}

My Jackson Object looks like this:

@JsonRootName("foobar")
public class TestType {

    @JsonProperty("goodbye")
    String hello;

    public String getHello() {
        return hello;
    }

    public void setHello(String hello) {
        this.hello = hello;
    }
}

As a convienence I also have this in my web.xml, it allows you to request json by just putting .json on the URL rather than setting headers:

<context-param>
    <param-name>resteasy.media.type.mappings</param-name>
    <param-value>json : application/json, xml : application/xml</param-value>
</context-param>

When I call http://127.0.0.1:8080/api/v1/test.json I get:

{
    "foobar": {
        "goodbye": "Here is some random string"
    }
}

I have posted my full working code here: https://github.com/teacurran/java-experiments/tree/master/stackoverflow-sandbox/Q42416036

like image 117
teacurran Avatar answered Nov 11 '22 17:11

teacurran


The fix was to put the exclusions and the dependencies in the sub-deployment, instead of deployment tag, as I was doing.

<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
<sub-deployment name="axis.war">
    <exclusions>
        <module name="org.jboss.resteasy.resteasy-jackson2-provider"/>
        <module name="org.jboss.resteasy.resteasy-jettison-provider"/>
    </exclusions>
    <dependencies>
        <module name="org.jboss.resteasy.resteasy-jackson-provider" services="import"/>
    </dependencies>
</sub-deployment>

like image 44
Alin Avatar answered Nov 11 '22 16:11

Alin