7 Replies Latest reply on Mar 18, 2003 7:22 AM by dokomesiter

    Classloader problem with cascaded manifest

    jamoville

      Here is the issue:
      jboss 3.0.6

      I have a ear file. This ear file contains a war file and a lib/utils.jar and lib/utils2.jar. The manifest of the war file contains a class-path of lib/utils.jar and the lib/utils.jar contains a manifest with a class-path of utils2.jar. I get classnotfound exceptions when the war file calls classes of the lib/utils.jar that use classes of the lib/utils2.jar. Basically the lib/utils.jar is loaded and the lib/utils2.jar is not. It's as if the manifest of the lib/utils.jar is ignored. According to the new 3.0.5/3.2 jboss-classloader.pdf documentation I would have to modify the manifest of the utils.jar which could very well be a third party jar. Here is the excerpt from the documentation:

      Classes that need to be shared between web application
      components and other components such as EJBs, and MBeans need to be loaded into the shared class
      loader repository either by including the classes into a SAR or EJB deployment, or by referencing a
      jar containing the shared classes through a manifest Class-Path entry.

      This seems to break the cascading behavior of the manifest file. Is this a bug?

        • 1. Another Classloader manifest problem
          dokomesiter

          Simpler version of the same thing, and it looks like a classloader bug (at least as of 3.0.5)???: EJB Jars must contain the utility jars they reference because they will not find them inside the EAR file (regardless of jar manifest class-path).

          ie, jars must contain other jars instead of having the ear contain all the jars. (yuck)

          Example:

          if class A instantiates
          class B which instantiates
          class C
          and each is in their own jar with Class-Path references
          A.JAR
          META-INF/MANIFEST.MF
          Class-Path: B.JAR
          B.JAR
          META-INF/MANIFEST.MF
          Class-Path: C.JAR
          C.JAR
          Then everything is done correctly according to the J2EE spec.

          What should work:
          COMBINE.EAR=(A.JAR, B.JAR, C.JAR)
          ie
          jar a.jar a.class ...
          jar b.jar b.class ...
          jar c.jar c.class ...
          jar combine.ear a.jar b.jar c.jar ...

          What does work:
          COMBINE.EAR=(A.JAR=(B.JAR, C.JAR))
          ie
          jar c.jar c.class ...
          jar b.jar b.class ...
          jar a.jar a.class b.jar c.jar ... // bug workaround
          jar combine.ear a.jar ...

          In other words, the other jars have to be embedded within the first jar rather than simply packaged with the ear (look at petstore.ear's tracer.jar as an example)

          Class A is an EJB
          Classes B and C are utility POJOs

          This ClassLoader ClassNotFound problem is easily reproducible in 10 or so lines of code, both in Servlet and RichClient applications.

          What also works, and is also not very pretty, is to not use EARs at all, but dump all the jars in /deploy.

          Since we have hundreds of utility jars that's gunna be ugly, and it kills "hot-deploy".

          Any ideas or words of wisdom?

          Thanks

          Chris

          • 2. Re: Classloader problem with cascaded manifest
            yurim

            As far as I understand from:

            http://www.onjava.com/lpt/a/961

            referring to other libraries by specification is available only from .jar files and not from .ear files. Probably it is not a bug.

            • 3. Re: Classloader problem with cascaded manifest
              dokomesiter

              To quote a colleague of mine:
              "Sorry, but that article is almost 2 years old and refers to 1.3. Out of date at best; I'm sure things have changed in 1.4 and containers have improved dramatically during that time."

              And would it not be be a little silly to have utility.jar embedded inside every other jar rather than just have a single version inside the ear.

              jboss way (seem very wrong)
              jar a.jar a.class util1.jar util2.jar
              jar b.jar b.class util1.jar util2.jar
              jar c.jar c.class util1.jar util2.jar
              jar combo.ear a.jar b.jar c.jar

              correct way:
              jar a.jar a.class
              jar b.jar b.class
              jar c.jar c.class
              jar combo.ear a.jar b.jar c.jar util1.jar util2.jar

              in either clase, the manifests of a, b, and c point to
              util1.jar, util2.jar, as it

              a.jar, manifest.mf
              Class-Path: util1.jar util2.jar

              so the only question is whether or not one needs multiple copies of the same jar file inside the container (and for us, that might be hundreds of copies of the same jar file, that seems wrong)



              • 4. JBoss Classloader Bug
                dokomesiter

                Here is an tiny standalone example showing the JBoss Classloader problem: utility jars have to be inside other jars and in web-inf/lib instead of in the EAR, even when referenced by Class-Path in manifest.

                Attachment: loadbug.zip

                Usage:
                1. unzip
                2. ant deploy
                3. ant test.richclient

                This is *not* how J2EE Reference Implementation (j2ee-ri) does it; instead of putting utility JARS inside ejb JARS, it puts the utility JARS in the EAR (in a /library directory relative to the ear) where all other jars inside the ear that have class-path cna laod those classes.

                In JBoss it yields NoClassDefFoundError.

                It looks like RI does it the right way, and JBoss does not.

                Cheers,

                Chris

                • 5. Re: Classloader problem with cascaded manifest
                  fred

                  The A_Bean.jar manifest classpath declaration is incorrect. It should read:

                  Class-Path: B_Util.jar A_Interface.jar

                  instead of:

                  Class-Path: B_Util.jar
                  Class-Path: A_Interface.jar

                  With that change, the test works.

                  • 6. Re: Classloader problem with cascaded manifest
                    dokomesiter

                    You are absolutely correct, I am wrong.

                    In my comparison with JBoss I used the "clean" build.xml
                    <jar ..




                    with JBoss, and with RI I used the deploytool which lists each of the referenced JARs on a separate line but concatenates them in the deployment descriptor.

                    Sorry about the fuss, though I had done due diligence, but was very mistaken

                    Great catch, thanks...

                    • 7. Re: Classloader problem with cascaded manifest
                      dokomesiter

                      Maybe JBoss isn't as egregiously wrong as thought, but on closer look at the doc it still apears to be a bug.

                      From Sun's online J2EE 1.3 documentation

                      http://java.sun.com/docs/books/tutorial/ext/basics/download.html

                      <near the bottom of the page>

                      "You can also specify multiple extension URLs by using more than one Class-Path header in the manifest. For example:

                      Class-Path: area.jar
                      Class-Path: servlet.jar

                      Download extensions can be "daisy chained", meaning that the manifest of one download extension can have a Class-Path header that refers to a second extension, which can refer to a third extension, and so on. "