6 Replies Latest reply on Dec 1, 2006 6:40 AM by Claus Dreyer

    J2EE classloading question (I've read the CL wiki topics)

    Laird Nelson Apprentice

      I am using JBoss 4.0.3SP1 on Windows XP Professional.

      I have an isolated ear with its own loader repository (via jboss-app.xml). Its classloader is set to look inwards first before delegating to its parent (java2ClassloadingCompliance=false).

      Inside the ear I have a garden variety utility jar, framework.jar. Inside the ear I also have a .war file, startup.war.

      That war file has a MANIFEST.MF file with a Class-Path entry like this:
      Class-Path: framework.jar

      Finally, there is a servlet in the .war that calls a class from framework.jar.

      Framework.jar in turn at one point does a Class.forName("config.ConfigAnchor"). That class is in startup.war/WEB-INF/classes.

      This setup gives me a NoClassDefFoundError when framework.jar tries to load config.ConfigAnchor.

      The J2EE specification indicates that this should work, and in fact must work. I am happy to quote the section if necessary.

      What must I do to enable this spec-compliant deployment in JBoss 4.0.3SP1?

      My working hypothesis is that the classloader that loads startup.war correctly looks inwards first (i.e. to WEB-INF/classes, WEB-INF/lib, etc.), but then perhaps incorrectly? ignores the Class-Path manifest entry present in the containing war file. What seems to be happening is that the classes in framework.jar are loaded by the ear classloader (why are they even preloaded at all? Shouldn't they be totally opaque to the system until referenced by another jar within the ear via a Manifest Class-Path entry?!), and that therefore when framework.jar does a Class.forName("config.ConfigAnchor") call, it can't see inside the .war file.

      I must say I'm not expecting much here, but if anyone has any pointers, they'd be greatly appreciated.

      Thanks,
      Laird

        • 1. Re: J2EE classloading question (I've read the CL wiki topics
          jaikiran pai Master

          NoClassDefFoundException, means that the classes/jars reffered by config.ConfigAnchor, through its import statements or extends clause or through some other means, is NOT present in the classpath. Make available the related classes/jars in the classpath.

          Remember, ClassNotFoundException and NoClassDefFoundException are two different things.

          • 2. Re: J2EE classloading question (I've read the CL wiki topics
            Laird Nelson Apprentice

            Yes, thank you. The specific error is:
            NoClassDefFoundError: No ClassLoaders found for config.ConfigAnchor
            ...which is a JBoss-specific error, I have to guess.

            I was wrong in the sense that a Class.forName() is not happening; there is a specific reference to config.ConfigAnchor. So, as you say, config.ConfigAnchor is not showing up in the classpath, even though I have all the references (MANIFEST.MF Class-Path entries) in place properly.

            Now what is weird to me is this: I understand how this error would occur if you simply look at the situation from the ear's point of view. In there, you have a framework.jar library that contains a normal static reference to config.ConfigAnchor. If the code should ever flow in that direction, you'll get a NoClassDefFoundError, because config.ConfigAnchor is not present in framework.jar. All fine and good.

            But what I do NOT understand is that we're looking at this from the standpoint of the war file. That is, the call graph starts from StartupServlet.init() (loaded by the webapp loader). I would THINK that framework.jar would be added to the classpath of the webapp loader by virtue of the MANIFEST.MF entry. That would mean that the framework.jar, the StartupServlet.class and the config.ConfigAnchor.class would all be in the classpath of the webapp loader. That is not what I'm seeing.

            It almost looks like JBoss is doing something weird or stupid and is adding the ear jars to the ear loader's classpath, which of course is not correct according to J2EE1.4. That is, jars in the ear are supposed to be sort of dormant or invisible until they're referenced by someone else's classpath. In this case, that means that my case should work, because framework.jar would be not-present-in-any-classpath until the webapp loader sucked it in via the war file's manifest.

            Laird

            • 3. Re: J2EE classloading question (I've read the CL wiki topics
              Laird Nelson Apprentice

              Let me refine my last post. I believe it is the specification's intention that "normal" utility jars in an ear are available for inclusion in a classpath, but should not be included in that classpath by default.

              So framework.jar can be roped in to someone else's classpath by way of a MANIFEST.MF Class-Path: entry, but should NOT be dumped into a classpath by default.

              For that matter--and I freely admit ignorance here--why does the ear loader need a classpath inside it at all? It seems that one must be in place.

              • 4. Re: J2EE classloading question (I've read the CL wiki topics
                Laird Nelson Apprentice

                Since I'm just talking to myself here, I figure I'll do it here so others can find this in a Google search later.

                To illustrate how most (every other) environments handle classloading and Manifest Class-Path entries, try this little experiment.

                Create three jars, a.jar, b.jar and c.jar. Give a.jar and b.jar manifest files that reference c.jar.

                Now point a URLClassLoader at a, and another one at b.jar.

                Although they are, in some sense, "sharing" a single copy of c.jar, they will each get different versions of the classes contained in c.jar. That's because the Manifest Class-Path entry will effectively just-in-time expand the classpath before any loading takes place.

                This is part of the jar specification.

                By extension, it is also required behavior in J2EE 1.4, which requires that implementors respect the jar specification.

                That means that if you have a war file with a manifest class-path entry, whatever classpath its loader has must be augmented right off the bat with whatever is in its manifest.

                So if a war file loader is, in any respect, ultimately a URLClassLoader with a URL of wherever-the-war-file-is and basically nothing else, then its classpath must be immediately augmented to contain whatever the Manifest says. So the loader in this stupid example would have a class path of {wherever-the-war-file-is, some-jar-the-war-file-references-via-its-manifest, some-other-jar-similarly-referenced}. This would happen and would take place before any other class loading occurs within that war.

                (Note again this is exactly what happens with URLClassLoaders right out of the box.)

                This means that because webapp loaders are bound by the servlet and J2EE specifications to "look inward first", anything roped in via this manifest mechanism will have the same visibility as anything contained in WEB-INF/classes or WEB-INF/lib. That's the intention, anyway.

                This does not appear to be what JBoss 4.0.3 is doing. Either the webapp manifest is ignored (this is not true; in my case I can see classes inside framework.jar), or the initiating loader of framework.jar is not correctly being set by the webapp loader to itself. What I mean by that is from the standpoint of any code inside the webapp, framework.jar in my case should appear (from a classloading perspective) as though it were dumped in WEB-INF/lib.

                Since this behavior is free out of the box with URLClassLoaders, I have to assume that the Tomcat classloader that loads webapps is doing something odd, or the JBoss subclass thereof is doing something odd.

                • 5. Re: J2EE classloading question (I've read the CL wiki topics
                  Laird Nelson Apprentice

                  ...and my .ear as built deploys just fine on Sun's reference application server, which handles manifest class-path entries properly.

                  OK, time to figure out how to beat JBoss with a lead pipe sufficiently hard to convince it to obey the specification....

                  • 6. Re: J2EE classloading question (I've read the CL wiki topics
                    Claus Dreyer Newbie

                    Did you ever find a solution to this problem?

                    In the wiki
                    http://wiki.jboss.org/wiki/Wiki.jsp?page=GetClassNotFoundExceptionOrNoClassDefFoundError
                    this issue is mentioned under the heading "The class is not visible from where it is being loaded".

                    I know it does not help much, but someone was/is aware of this.