8 Replies Latest reply on Jul 30, 2003 2:17 AM by slaboure

    callAsynchMethodOnCluster ClassLoader Problem

    darranl

      Hi,

      I am having a ClassLoader problem that occurs when I use callAsynchMethodOnCluster.

      My application consists of a session bean and data transfer object packed in a jar and a service packed in a sar, both of these are then packed in an ear for deployment.

      The session bean passes in an instance of 'DataObject' to sendDataObject on my MBean, the two System.out.println calls show that the 'DataObject' is in the same class loader that was used to deploy my application.

      setTestData is then automatically called and again both System.out.println calls show that the DataObject is in the same class loader that was used to deploy my application.

      I then remove my ear from deploy and after it has been undeployed I deploy it again (The same ear, no recompilation).

      When I run my test again the session bean calls sendDataObject, the System.out.println calls show that a new class loader is being used (Correctly).

      setTestData as before is called correctly however the first System.out.println call shows that the data object was loaded by the class loader from the previous deployment. The second System.out.println call shows that creating a new DataObject would use the new class loader. As a result I can no longer cast obj to DataObject.

      I have been using a profiling tool to find out what is happening and after the first class loader is eventually garbage collected I get the following error displayed on the console.

      13:44:57,617 WARN [DefaultPartition] RpcProtocol.Handle(): java.lang.NullPointerException

      And setTestData is no longer called.

      If setTestData is declared to actually take the type DataObject there are no problems. I can not declare the method to take the specific type as I want to pass round a Hashtable of DataObjects not just a single instance and the Hashtable references them as Object.

      Does anyone have any idea as to what I need to do to get the correct class loader to be used when a method on a service is being called across the cluster?


      I have pasted in the implementation of the MBean below as I can not get 'Attach Files' to work.

      package com.darranl.service;

      import javax.management.MBeanServer;
      import javax.management.ObjectName;
      import javax.naming.Context;
      import javax.naming.InitialContext;
      import javax.naming.Name;
      import javax.naming.NamingException;

      import org.jboss.ha.framework.interfaces.HAPartition;
      import org.jboss.naming.NonSerializableFactory;
      import org.jboss.system.ServiceMBeanSupport;

      import com.darranl.dto.DataObject;

      /**
      *
      * @author Darran L
      * @version 1.1.001
      */
      public class ClassLoaderTest
      extends ServiceMBeanSupport
      implements ClassLoaderTestMBean {

      private MBeanServer mBeanServer;

      protected ObjectName clusterPartitionName = null;

      private HAPartition partition = null;

      private static final String SERVICE_NAME = "ClusteredMBean";

      private final long createTime = System.currentTimeMillis();

      /**
      * @see com.darranl.service.ClassLoaderTestMBean#setTestData(com.darranl.dto.DataObject)
      */
      public void setTestData(Object obj) {
      System.out.println(
      "setTestData - "
      + obj.getClass().getClassLoader().toString()
      + " - "
      + createTime);
      System.out.println(
      "setTestData - "
      + new DataObject().getClass().getClassLoader().toString()
      + " - "
      + createTime);

      if (obj instanceof DataObject) {
      System.out.println("obj is instanceof DataObject");
      } else {
      System.out.println("obj is NOT instanceof DataObject");
      }

      try {
      Object other = (DataObject) obj;
      System.out.println("No problems casting");
      } catch (ClassCastException cce) {
      System.out.println("ClassCastException");
      }
      }

      public void sendDataObject(Object obj) {
      System.out.println(
      "sendDataObject - "
      + obj.getClass().getClassLoader().toString()
      + " - "
      + createTime);
      System.out.println(
      "sendDataObject - "
      + new DataObject().getClass().getClassLoader().toString()
      + " - "
      + createTime);
      try {
      partition.callAsynchMethodOnCluster(
      SERVICE_NAME,
      "setTestData",
      new Object[] { obj },
      false);
      } catch (Exception e) {
      System.err.println("sendDataObject error - " + e.getMessage());
      }
      }

      /**
      * @see javax.management.MBeanRegistration#preRegister(javax.management.MBeanServer, javax.management.ObjectName)
      */
      public ObjectName preRegister(MBeanServer mBeanServer, ObjectName objectName)
      throws Exception {
      this.mBeanServer = mBeanServer;
      return super.preRegister(mBeanServer, objectName);
      }

      private void rebind() throws NamingException {
      Context ctx = new InitialContext();
      Name fullName = ctx.getNameParser("").parse("ClassLoaderTest");
      NonSerializableFactory.rebind(fullName, this, true);
      }

      private void unbind() throws NamingException {
      System.out.println("++unbind - " + "ClassLoaderTest");
      InitialContext ctx = new InitialContext();
      Name fullName = ctx.getNameParser("").parse("ClassLoaderTest");
      NonSerializableFactory.unbind(fullName);
      System.out.println("unbind - finished");
      }

      /**
      * @see org.jboss.system.ServiceMBeanSupport#startService()
      */
      protected void startService() throws Exception {
      rebind();
      clusterPartitionName =
      new ObjectName("jboss:service=" + "DefaultPartition");

      partition =
      (HAPartition) mBeanServer.getAttribute(
      clusterPartitionName,
      "HAPartition");

      partition.registerRPCHandler(SERVICE_NAME, this);
      }

      /**
      * @see org.jboss.system.ServiceMBeanSupport#stopService()
      */
      protected void stopService() throws Exception {
      partition.unregisterRPCHandler(SERVICE_NAME, this);
      unbind();
      }

      /**
      * @see com.darranl.service.ClassLoaderTestMBean#getCreateTime()
      */
      public long getCreateTime() {
      return createTime;
      }

      }

        • 1. Re: callAsynchMethodOnCluster ClassLoader Problem
          darranl

          Sorry forgot to say this is on JBoss 3.2.1

          • 2. Re: callAsynchMethodOnCluster ClassLoader Problem
            darranl

            Hi,

            I thought I would start to take a look at the JBoss source to see if I can get an understanding of what is happening.

            The only place that the NullPointerException could be logged is in org.jboss.ha.framework.server.HAPartitionImpl in the handle method :-

            try
            {
            body=org.javagroups.util.Util.objectFromByteBuffer(req.getBuffer());
            }
            catch(Exception e)
            {
            log.warn("RpcProtocol.Handle(): " + e);
            return null;
            }

            I have edited the code to also print a stack trace when the exception occurs.

            I then ran through my tests again :-

            Deploy my app.
            Run the client.
            Undeploy my app.
            Deploy my app.
            Run the client (See the class cast problems)
            Wait for the class loader to be garbage collected.
            Run the client.

            Get the NullPointerException and the following stack trace :-

            java.lang.NullPointerException
            at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:322)
            at java.lang.Class.getDeclaredFields0(Native Method)
            at java.lang.Class.privateGetDeclaredFields(Class.java:1480)
            at java.lang.Class.getDeclaredFields(Class.java:1061)
            at java.io.ObjectStreamClass.getDefaultSerialFields(ObjectStreamClass.java:1384)
            at java.io.ObjectStreamClass.getSerialFields(ObjectStreamClass.java:1320)
            at java.io.ObjectStreamClass.access$600(ObjectStreamClass.java:45)
            at java.io.ObjectStreamClass$3.run(ObjectStreamClass.java:332)
            at java.security.AccessController.doPrivileged(Native Method)
            at java.io.ObjectStreamClass.(ObjectStreamClass.java:329)
            at java.io.ObjectStreamClass.lookup(ObjectStreamClass.java:249)
            at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:444)
            at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1511)
            at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1425)
            at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1616)
            at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1264)
            at java.io.ObjectInputStream.readArray(ObjectInputStream.java:1593)
            at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1261)
            at java.io.ObjectInputStream.readObject(ObjectInputStream.java:322)
            at org.javagroups.blocks.MethodCall.readExternal(MethodCall.java:492)
            at java.io.ObjectInputStream.readExternalData(ObjectInputStream.java:1676)
            at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1634)
            at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1264)
            at java.io.ObjectInputStream.readObject(ObjectInputStream.java:322)
            at org.javagroups.util.Util.objectFromByteBuffer(Util.java:39)
            at org.jboss.ha.framework.server.HAPartitionImpl.handle(HAPartitionImpl.java:630)
            at org.javagroups.blocks.RequestCorrelator.handleRequest(RequestCorrelator.java:532)
            at org.javagroups.blocks.RequestCorrelator.access$100(RequestCorrelator.java:37)
            at org.javagroups.blocks.RequestCorrelator$Request.run(RequestCorrelator.java:748)
            at org.javagroups.util.ReusableThread.run(ReusableThread.java:208)
            at java.lang.Thread.run(Thread.java:536)


            A short while after this the JVM also crashed but I am not sure if that was a result of using the profiling tool.

            • 3. Re: callAsynchMethodOnCluster ClassLoader Problem
              darranl

              I have tested the JVM crashing issue without using the profiling tool.

              I start JBoss
              Deploy my app
              Run the client
              Undeploy my app
              Wait

              The JVM then crashes.

              • 4. Re: callAsynchMethodOnCluster ClassLoader Problem
                darranl

                Originally I was testing using

                JDK 1.4.0 On Windows 2000

                I then upgraded the JDK to 1.4.1_03 and the symptoms of the problems have now changed :-

                Now running through the tests (Deploy, Run Client, Undeploy, Deploy, Run Client).

                The final 'Run Client' still has problems identifying the correct object type immediately after the deployment, however after enough time for the garbage collector to remove the old classloader everything starts working properly again without any null pointer exceptions.

                I also tried JDK 1.4.Beta2 and get the same result.

                How is the correct class loader selected when de-serializing an object?

                • 5. Re: callAsynchMethodOnCluster ClassLoader Problem
                  darranl

                  Just wanted to check to see if anyone else has been able to have a look at this yet or should I raise a bug on sourceforge?

                  • 6. Re: callAsynchMethodOnCluster ClassLoader Problem
                    slaboure

                    - Can you please show the code of your data object
                    - make sure it is (your DO and linked DO) contained in *only one* of your xAR files
                    - do you have other things deployed for your test or simply a single EAR?

                    sacha

                    • 7. Re: callAsynchMethodOnCluster ClassLoader Problem
                      darranl

                      Here is the source of the data object :-

                      import java.io.Serializable;

                      public class DataObject implements Serializable {

                      private String data1 = "";

                      private String data2 = "";

                      public String getData1() {
                      return data1;
                      }

                      public String getData2() {
                      return data2;
                      }

                      public void setData1(String data1) {
                      this.data1 = data1;
                      }

                      public void setData2(String data2) {
                      this.data2 = data2;
                      }

                      public String toString() {
                      return "data1 = " + data1 + " - data2 = " + data2;
                      }
                      }


                      The only items deployed in the ear are the sar containing the MBean and the jar containing a session bean that I use to invoke a method on the service, the DTO is only contained in the jar and not in the sar.

                      • 8. Re: callAsynchMethodOnCluster ClassLoader Problem
                        slaboure

                        If you have a reproducible testcase, I would love to see it or put it on sf.net/projects/jboss as a bug report.

                        cheers,

                        sacha