9 Replies Latest reply on May 18, 2005 9:41 AM by tigerreef

    4.0.1 to 4.0.2 ClassCastException on PortableRemoteObject.na

    tigerreef

      Hello,

      Firstly I know there is a lot of discussion on this point, but I can't find anything that describes my problem exactly.

      To emphasize up front:

      1. My app is not distributed between two ears (I don't have a hot deploy of partial app issue)
      2. The EXACT same ear works in 4.0.1 but not in 4.0.2

      The problem:

      The code (which is in a Servlet):

      Context ctx = new InitialContext();
      Object o = ctx.lookup("ejb/TSmartAdmin");
      Class target = TSmartAdminHome.class;
      TSmartAdminHome home = (TSmartAdminHome)PortableRemoteObject.narrow(o, TSmartAdminHome.class);

      causes:

      java.lang.ClassCastException
      com.sun.corba.se.internal.javax.rmi.PortableRemoteObject.narrow(PortableRemoteObject.java:293)
      javax.rmi.PortableRemoteObject.narrow(PortableRemoteObject.java:134)
      com.tigerreef.templatesmart.servlet.NavServlet.lookupTSmartAdmin(NavServlet.java:397)


      but ONLY when deplyed to a JBOSS 4.0.2 server (works fine on 4.0.1).

      Additional the same code run in a testClient running within Eclipse runs fine.

      It seems to me that the problem is to do with Class loaders. So, added the follwoing code after I have the object ref "o":

      System.out.println("Servlet>>>>>> o's CL: " + o.getClass().getClassLoader() + " target's CL: " + target.getClassLoader());

      and on 4.0.2 I get:

      Servlet>>>>>> o's CL: org.jboss.mx.loading.UnifiedClassLoader3@773a1{ url=file:/C:/jboss-4.0.2/server/default/tmp/deploy/tmp27550TSmartApp.ear ,addedOrder=40} target's CL: WebappClassLoader
      delegate: false
      repositories:
      /WEB-INF/classes/
      ----------> Parent Classloader:
      java.net.FactoryURLClassLoader@1f5248c

      Two different class loaders, used for the EJB tier and the Web tier (both from the same ear running in the same JVM - my unserstanding is JBoss is supposed to optimise this.)

      I tried the same thing on 4.0.1:

      and get:

      Servlet>>>>>> o's CL: org.jboss.mx.loading.UnifiedClassLoader3@6dd8e1{ url=file:/C:/jboss-4.0.1/server/default/tmp/deploy/tmp7498TSmartApp.ear ,addedOrder=39} target's CL: org.jboss.mx.loading.UnifiedClassLoader3@6dd8e1{ url=file:/C:/jboss-4.0.1/server/default/tmp/deploy/tmp7498TSmartApp.ear ,addedOrder=39}

      NOTE UnifiedClassLoader in both cases....

      And now I am stumped. I see a lot of people having problems with comms between two different ears and hot deploys of one of them due to dangling references, but I don't think this is in that category. To reiterate - ONE ear - everything deployed fresh (at the same time), the same problem occurs if I start the server form the command line or from within Eclipse AND the EXACT same ear works fine in 4.0.1.

      Please help!
      PJ


        • 1. Re: 4.0.1 to 4.0.2 ClassCastException on PortableRemoteObjec
          bocio

          Sad to say the misused RTFM:


          To revert to pre-4.0.2 config use:
          <attribute name="Java2ClassLoadingCompliance">true</attribute>
          <attribute name="UseJBossWebLoader">true</attribute>


          • 2. Re: 4.0.1 to 4.0.2 ClassCastException on PortableRemoteObjec
            bocio

            Sorry was:

            To revert to pre-4.0.2 config use:
            <attribute name="Java2ClassLoadingCompliance">true</attribute>
            <attribute name="UseJBossWebLoader">true</attribute>
            


            • 3. Re: 4.0.1 to 4.0.2 ClassCastException on PortableRemoteObjec
              amit.bhayani

              The only difference between 4.0.1 and 4.0.2 is 4.0.2 JBoss has changed to the Servlet spec classloading model, i.e. it uses the Tomcat classloader.

              But I don't see why this should break the working application. How have you done the packaging of war and ear file?

              • 4. Re: 4.0.1 to 4.0.2 ClassCastException on PortableRemoteObjec
                gozilla

                Hi tigerref,

                I thing the problems comes from the fact that the classes in question exist in two places in your EAR: once in the EJB jar and once in the war (WEB-INF/classes).

                Servlet>>>>>> o's CL: org.jboss.mx.loading.UnifiedClassLoader3@773a1{ url=file:/C:/jboss-4.0.2/server/default/tmp/deploy/tmp27550TSmartApp.ear ,addedOrder=40} target's CL: WebappClassLoader
                delegate: false
                repositories:
                /WEB-INF/classes/


                In that case, a change in webapp class loading will have an impact.

                So, two options:
                - fight with the CL setting
                - or (easier) remove the duplicate classes from the WAR.

                Cheers

                • 5. Re: 4.0.1 to 4.0.2 ClassCastException on PortableRemoteObjec
                  tigerreef

                  Thanks for the various replies.

                  Bocio: Yes the manual says how to revert to a 4.0.1 config. I don't think it makes it very clear as to why. The default settings are false (manual says true), strange that it will break ears that have work through many versions.

                  gozilla: Thanks for your reply. My TSmartAdminHome class can be found in my EJB.jar AND in the war, but ONLY in my EJB_Client.jar within it. I don't see how this will work any other way with the deployment I want.

                  amit.bhayani@jboss.com: I agree I don't see how the changes between 4.0.1 and 4.0.2 should affect what I am doing. To answer your specific question. All my packaging is happening using JBoss IDE within Eclipse.

                  So....

                  I will change the ClassLoadingCompliance and JBossWebLoader setting, I imagine this will fix it as I will be back to the UnifiedClassLoader strategy which works in 4.0.1. It seems that the crux of this problem that if I want to run the EJB and WEB tier code in the same JVM that I MUST change these settings to true, else I cannot package the EJB_Client code in the same EAR as the EJB code. BTW: These values default to false, when I RTFM it says they are true...

                  • 6. Re: 4.0.1 to 4.0.2 ClassCastException on PortableRemoteObjec
                    gozilla

                    tigerreef, I don't get your point about

                    I don't see how this will work any other way with the deployment I want.

                    Using the UCL is the way to avoid an 'ejbclient.jar', because the classes are found in the jar already. Just wipe it out.

                    If you absolutely wants to have an ejbclient.jar (and also use one) then it means that the classes are ALWAYS loaded by two different class loaders (as you found), and then you CAN'T possibly use any PASS BY REFERENCE optimisation, and you loose nearly all the advantages of colocating the WAR and the JAR inside the same EAR (except the ease of deploy) because you have to use the 'PASS BY VALUE' semantics (means serialize/deserialize).

                    Cheers



                    • 7. Re: 4.0.1 to 4.0.2 ClassCastException on PortableRemoteObjec
                      tigerreef

                      Thanks gozilla, that makes a lot more sense now.

                      Am I right in saying that with the UCL (as i had in my 4.0.1 deployment), although I still had the class in the EJB-Client jar AND the ejb.jar then effectively the client version is being ignored and the UCL is just passing the reference of the one it loaded from ejb.jar.

                      The problem then arose for me in 4.0.2 because the webclassloader is getting involved but there is still some pass by reference going on? I was assuming that I would still work because the object would get serialized/deserialized as if the war and ejb.jar when on separate machine - i.e. no optimization.

                      Hope that makes sense,
                      PJ

                      • 8. Re: 4.0.1 to 4.0.2 ClassCastException on PortableRemoteObjec
                        gozilla

                        Yes, thats what happened.

                        AFAIK, you have to force the pass-by-value semantics somewhere, but I don't remember where.

                        As I already said, I wouldn't go that way, I would simply wipe the client jar.

                        Cheers

                        • 9. Re: 4.0.1 to 4.0.2 ClassCastException on PortableRemoteObjec
                          tigerreef

                          Hi,

                          Have tried the various suggestions posted and thought I would wrap up with the findings, in case anyone else is reading this thread with the same problem.

                          This problem does indeed manifest itself in moving from JBoss 4.0.1 and 4.0.2 because as amit.bhayani@jboss.com says

                          difference between 4.0.1 and 4.0.2 is 4.0.2 JBoss has changed to the Servlet spec classloading model, i.e. it uses the Tomcat classloader.


                          So, there were two options:

                          Option 1. Reconfigure the server to reverse this change (which gives you the same class loading behaviour as you would see in 4.0.1.)

                          This is achieved by changing the settings in "$jboss-4.0.2"\server\default\deploy\jbossweb-tomcat55.sar\META-INF\jboss-service.xml to:

                          <attribute name="Java2ClassLoadingCompliance">true</attribute>
                          <attribute name="UseJBossWebLoader">true</attribute>


                          Actually I found you only seem to need to change the latter of these.

                          What is confusing about this area is that if you read "What's new in JBoss 4.0". It explains that 4.0.1 has the same default config as 3.2. I.e. the class loading is using JBoss's effecient model and NOT a J2EE 1.4 compliant one. (Efficient, but not compartmentalized). It tells you that if you want to go J2EE compliant you should change the above settings to false (the default in 4.0.2) and make a change in "$jboss-4.0.2"\server\default\conf\jboss-servie.xml :

                          <mbean code="org.jboss.naming.NamingService"
                          name="jboss:service=Naming">
                          <!-- The call by value mode. true if all lookups are unmarshalled using
                          the caller's TCL, false if in VM lookups return the value by reference.
                          -->
                          <attribute name="CallByValue">true</attribute>
                          ...
                          </mbean>


                          However the default config in 4.0.2 IS supposed to be J2EE compliant, but if you check the CallByValue setting above it is still false. So I think you end up with "compartmentalized" class loader namespace but objects are STILL being passed by reference instead of being mashalled across the layers. Hence the ClassCast problems after the JNDI lookup.


                          Anyway on to option 2...


                          Option 2. Leave the settings as they are delivered for JBoss 4.0.2 and make sure that any classes are not duplicated anywhere in an ear. I.e. remove your EJB-Client.jar from your .war and any other utility classes used in both layers (they are provided only in ejb.jar).

                          This is probably the simplest option (as gozilla points out), but does mean that if you do suddenly want to move your web and ejb tiers onto different virtual/physical machines, you will need to do some repackaging to put the stuff back in.


                          Interesting further discovery.

                          Regardless of which option I tried I came up with a different problem, and I can't find it documented in any of the "what's new" docs.

                          As soon as I started getting beyond the ClassCastExceptions I found that I would get "java.lang.IllegalArgumentException: setAttribute: Non-serializable attribute" whenever trying to put a non-serializable object into the session. I am not clustered so I couldn't see why the object would have to be serializable (activation/passivation issues aside). After some investigation I could track it down to one thing. My web.xml had the element in it. Take it out and no llegalArgumentExceptions, put it back and you get them again.

                          It seem that a new feature of 4.0.2 checks if you have shown the "intent" of having your app distributable and throws an exception if you try to set an attribute that would not allow a session to be distributed between servers. As I said, it may be there but I can't find it in any of the change logs. BTW: If you are using JBoss-IDE for eclipse and you don't specifically say you don't want your app distributable (via your xdoclet config) you will get the tag in your web.xml.


                          Anyway, I am happy now - thanks to everyone who helped me resolve this.

                          Cheers,
                          PJ