7 Replies Latest reply on May 10, 2005 8:22 PM by adrian.brock

    Setting the thread context class loader on mbean op dispatch

    starksm64

      The mbean invokers set the current thread context class loader to that of the mbean on op dispatches. This is what you want in general, but if the mbean represents a shared service like a cache that interacts with other isolated deployments, the thread context class loader needs to be that of the caller.

      We need a notion of a chained thread context class loader which would combine the incoming thread context class loader with that of the mbean service as its parent to allow for visibility into both layers.

        • 1. Re: Setting the thread context class loader on mbean op disp

          I think chaining is likely to lead all sorts of linkage errors or Class Casts.

          If you don't want an MBean to run under its own classloader you can do
          this, e.g. just doing a plain registerMBean without specifying a classloader
          will do this. Or could not use a JMX invocation and allow a getInstance()
          method to bypass the JMX Bus.

          A more general solution would be to add metadata on which operations
          should be run in the caller classloader versus the deployment classloader
          like lifecycle operations.

          • 2. Re: Setting the thread context class loader on mbean op disp
            starksm64

            Yes, there is a red flag issue once you have a service trying to interact with multiple class loaders, but it should be possible if designed for this. The only classes that are subject to linkage errors and class cast expections are those that show up in the service's interface (ignoring any bugs due to caching of types by the class loader or other layers). This of course means a detyped interface in general. An interesting question is, if such an interface was modifed to use generics, is there any type leakage. As far as I understand the generics type nullifcation mechanism, the type binding would be in the client usage context and therefore its class loader context, but this is something I would like to verify, and verify that it works with type reloading in the client class loader.

            • 3. Re: Setting the thread context class loader on mbean op disp

              I have created a Jira for future reference:
              http://jira.jboss.com/jira/browse/JBAS-1803

              Meanwhile, I will try to get around that problem by using a MarshalledValue wrapper. It will have performance impact but it should work for now.

              • 4. Re: Setting the thread context class loader on mbean op disp

                I'm obviously looking at the wrong code since it is already setting
                the correct TCL:

                JBossCacheService

                 private Object getUnMarshalledValue(Object mv)
                 {
                 if (mv == null) return null;
                 // Swap in/out the tcl for this web app. Needed only for un marshalling.
                 ClassLoader prevTCL = Thread.currentThread().getContextClassLoader();
                 Thread.currentThread().setContextClassLoader(tcl_);
                 try
                 {
                 return ((MarshalledValue) mv).get();
                 }
                 catch (IOException e)
                 {
                 e.printStackTrace();
                 return null;
                 }
                 catch (ClassNotFoundException e)
                 {
                 e.printStackTrace();
                 return null;
                 }
                 finally
                 {
                 Thread.currentThread().setContextClassLoader(prevTCL);
                 }
                 }
                


                where tcl_ is coming from the Tomcat config.

                • 5. Re: Setting the thread context class loader on mbean op disp

                  Please fix the e.printStackTrace()
                  while you are in this code.

                  And the double logging abomination.

                   String str = cacheServiceName_ + " service to Tomcat clustering not found";
                   log_.error(str);
                   throw new ClusteringNotSupportedException(str);
                  


                  trace and throw - yes
                  error and throw just makes your eyes bleed as the console flashes past
                  with the all the duplicate logging. :-)

                  • 6. Re: Setting the thread context class loader on mbean op disp

                    Yeah, you were looking at the wrong code. That marshalling/unmarshalling code works.

                    This is the code that failed:

                     public Object getPojo(String sessionId, String key)
                     {
                     String realId = stripJvmRoute(sessionId);
                     if(log_.isDebugEnabled())
                     {
                     log_.debug("getPojo(): session id: " +sessionId + " key: " +key);
                     }
                     // Construct the fqn.
                     Fqn fqn = getFieldFqn(realId, key);
                     try {
                     return proxy_.getObject(fqn);
                     } catch (CacheException e) {
                     e.printStackTrace();
                     throw new RuntimeException("JBossCacheService: exception occurred in cache getPojo ... ", e);
                     }
                     }
                    


                    And this code will call JBossCacheAop (you will have to check out JBossCache module though. :-)

                    But you maybe right. Solution is then I will need to set tcl before calling getPojo regardless what.

                    Let me try.


                    • 7. Re: Setting the thread context class loader on mbean op disp

                       

                      "ben.wang@jboss.com" wrote:

                      But you maybe right. Solution is then I will need to set tcl before calling getPojo regardless what.

                      Let me try.


                      No, because invoking the proxy will change the classloader.

                      Your fundamental problem (as I understand your description in JBCACHE-151)
                      is that the code that does the serialization/deserialization
                      doesn't understand classloader context. That is why you need the MarshalledValue.

                      Class.forName(String) is going to use the classloader that loads your jboss cache class
                      regardless of what JMX does.