8 Replies Latest reply on Oct 19, 2002 11:25 AM by Aaron Roller

    More Struts Classloader Issues

    Aaron Roller Newbie

      I have been reading loads of classloader issues associated with deploying Struts in wars and ears. Here are some additional observations/problems that I would like to share...

      I deploy five stand-alone webapps in their own .war archives each including struts.jar and dimensions.jar (extension to Struts). A class from dimensions.jar is loaded upon webapp/Struts initialization. All of these work happily.

      I now deploy an enterprise archive (.ear) that contains one ejb and dependent library files (including dimensions.jar). Accessing the previously mentioned webapps now fail with...you guessed it...NoClassDefFoundError referencing Struts classes. I remove the ear and restart and the webapps are happy again.

      Theoretically webapps and enterprise apps are supposed to be independent of each other's classpath, but this is clearly not the case.

      I will add more as I discover a solution/workaround.

        • 1. Re: More Struts Classloader Issues
          Aaron Roller Newbie

          This behavior is consistent across JBoss 3.0.3 and JBoss 3.0.2/Tomcat 4.0.4

          • 2. Re: More Struts Classloader Issues
            Aaron Roller Newbie

            It appears to happen regardless if the webapp is deployed in a .war archive or an expanded .war directory.

            • 3. Re: More Struts Classloader Issues
              Aaron Roller Newbie

              I expanded the .ear archive to a .ear directory. I then expanded the .war archive into the .ear directory to make a .war directory within the .ear directory.

              I removed all libraries from the webapp WEB-INF/lib directory and placed them in the root of the .ear directory.

              Now, in theory, the Struts classes should only be available if I make a reference from the xxx.war/META-INF/MANIFEST.MF Class-Path: entry to struts.jar. Ironically I do not need to make this reference for my web application to load properly..because it does load properly without any reference.

              The webapp does not work properly though. The JSP tags in the pages cannot find their classes. I put a reference in the xxx.war/META-INF/MANIFEST.MF Class-Path: to the tag library archives and still no success. I believe JBoss is ignoring the Manifest file for the web application expanded into an expanded .ear directory.

              Summary: JBoss appears to be finding classes from anywhere during the loading and initialization of a Struts web application, but once loaded JBoss will only look in the web application's classpath for necessary files. My conclusion comes from investigation prior to this ongoing experiment.

              • 4. Re: More Struts Classloader Issues
                Aaron Roller Newbie

                Removing the struts.jar from the .ear directory, cleaning up the temp directory and restarting JBoss yields a NoClassDefFoundError of a Struts class when trying to load the enterprise application (actually the web application inside the ear).

                So I then place struts.jar in the deploy directory of JBoss (not in the ear where it use to reside). I restart and now the enterprise app and web app load without exception. I now try to access the web application through a browser and it reports ClassNotFoundError of org/apache/commons/beanutils/Converter class that struts.jar is dependent (referenced by the struts.jar Class-Path entry).

                Correct Behavior: Struts classes cannot find beanutils because the commons-beanutils.jar is not in the scope of struts.jar.

                Incorrect Behavior: Struts classes should never have been found in the first place. If there is no reference in the Manifest.mf Class-Path of the .ear to an external archive then those classes should not be available.

                I could be wrong, but I thought the deploy directory was for individual archives that may exist in their own "bubble". If want to make struts available to all archives then I would place struts.jar in the lib directory of the JBoss Server directory which I am running.

                • 5. Re: More Struts Classloader Issues
                  Aaron Roller Newbie

                  The webapplication and ejb work properly when I have an ear expanded directory containing the ejb and a bunch of libraries. Also when the web application is expanded into the ear and the WEB-INF/lib directory contains all libraries (struts.jar, dimensions.jar, commons-*.jar,etc). It does not work when the lib directory is empty and the Manifest references the libraries in the root of the .ear directory.

                  So now if I take the xxx.war directory and move it to a standalone, remove the reference to this webapp in the application.xml and restart. The ejb deploys fine, but loading the web application results in the following exception:


                  11:16:44,874 WARN [jbossweb] WARNING: Exception for /map/
                  java.lang.LinkageError: Class org/apache/struts/action/ActionServlet violates loader constraints
                  at java.lang.ClassLoader.defineClass0(Native Method)
                  at java.lang.ClassLoader.defineClass(ClassLoader.java:486)
                  at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:111)
                  at java.net.URLClassLoader.defineClass(URLClassLoader.java:248)
                  at java.net.URLClassLoader.access$100(URLClassLoader.java:56)
                  at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
                  at java.security.AccessController.doPrivileged(Native Method)
                  at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
                  at org.jboss.mx.loading.UnifiedClassLoader.findClass(UnifiedClassLoader.java:226)
                  at java.lang.ClassLoader.loadClass(ClassLoader.java:297)...

                  This occurs during initialization of the web application instigated by accessing the context path on the browser.

                  The reason for this problem is because the ear containing only the ejb also contains struts.jar. The Web application (now removed from the ear) is using the struts.jar contained in the ear, not from it's own WEB-INF/lib directory!

                  We can confirm this by removing the struts.jar from the .ear directory and now the error is:

                  11:22:53,124 WARN [jbossweb] WARNING: Exception for /map/
                  java.lang.NoClassDefFoundError: org/apache/struts/tiles/xmlDefinition/FactorySet

                  at java.lang.ClassLoader.defineClass0(Native Method)
                  at java.lang.ClassLoader.defineClass(ClassLoader.java:486)
                  at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:11...

                  This is because a class from dimensions.jar is being loading during web application initialization from dimensions.jar that is both in the web app and the ear (the ActionServlet is now only found in the webapp so it is not the problem anymore). Removing dimensions.jar (and any other struts-dependent libraries) from the ear yields a successful load of the web application. The problem is both the ejb in the ear and the webapp are dependent on classes contained in dimensions.jar. So now the ejb doesn't work without dimensions.jar.

                  I am actually happy to find that the ear does not look inside the stand-alone webapp for it's classes. At this point I expect JBoss to find jar files sitting on another computer on the same network:)


                  • 6. Re: More Struts Classloader Issues
                    Aaron Roller Newbie

                    For those of you who have made it this far and would really like to see an end to this saga...trust me so would I!!!

                    *********************************************
                    SOLUTION
                    *********************************************

                    It appears the only way I can protect a stand-alone web application from sharing libraries with another ear file while loading a web application is by putting that web application into it's own ear (not really a stand-alone anymore). So now I have one ear containing an ejb referencing dimensions.jar and another ear containing a web application that also contains dimensions.jar in it's library and both work properly.

                    This effort is strictly for development purposes. I want to deploy the web application individually rather than in an ear file so I don't have to re-deploy all the web applications every time I change code to one web application. Expanded directories are nice for JSP compiling.

                    For production it is clear that deploying all web applications and enterprise beans in a single ear provides the most predictable and efficient behavior. So my ant build file just becomes much more complex!

                    Lessons Learned:

                    - jar libraries in the deploy directory are made available to ears and wars inside ears without Manifest Class-Path references.

                    -struts is a problem because it is loaded upon intialization of a web application, probably not for a struts-specific reason.

                    - classes referenced during loading of a web application will be taken from a jar file external to the web application if available, otherwise it will look in the web application last library last.

                    - classes used within a web application after loading will be kept to local scope as defined by J2EE specs.

                    - classloader issues seem to be consistent on both JBoss/Jetty and JBoss/Tomcat.

                    - never deploy stand-alone webapps built on Struts if an ear is also deployed. deploy ears containing one or two webapps for development and then combine all webapps and ejbs into a single ear for production.

                    -an exapanded web application in an expanded enterprise application does not use the MANIFEST.MF Class-Path: to reference common libraries in the ear directory. It does work in an ear archive. A work-around is to place all common libraries in the web app's lib.

                    Any suggestions?

                    • 7. Re: More Struts Classloader Issues
                      Robert Augustyn Newbie

                      From what I understand, you want to have one ear file for eficiency reasons. When moving strats.jar to the deploy directory rename it to 1strats.jar and it will get loaded first and then your other jars/wars will load ok.

                      • 8. Re: More Struts Classloader Issues
                        Aaron Roller Newbie

                        I am only using an ear because I need to deploy an ejb and I find it cleaner to package dependent jars of an ejb with an ear rather than deploying the jar files separately.

                        Once I deployed this ear containing only an ejb my .war archives stopped working. They stopped working because instead of finding struts.jar within their own WEB-INF/lib directory, JBoss started finding struts.jar in the newly deployed .ear archive. I consider this a JBoss bug.

                        So I am using ears not for efficiency reasons, but because I was forced to use an ear to package my wars to keep them from using struts.jar from another ear (during the webapp loading).