OpenWebBeans
  1. OpenWebBeans
  2. OWB-629

NoClassDefFoundError for optional dependencies

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 1.1.2
    • Fix Version/s: 1.1.3
    • Component/s: Core
    • Labels:
      None
    • Environment:
      Tomcat 7.0.22, JDK 1.6.0_23

      Description

      Some of the Seam modules (like Seam Persistence) contain optional Maven dependencies. The persistence module for example declares Hibernate as an provided dependency. The Seam Persistence module contains only one single class that requires Hibernate which is an SPI implementation to support specific Hibernate features. This class is only loaded/used if Hibernate is detected. Furthermore it's important to know that this bean is not meant to be a bean manged by the CDI runtime. Therefore it is annotated with @Veto (Seam Solder API) to get vetoed during CDI startup.

      All this works very fine with Weld 1.1.x. Unfortunately OWB fails to deploy the Seam Persistence JAR with the following exception:

      SCHWERWIEGEND: Exception sending context initialized event to listener instance of class org.apache.webbeans.servlet.WebBeansConfigurationListener
      java.lang.NoClassDefFoundError: org/hibernate/TransientObjectException
      at java.lang.Class.forName0(Native Method)
      at java.lang.Class.forName(Class.java:264)
      at org.apache.webbeans.util.ClassUtil.getClassFromName(ClassUtil.java:115)
      at org.apache.webbeans.corespi.scanner.AbstractMetaDataDiscovery.getBeanClasses(AbstractMetaDataDiscovery.java:209)
      at org.apache.webbeans.config.BeansDeployer.checkStereoTypes(BeansDeployer.java:732)
      at org.apache.webbeans.config.BeansDeployer.deploy(BeansDeployer.java:160)
      at org.apache.webbeans.lifecycle.AbstractLifeCycle.startApplication(AbstractLifeCycle.java:127)
      at org.apache.webbeans.web.lifecycle.WebContainerLifecycle.startApplication(WebContainerLifecycle.java:88)
      at org.apache.webbeans.servlet.WebBeansConfigurationListener.contextInitialized(WebBeansConfigurationListener.java:85)
      at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4723)
      at org.apache.catalina.core.StandardContext$1.call(StandardContext.java:5226)
      at org.apache.catalina.core.StandardContext$1.call(StandardContext.java:5221)
      at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
      at java.util.concurrent.FutureTask.run(FutureTask.java:166)
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
      at java.lang.Thread.run(Thread.java:679)
      Caused by: java.lang.ClassNotFoundException: org.hibernate.TransientObjectException
      at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1678)
      at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1523)
      ... 17 more

      For me it looks like the class scanner tries to build a set of Class objects from all classes in the bean archive. But this fails as soon as the class compiled against the Hibernate API is picked up as Hibernate is not on the classpath.

      If I remember correctly Weld 1.0.x had similar issues. Somehow this was resolved in Weld 1.1.x but I don't know anything about the details.

      I think OWB could handle NoClassDefFoundErrors a bit nicer. Currently OWB doesn't support bean archives that have optional dependencies at all. So an archive that contains just one class for which not all dependencies (classes) are accessible from the ClassLoader completely fails to deploy using OWB. Even if this class isn't used at all (neither within CDI nor manually instantiated in the application code).

      I think it would be easy to fix this. ClassUtil.getClassFromName() or AbstractMetaDataDiscovery.getBeanClasses() could catch NoClassDefFoundError and handles them in some way. I think it would make sense to simply ignore such errors during the bean archive scanning process on continue with the next class.

      1. OWB-629-first-try.patch
        2 kB
        Christian Kaltepoth
      2. owb-test-updated.zip
        72 kB
        Christian Kaltepoth
      3. owb-test.zip
        72 kB
        Christian Kaltepoth

        Activity

        Hide
        Mark Struberg added a comment -

        thanks again to Christian Kaltepoth for contributing this patch!

        Show
        Mark Struberg added a comment - thanks again to Christian Kaltepoth for contributing this patch!
        Hide
        Mark Struberg added a comment -

        Committed. Thanks Christian for contributing this patch!

        Show
        Mark Struberg added a comment - Committed. Thanks Christian for contributing this patch!
        Hide
        Christian Kaltepoth added a comment -

        First version of a patch for this issue.

        Show
        Christian Kaltepoth added a comment - First version of a patch for this issue.
        Hide
        Christian Kaltepoth added a comment -

        I've created a first simple version of a patch for this issue. Please note that it is more or less a quickfix. But it works fine.

        Let me first explain what Weld does to prevent such problems. Weld creates one "WeldClassImpl" object for every class it finds. This object holds all information of a class like it's fields, methods, constructors and so on. All the reflection is done in the constructor of WeldClassImpl. If some of the reflection code fails (due to a NoClassDefFoundError for example) an info message is created in the logs and the class is ignored for the deployment.

        My first try to fix this issue in OWB was to simply catch NoClassDefFoundError when loading the class. Unfortunately this was not enough. It seems like in some cases loading the class works fine but later when getDeclaredFields() is called similar exceptions are thrown. Weld doesn't suffer from these issues as all reflection is done initially for the class when creating WeldClassImpl.

        My patch does the following. It tries to load the class using ClassUtil.getClassFromName(). If this succeeds it invokes getDeclaredFields() and getDeclaredFields(). This is done just to provoke a NoClassDefFoundError if some dependencies of the class are missing. If an exception is thrown, a warning is created in the logs and the class is ignored.

        I know this is probably a quickfix. There are definitively nicer ways to do it. But it seems to work fine. I was able to deploy an application that includes Seam Persistence and Seam Faces without any issues which wasn't the case with plain 1.1.2.

        I hope my explanations helps a bit!

        Show
        Christian Kaltepoth added a comment - I've created a first simple version of a patch for this issue. Please note that it is more or less a quickfix. But it works fine. Let me first explain what Weld does to prevent such problems. Weld creates one "WeldClassImpl" object for every class it finds. This object holds all information of a class like it's fields, methods, constructors and so on. All the reflection is done in the constructor of WeldClassImpl. If some of the reflection code fails (due to a NoClassDefFoundError for example) an info message is created in the logs and the class is ignored for the deployment. My first try to fix this issue in OWB was to simply catch NoClassDefFoundError when loading the class. Unfortunately this was not enough. It seems like in some cases loading the class works fine but later when getDeclaredFields() is called similar exceptions are thrown. Weld doesn't suffer from these issues as all reflection is done initially for the class when creating WeldClassImpl. My patch does the following. It tries to load the class using ClassUtil.getClassFromName(). If this succeeds it invokes getDeclaredFields() and getDeclaredFields(). This is done just to provoke a NoClassDefFoundError if some dependencies of the class are missing. If an exception is thrown, a warning is created in the logs and the class is ignored. I know this is probably a quickfix. There are definitively nicer ways to do it. But it seems to work fine. I was able to deploy an application that includes Seam Persistence and Seam Faces without any issues which wasn't the case with plain 1.1.2. I hope my explanations helps a bit!
        Hide
        Christian Kaltepoth added a comment -

        Updated example application. There is now a "weld" Maven profile to test the application against the most current Weld release.

        Show
        Christian Kaltepoth added a comment - Updated example application. There is now a "weld" Maven profile to test the application against the most current Weld release.
        Hide
        Christian Kaltepoth added a comment -

        This is a simple sample application to reproduce this. There are two Seam dependencies (Seam Persistence and Seam Transaction) included. The WAR will fail to deploy if these dependencies are included in the WAR file. Tested on Tomcat 7.0.22.

        Show
        Christian Kaltepoth added a comment - This is a simple sample application to reproduce this. There are two Seam dependencies (Seam Persistence and Seam Transaction) included. The WAR will fail to deploy if these dependencies are included in the WAR file. Tested on Tomcat 7.0.22.

          People

          • Assignee:
            Mark Struberg
            Reporter:
            Christian Kaltepoth
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development