9 Replies Latest reply on May 16, 2010 9:21 AM by alesj

    ClassLoading delegation

    rafaelliu

      It's actually not a MC thing, but it's on MC's expertise...

       

      I'm trying to do a classloader which will chain 2 other classloaders. I need this since I'm making a ContainerApp that will get another web application's context (another WAR), say BookingApp, to dispatch requests.

       

      So whenever ContainerApp has to load classes it will look first at BookingApp, then at ContainerApp classpash itself. My first naive approach was:

       

      public class DelegatorClassLoader extends ClassLoader {
      
              @Override
              public synchronized Class<?> loadClass(String name) throws ClassNotFoundException {
                      try { 
                              return bookingClassLoader.loadClass(name);
                      } catch (Exception e) {
                              return containerClassLoader.loadClass(name);
                      }
              }
      
      ...
      
      }
      

       

      Which works OK unless classes BookingApp reference ContainerApp, or vice-versa. That is, supposing the WARs had the following classes:

       

      ContainerApp.war:

      ContainerOnlyClass

       

      BookingApp .war:

      BookingOnlyClass

      BookingContainerClass extends  ContainerOnlyClass

       

      The results I get are:

       

      DelegatorClassLoader.loadClass(ContainerOnlyClass) works

      DelegatorClassLoader.loadClass(BookingOnlyClass) works

      DelegatorClassLoader.loadClass(BookingContainerClass) doesn't work

       

      The associated classloader is either ContainerApp or BookingApp, never DelegatorClassLoader. Reading the VM spec it's clear that the reference the class holds in to it's defining classloader, while in my code  DelegatorClassLoader is the initiator classloader.

       

      My question is: doesn't MC VFS has something like this already so I could reuse it? This seems precisely what JBoss does.. Could you point me to some source code?

       

      thanks a lot! cheers

        • 1. Re: ClassLoading delegation
          alesj

          Like you said, this is already exactly what JBossAS does.

          So, why would you need your own (bad) mechanism?

           

          The way JBossAS does it is by having the notion of domains, which are shared among multiple classloaders.

          e.g. classloader in a domain sees classes from other classloaders in that same (or parent) domain

           

          Pushing your work into JBossAS, this should work out-of-the-box.

          If you wanna create smaller environment than JBossAS, have a look at my MC demos which does exactly that:

          * http://anonsvn.jboss.org/repos/jbossas/projects/demos/microcontainer/trunk/ -- bootstrap module

          • 2. Re: ClassLoading delegation
            rafaelliu

            Yeah, I could make my container's parent domain point to the target application's domain. The problem is I can't change anything at target application bundle.

             

            Is there some documentation about these demos? I ran it and got:

             

             

            java.lang.NoClassDefFoundError: org/jboss/kernel/plugins/bootstrap/basic/BasicBootstrap
            at org.jboss.demos.bootstrap.Main.configure(Main.java:109)
            at org.jboss.demos.bootstrap.Main.main(Main.java:54)
            at org.jboss.demos.bootstrap.Main.main(Main.java:48)
            Caused by: java.lang.ClassNotFoundException: org.jboss.kernel.plugins.bootstrap.basic.BasicBootstrap
            at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
            at java.security.AccessController.doPrivileged(Native Method)
            at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:319)
            at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:264)
            at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:332)

             

             

            Looks like a Microcontainer class. Reading the docs, seems Microcontainer doesn't have a binary release yet..

             

            Thanks

            • 3. Re: ClassLoading delegation
              alesj
              Yeah, I could make my container's parent domain point to the target application's domain. The problem is I can't change anything at target application bundle.

              No, just the other way around.

              Your app's parent should be container's domain.

               

              Is there some documentation about these demos? I ran it and got:

              My first article on DZone has some very simple instructions on how to set it up:

              * http://java.dzone.com/articles/jboss-microcontainer-scanning (see Part 1)

               

              java.lang.NoClassDefFoundError: org/jboss/kernel/plugins/bootstrap/basic/BasicBootstrap
              at org.jboss.demos.bootstrap.Main.configure(Main.java:109)
              at org.jboss.demos.bootstrap.Main.main(Main.java:54)
              at org.jboss.demos.bootstrap.Main.main(Main.java:48)
              Caused by: java.lang.ClassNotFoundException: org.jboss.kernel.plugins.bootstrap.basic.BasicBootstrap
              at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
              at java.security.AccessController.doPrivileged(Native Method)
              at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
              at java.lang.ClassLoader.loadClass(ClassLoader.java:319)
              at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
              at java.lang.ClassLoader.loadClass(ClassLoader.java:264)
              at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:332)

               

              Looks like a Microcontainer class. Reading the docs, seems Microcontainer doesn't have a binary release yet..

              There should be no problems running this from IDE.

              All MC binaries are in our maven repository.

              • 4. Re: ClassLoading delegation
                rafaelliu

                Ales, thanks for the interest.

                 

                I managed to executed jmx + demo-models example. Doesn't seem like what I need, tho. In my case I already have both classloaders' references: the application's one and my container's one, I just want to chain them.

                 

                I did a drawing.. http://drop.io/ktdredz/asset/deletagorclassloader-png

                • 5. Re: ClassLoading delegation
                  alesj
                  I did a drawing.. http://drop.io/ktdredz/asset/deletagorclassloader-png

                  This looks easy to do.

                   

                  DefaultDomain (<JBoss_Home>/lib)

                                 ^

                                 |

                  ContainerDomain (Container.war)

                                 ^

                                 |

                  ApplicationDomain_1 (App1.war)

                   

                  And properly set parent/child delegation.

                  • 6. Re: ClassLoading delegation
                    rafaelliu

                    Precisely. That's what I want. But I can't change the App1 (neither App2 or App3).. That's why I was setting ContextClassLoader: I know they use it for class loading. I do something like this:

                     

                    Thread.currentThread().setContextClassLoader(delegatorClassloader);
                    RequestDispatcher dispatcher = appServletCtx.getRequestDispatcher(path);
                    dispatcher.forward(request, response);

                     

                    It's pretty much the approach you suggested, but done dynamically. I was thinking about writing my own classloader, any idea how it cann be done with MC? Certainly it would be much prettier..

                     

                    Thanks

                    • 7. Re: ClassLoading delegation
                      alesj

                      I know they use it for class loading.

                      Who's they?

                      Where and how exactly are you running this?

                      I'm trying to do a classloader which will chain 2 other classloaders. I need this since I'm making a ContainerApp that will get another web application's context (another WAR), say BookingApp, to dispatch requests.

                       

                      So whenever ContainerApp has to load classes it will look first at BookingApp, then at ContainerApp classpash itself.

                      Re-reading this, this actually doesn't look like the hierarchy you described.

                      It's more of a delegation model, then actual parent-child hierarchy.

                       

                      In MC's CL you could do this with simply configuring ContainerApp's jboss-classloading.xml to use BookingApp's module / packages.

                      Since in BaseClassLoaderDomain, we first try imports, before looking into local resources.

                       

                      But why exactly do you need to change classloader before dispatch?

                      I must admit I don't see a valid reason, but it's true it's been a while since I did web apps ...

                      • 8. Re: ClassLoading delegation
                        rafaelliu

                        Who's they?

                        Where and how exactly are you running this?

                        "they" are my applications. It's all in JBoss.

                         

                        Re-reading this, this actually doesn't look like the hierarchy you described.

                        It's more of a delegation model, then actual parent-child hierarchy.

                        Probably "delegation" is a batter word, because I already has the 2 classloaders references. I want to write a third classloader that would load classes from a classloader and "fail over" to the other classloader.

                         

                        But why exactly do you need to change classloader before dispatch?

                        I must admit I don't see a valid reason, but it's true it's been a while since I did web apps ...

                        Well, the whole history then.. I'm doing a portlet that is a dynamic Portlet Bridge. It can be configured to show any existing web app deployed along with it in the same JBoss. That's for the dispatch.

                         

                        For that I need to "inject" JBoss Portlet Bridge libs/classes/configs in the application's classpath, so everything loaded by application should look also in the container's classpath. That's for the delegation.

                         

                        The problem is as described in my first post, I can't do an actually delegation.. Today I got it working by moving some jars to $JBOSS_CONF/lib which obviously isn't a good solution.

                         

                        I've been talking to Wesley Hales from JBoss Portlet Bridge too, maybe he can give you some insight (I believe you don't mess with portlets a lot )

                        • 9. Re: ClassLoading delegation
                          alesj

                          But why exactly do you need to change classloader before dispatch?

                          I must admit I don't see a valid reason, but it's true it's been a while since I did web apps ...

                          Well, the whole history then.. I'm doing a portlet that is a dynamic Portlet Bridge. It can be configured to show any existing web app deployed along with it in the same JBoss. That's for the dispatch.

                           

                          For that I need to "inject" JBoss Portlet Bridge libs/classes/configs in the application's classpath, so everything loaded by application should look also in the container's classpath. That's for the delegation.

                           

                          The problem is as described in my first post, I can't do an actually delegation.. Today I got it working by moving some jars to $JBOSS_CONF/lib which obviously isn't a good solution.

                          I see.

                           

                          I guess you could somehow super hack it with your approach -- changing the TCCL,

                          but that's not the way it should be done. ;-)

                           

                          Why it doesn't work in JBossAS by default is b/c each .war is placed into its own domain, which then gets child-first lookup notion.

                          So, all you need to do in order to make this work is for all web apps that you want them to share resources to be in the same domain.

                          Which is why your jar move makes this work -- the common resources can be fund in common default domain.

                          I've been talking to Wesley Hales from JBoss Portlet Bridge too, maybe he can give you some insight (I believe you don't mess with portlets a lot )

                          Well, I'm sharing an office with Marko (JBoss Portal core dev), so why don't the three of you decide what exactly you need to achieve,

                          and I'll make sure Marko makes that a reality with MC's CL, instead of us two playing forums ping-pong.