Tuesday, 15 September 2015

Foolproof method of excluding Resteasy API provided byJBoss 6 EAP or JBoss 7 AS from application

A few months back I was working on a REST based web service application using CXF. CXF provides a cool XML file out of box describing the methods available in the REST interfaces. Since I was using CXF I did not want JBoss scanning my REST interfaces and interfering with the classloader of my application. That's right class loader of the application (WAR, EAR). I'm taking a detour to explain what I meant there. There's a hierarchy of classloaders in JVM. In general if an application requires a class it asks it's classloader, this classloader asks it's parent classloader first. This way it's made sure that each class is uniquely available to entire JVM as the resolution is done by the parent classloaders first. It's an object hierarchy not a class hierarchy (not parent child, it's just a list). However, in case of servlet containers or JavaEE containers, applications try to load the class themselves first, instead of asking for parent first. This was done so that web application is run with the versions of the classess that were packed with it.

 Reference : Do You Really Get Class Loaders? A video from the ZeroTurnaround, creators or JRebel

Coming back to the topic, since there were two different implementations of REST API (resteasy and CXF) available to my application, there was a conflict. No matter what I did, I could not get the JBoss to serve the WADL document. I was sure that this was happening because of the interference of Resteasy with the CXF. I looked around and couldn't find the foolproof method. Most were asking to exclude Resteasy, JAXRS subsystems in jboss-deployment-structure.xml file. Well I did that and that didn't work. I thought may be it couldn't solve my problem because I was trying to force solutions for the old JBoss version on the new one. However the problem runs deeper.

JBoss 6 has a maven like module system (just maven like, dependencies and all in "modules" directory). Every web application has a dependency on JAVAEE module, which in turn loads Resteasy and what not as dependency. (modules/javaee/api/main/module.xml)

I could'nt exlude JAVAEE module, or could I? It turned out that I could actually exclude JAVAEE entirely, and as JAVAEE module itself is just a name for a collection of dependency modules, I could now load these modules selectively. Take a look at modules/javaee/api/main/module.xml to see what dependencies are loaded by JAVAEE module. If you try this method, it's guaranteed that this will cause less suffering and pain than any other method out there. Just start with everything(dependencies) excluded, except for the ones you know that no Java application can run without and start adding dependencies by taking the hint from the error that JBoss gives you during the deployment. I'm copying a sample jboss-deployment-structure.xml file here. It should be packed in the WEB-INF directory of the web application.

And yes, this solved my problem, finally I was able to get the CXF provided WADL document.
As you can see I have excluded parts which even remotely whisper web service.

Sample WEB-INF/jboss-deployment-structure.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
  <deployment>
    <exclude-subsystems>
      <subsystem name="jaxrs" />
      <subsystem name="webservices" />
      <subsystem name="resteasy" />
    </exclude-subsystems>
    <exclusions>
      <module name="javaee.api" />
      <module name="javax.ws.rs.api" />
      <module name="org.jboss.as.jaxrs" />
      <module name="org.jboss.resteasy.resteasy-jaxrs" />
      <module name="org.jboss.resteasy.resteasy-cdi" />
      <module name="org.jboss.resteasy.jackson-provider" />
      <module name="org.jboss.resteasy.resteasy-atom-provider" />
      <module name="org.jboss.resteasy.resteasy-hibernatevalidator-provider" />
      <module name="org.jboss.resteasy.resteasy-jaxb-provider" />
      <module name="org.jboss.resteasy.resteasy-jettison-provider" />
      <module name="org.jboss.resteasy.resteasy-jsapi" />
      <module name="org.jboss.resteasy.resteasy-multipart-provider" />
      <module name="org.jboss.resteasy.resteasy-yaml-provider" />
      <module name="org.codehaus.jackson.jackson-core-asl" />
      <module name="org.codehaus.jackson.jackson-jaxrs" />
      <module name="org.codehaus.jackson.jackson-mapper-asl" />
      <module name="org.codehaus.jackson.jackson-xc" />
      <module name="org.codehaus.jettison" />
      <module name="org.jboss.as.webservices.*" />
      <module name="org.jboss.ws.*" />
    </exclusions>

    <dependencies>
      <module name="javax.activation.api" export="true" />
      <module name="javax.annotation.api" export="true" />
      <!-- <module name="javax.ejb.api" export="true" />
      <module name="javax.el.api" export="true" /> -->
      <module name="javax.enterprise.api" export="true" />
      <module name="javax.enterprise.deploy.api" export="true" />
      <module name="javax.inject.api" export="true" />
      <module name="javax.interceptor.api" export="true" />
      <!-- <module name="javax.jms.api" export="true" />
      <module name="javax.jws.api" export="true" />
      <module name="javax.mail.api" export="true" />
      <module name="javax.management.j2ee.api" export="true" /> -->
      <module name="javax.persistence.api" export="true" />
      <module name="javax.resource.api" export="true" />
      <!-- <module name="javax.rmi.api" export="true" />
      <module name="javax.security.auth.message.api" export="true" />
      <module name="javax.security.jacc.api" export="true" /> -->
      <module name="javax.servlet.api" export="true" />
      <module name="javax.servlet.jsp.api" export="true" />
      <module name="javax.transaction.api" export="true" />
      <module name="javax.validation.api" export="true" />
      
      <!-- <module name="javax.ws.rs.api" export="true" services="export" /> -->
      
      <module name="javax.xml.bind.api" export="true" />
      <module name="javax.xml.registry.api" export="true" />
      <module name="javax.xml.soap.api" export="true" />
      <module name="javax.xml.ws.api" export="true" />

      <!-- This one always goes last. -->
      <module name="javax.api" export="true" />
    </dependencies>

  </deployment>
</jboss-deployment-structure>

In this application I was loading my own Jackson library, so that's also excluded.
...till next time

5 comments:

  1. Hi,

    I'm trying to deploy an application that uses Jersey on WildFly 10, and I've tried your method to replace resteasy with no success.

    Now I'm getting:
    "Caused by: org.jboss.modules.ModuleNotFoundException: javax.enterprise.deploy.api:main"

    Have gotten this kind of error? Do you know if there is something more to do to solve this?

    Regards,

    ReplyDelete
    Replies
    1. FWIW, I have been working on a deployment of my company's Web-app on to WildFly and have encountered the exact failure to deploy because of the same symptom: 'missing module javax.enterprise.deploy.api'.

      This occurs when I want to exclude 'javax.mail.api' v1.4.4 from the vanilla WildFly and then have it use OUR 'java.mail.api; v1.4.
      I started off with a jboss-deployment-structure.xml that successfully loads our java.mail.api on EAP 6.
      Using this same jboss-deployment-structure.xml fails on WildFly.

      By trial and error I narrowed down 'the problem configuration' that 'caused' the 'error'. I had 'javaee.api' in the list and added back via all the excluded default modules from javaee.api except for the special javax.mail.api, which was added using our version in it's slot:

      It turns out that adding this custom module 'caused' the deployment failure. If I only exclude javax.mail.api, then WildFly loads our WEB-INF/lib javax.mail.api.
      I guess this is because of some new classloading behavior that has changed between AS7 and WildFly. If I had more time I'd investigate the cause, but I don't... It took me enough time just to get this 'workaround' going...

      Delete
  2. Hi Reginaldo,
    I apologize for late response.

    I have used this method with JBoss AS and EAP servers. I haven't tried with WildFly.

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. This worked for me (jboss-eap-6.4). Thanks.

    ReplyDelete