WildFly
  1. WildFly
  2. WFLY-894

javax.ws.rs.ext.FactoryFinder use application's Class Loader which causes CL Memory Leak

    Details

    • Similar Issues:
      Show 10 results 

      Description

      Deploying a Seam 2 application with Resteasy (deployed as JAR withing EarContent/lib) actually causes the following issue:

      • When Seam access classes such as CacheControl, Cookie, EntityTag, MediaType or NewCookie, it loads my application's Resteasy classes, as using the CurrentThread's context class loader.
      • So I ended up with the static field delegate of those classes keeping a reference to my application's class loader which cause a CL Memory Leak.

      Since, the javax.ws.rs.core.Cookie/Entitytag, etc.. are server side components, I don't think they should refer to any deployed application.

      To avoid this, the only way to do so, was at the server start-up to initialize those components in that way:

      try {
          javax.ws.rs.core.CacheControl.valueOf("");
      } catch (IllegalArgumentException e) {
      
      }
      try {
          javax.ws.rs.core.Cookie.valueOf("");
      } catch (IllegalArgumentException e) {
      
      }
      try {
          javax.ws.rs.core.EntityTag.valueOf("");
      } catch (IllegalArgumentException e) {
      
      }
      try {
          javax.ws.rs.core.MediaType.valueOf("");
      } catch (IllegalArgumentException e) {
      
      }
      try {
          javax.ws.rs.core.NewCookie.valueOf("");
      } catch (IllegalArgumentException e) {
      
      }

      (I put this in org.jboss.as.server.Main before line 91, but this is not the best place of course. Perhaps a class such as JreMemoryLeakPreventionListener should be used, but for the moment the use of this listener is disabled in JBoss Web 7, see https://community.jboss.org/thread/164760 )

      But, since the RuntimeDelegate needed to find the Resteasy classed, I had to use the module ClassLoader instead of the current context class loader in the javax.ws.rs.ext.FactoryFinder.getContextClassLoader() method. Then, as the javax.ws.rs.api module actually depends on the org.jboss.resteasy.resteasy-jaxrs, this works well and avoids any dependency to my application's class loader.

        Activity

        Hide
        Jason Greene
        added a comment -

        Are you by chance bundling any resteasy jars in your app?

        Show
        Jason Greene
        added a comment - Are you by chance bundling any resteasy jars in your app?
        Hide
        Philippe Guinot
        added a comment -

        Yeah, that's it. Resteasy is deployed within EarContent/lib.

        Maybe we should only use Resteasy as a Module, but I was actually running with a different memory leak issue in that case (cf. https://issues.jboss.org/browse/RESTEASY-660 - but seems to actually be a Seam 2 issue)

        Show
        Philippe Guinot
        added a comment - Yeah, that's it. Resteasy is deployed within EarContent/lib. Maybe we should only use Resteasy as a Module, but I was actually running with a different memory leak issue in that case (cf. https://issues.jboss.org/browse/RESTEASY-660 - but seems to actually be a Seam 2 issue)
        Hide
        Bill Burke
        added a comment -

        I don't see why this is a problem. If you're using the resteasy modules, won't the resteasy module's classloader be used as it is a "parent" of the application's classloader?

        Show
        Bill Burke
        added a comment - I don't see why this is a problem. If you're using the resteasy modules, won't the resteasy module's classloader be used as it is a "parent" of the application's classloader?
        Hide
        Philippe Guinot
        added a comment -

        No, the current context loader will be used which is the application's CL, and so this may result as keeping final references to classes from the application if any META-INF/service is present within the application (with Resteasy or any other 3rd party library using javax.ws.rs).

        Show
        Philippe Guinot
        added a comment - No, the current context loader will be used which is the application's CL, and so this may result as keeping final references to classes from the application if any META-INF/service is present within the application (with Resteasy or any other 3rd party library using javax.ws.rs).
        Hide
        Jason Greene
        added a comment -

        The main problem, as you observed, is with the JAX-RS api. It's designed to support a pluggable (replaceable) implementation (uses TCCL and scans for META-INF/services much like JAXP, but the problem is that it caches the results in a static variable which contradicts plug-ability. We need to investigate what it would take to fix it (although it will have to wait until after 7.1.0).

        There is another issue though. If the app server detects usage of JAX-RS in your deployment it adds dependencies on resteasy (and the JAX-RS api) which are intended to be in such an order that it would override your usage. Ear/lib is, however, taking precedence, which it shouldn't be. So that's the second problem.

        99% of the time you don't want to override the JAX-RS impl we provide because you will disable integration with other EE capabilities. However if you do want to provide your own, the solution at the moment would be to add an exclusion to your deployment to filter out inclusion of "javax.ws.rs.api" and all of the resteasy modules. If you do provide your own you will have to do things like declare the resteasy servlet in web.xml, which are normally automated.

        Show
        Jason Greene
        added a comment - The main problem, as you observed, is with the JAX-RS api. It's designed to support a pluggable (replaceable) implementation (uses TCCL and scans for META-INF/services much like JAXP, but the problem is that it caches the results in a static variable which contradicts plug-ability. We need to investigate what it would take to fix it (although it will have to wait until after 7.1.0). There is another issue though. If the app server detects usage of JAX-RS in your deployment it adds dependencies on resteasy (and the JAX-RS api) which are intended to be in such an order that it would override your usage. Ear/lib is, however, taking precedence, which it shouldn't be. So that's the second problem. 99% of the time you don't want to override the JAX-RS impl we provide because you will disable integration with other EE capabilities. However if you do want to provide your own, the solution at the moment would be to add an exclusion to your deployment to filter out inclusion of "javax.ws.rs.api" and all of the resteasy modules. If you do provide your own you will have to do things like declare the resteasy servlet in web.xml, which are normally automated.
        Hide
        Jason Greene
        added a comment -

        We are trying to figure out how this ordering problem could happen. Could you provide a detailed description of your deployment structure. Specifically where are your classes that are annotated with JAX-RS annotations, what exlusions/deps you have added to any subdeployments, or you using any other library that would pull in resteasy via some other path. Perhaps even a unzip -l output of your ear.

        Thanks.

        Show
        Jason Greene
        added a comment - We are trying to figure out how this ordering problem could happen. Could you provide a detailed description of your deployment structure. Specifically where are your classes that are annotated with JAX-RS annotations, what exlusions/deps you have added to any subdeployments, or you using any other library that would pull in resteasy via some other path. Perhaps even a unzip -l output of your ear. Thanks.
        Hide
        Philippe Guinot
        added a comment -

        Sorry for the late reply.

        Well, the EAR application just comes with Seam 2.2.2 and Resteasy... All the other dependencies are managed with modules.

        Then, you don't need to create an annotated class to re-create the issue, the make a static call to
        javax.ws.rs.core.CacheControl.valueOf("");
        within your application and it'll keep a reference to the application class loader.

        If you really need to know when seam/resteasy from my application use CacheControl for the first time, I'll give you the stack.

        Show
        Philippe Guinot
        added a comment - Sorry for the late reply. Well, the EAR application just comes with Seam 2.2.2 and Resteasy... All the other dependencies are managed with modules. Then, you don't need to create an annotated class to re-create the issue, the make a static call to javax.ws.rs.core.CacheControl.valueOf(""); within your application and it'll keep a reference to the application class loader. If you really need to know when seam/resteasy from my application use CacheControl for the first time, I'll give you the stack.
        Hide
        Philippe Guinot
        added a comment -

        With JBoss 7.1.1 I tried to add calls to

        javax.ws.rs.core.CacheControl
        javax.ws.rs.core.Cookie
        javax.ws.rs.core.EntityTag
        javax.ws.rs.core.MediaType
        javax.ws.rs.core.NewCookie
        

        in the init phase of a LifecycleListener defined in my war's jboss-web.xml. In this phase the current class loader in not my application's CL by the jboss web module's cl.

        However, my seam EJB must be deployed before my web-app, therefore Seams calls org.jboss.seam.resteasy.ResteasyBootstrap.init() before the init phase of the listener. At that point the current class loader is my application's seam jar's cl...

        It sounds there is no way within the application to work around this issue.

        Show
        Philippe Guinot
        added a comment - With JBoss 7.1.1 I tried to add calls to javax.ws.rs.core.CacheControl javax.ws.rs.core.Cookie javax.ws.rs.core.EntityTag javax.ws.rs.core.MediaType javax.ws.rs.core.NewCookie in the init phase of a LifecycleListener defined in my war's jboss-web.xml. In this phase the current class loader in not my application's CL by the jboss web module's cl. However, my seam EJB must be deployed before my web-app, therefore Seams calls org.jboss.seam.resteasy.ResteasyBootstrap.init() before the init phase of the listener. At that point the current class loader is my application's seam jar's cl... It sounds there is no way within the application to work around this issue.
        Hide
        Stuart Douglas
        added a comment -

        Due to the way the API is designed there is no easy way to fix this, as the API classes themselves hold a static reference to the implementation.

        Fortunatly in Wildfly upstream it is much easier to exlude our JAX-RS API implementation as a dependency, allowing you to package the JAX-RS API classes in the jar, which will prevent a leak.

        Show
        Stuart Douglas
        added a comment - Due to the way the API is designed there is no easy way to fix this, as the API classes themselves hold a static reference to the implementation. Fortunatly in Wildfly upstream it is much easier to exlude our JAX-RS API implementation as a dependency, allowing you to package the JAX-RS API classes in the jar, which will prevent a leak.

          People

          • Assignee:
            Stuart Douglas
            Reporter:
            Philippe Guinot
          • Votes:
            0 Vote for this issue
            Watchers:
            9 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: