12 Replies Latest reply on Feb 9, 2004 4:42 PM by marc.fleury

    ClassCastException on narrow

    etrxndy

       

      "etrxndy" wrote:
      We have a project that we deploy to Jboss 3.2.1/Tomcat 4.1.24. We will call it proj1.ear. We have another project (proj2.ear) that has a stateless session bean (SessBeanProj2). SessBeanProj2 would really like to reuse a stateless session bean in proj1 (UsefulSessBeanProj1 – actually sits in proj1EJB.jar which is eared up in proj1.ear). Here’s a code snippet:

      InitialContext ctx = new InitialContext();
      Object fromJNDI = ctx.lookup("ejb/UsefulSBProj1");
      Object usefulSbHome = PortableRemoteObject.narrow(fromJNDI, IUsefulSBProj1Home.class);

      This throws a ClassCastException. The stacktrace is below.
      I have been researching this for a bit, so I have heard some things about Classloaders and some about the Classpath, but I need some more specifics. If it is the classpath issue, what do I need on the classpath? Do I need the ear on the classpath? Or just proj1EJB.jar? I am putting these things on the Class-Path line in my manifest file – is this the correct way?

      As you can see, I’m grasping. Any help would be much appreciated. Thanks!

      Here is part of the stacktrace. I will give you the whole thing if you think it will be useful, but I didn’t think it would be:
      java.lang.ClassCastException
      at com.sun.corba.se.internal.javax.rmi.PortableRemoteObject.narrow(PortableRemoteObject.java:293)
      at javax.rmi.PortableRemoteObject.narrow(PortableRemoteObject.java:134)
      at com.ndwc.proj2.ejb.bo.SessBeanProj2.daMethod(SessBeanProj2.java:92)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
      at java.lang.reflect.Method.invoke(Method.java:324)
      at org.jboss.ejb.StatelessSessionContainer$ContainerInterceptor.invoke(StatelessSessionContainer.java:629)
      at org.jboss.resource.connectionmanager.CachedConnectionInterceptor.invoke(CachedConnectionInterceptor.java:186)



        • 1. Re: ClassCastException on narrow
          darranl

           

          "darranl" wrote:
          Is 'ejb/UsefulSBProj1' the remote home interface?


          • 2. Re: ClassCastException on narrow
            etrxndy

             

            "etrxndy" wrote:
            Yes, 'ejb/UsefulSBProj1' is the remote home interface.


            • 3. Re: ClassCastException on narrow
              vertigo

              I can speak with some authority here, because I have been facing the very same problem in many app servers (WLS, WAS, etc.).

              1. First you must understand that there is a hierarchy of classloaders in any J2EE server, like:

              a) System classloader (the CLASSPATH stated at the "-cp" JVM argument);
              b) The extensions classloader, that loads all classes in all JARs in some special folders (like "JBOSS/server/default/lib");
              c) The classloader of the EAR application;
              d) The classloader of each (not exactly each, but wait) module in the EAR.

              When you look for a class each classloader first ask for its "parent" to find it (recursively), and if the parent can't find it the classloader tries to find it himself.

              This way if a class is available, say, at the server classpath it will be loaded only once, and will be available to all applications.

              And this way it is possible to redeploy an application without restarting the server (since the applications classloaders are isolated).

              OK until here? Let us proceed.

              Now, after EJB2.0 the common rule is this:

              Inside an EAR there is a single classloader that loads all EJB-jars. Each WAR has its own classloader, all of them "child" of the EJBs classloader. That is why a WAR can see all the EJB interfaces in the same EAR without anything in its manifest file. And, most important thing: there is a single classloader that loads the EJBs interfaces - and that is why typecastings after lookups work fine.

              Why is that important? It doesn't matter if a class is indeed the same: when loaded by different classloaders, two classes are considered different. This is your problem.

              When in different EARs, if you include an effective manifest Class-Path entry you will manage to load the EJB interfaces in your client application (I assume you did, or you would get a different exception), but...

              ...but the typecasting may not work: you will get a ClassCastException when typecasting the lookup result because the interface classes are different (same class, but different classloaders). Got it?

              This error makes sense with local interfaces, where you expect a pass-by-reference as a lookup result. With remote interfaces the narrowing should avoid it, but....

              ...but JBoss optimizes operations in the same JVM, so that any "remote" EJB will behave like a "local" EJB - the narrowing then is ignored, and you are typecasting as in a local lookup - BANG - ClassCastException, here it comes.

              A possible workaround is to create an "ejb-client.jar" containing all the EJB interfaces (and dependencies like VOs) that must be available to other EARs. This jar can be copied into an extension folder (like JBOSS/server/default/lib): this way there will be a single higher classloader loading these classes (they have precedence over the same classes inside any module).

              Please, if you copied the interfaces into WEB-INF/lib or WEB-INF/classes of any WAR remove them. There is a possible configuration that subverts the natural classloading behaviour (parent-first) inside WARs.

              That is all. Hope it helps ya.

              • 4. Re: ClassCastException on narrow
                etrxndy

                Andre,
                That was an excellent, well thought out way of explaining all this. Thank you for taking the time to explain it so I could understand.
                My problem still persists, though. I am going to show you a few of the things from the actual code and give you a bit more info based on my new understanding. Here is the actual code snippet:

                Object ohBoy = ctx.lookup("ejb/PremiumCalculator");

                System.out.println(ohBoy.getClass().getName());
                System.out.println(ohBoy.toString());
                System.out.println("1=" + ohBoy.getClass().getClassLoader());
                System.out.println("2=" + IPremiumCalculatorHome.class.getClassLoader());
                if (ohBoy instanceof IPremiumCalculatorHome){
                System.out.println("It is the premiumcalculator home interface!!");
                }
                else{
                System.out.println("son of a.... It isn't the right premiumcalculator home interface.");
                }

                IPremiumCalculatorHome ohBoyII = (IPremiumCalculatorHome)PortableRemoteObject.narrow(ohBoy, IPremiumCalculatorHome.class);

                Here is the output from that code:
                [STDOUT] $Proxy112
                [STDOUT] ejb/PremiumCalculatorHome
                [STDOUT] 1=org.jboss.mx.loading.UnifiedClassLoader3@15118a7{ url=file:/C:/jboss-3.2.1_tomcat-4.1.24/server/default/tmp/deploy/server/default/deploy/oai.ear/50.oai.ear ,addedOrder=34}
                [STDOUT] 2=org.jboss.mx.loading.UnifiedClassLoader3@988dd6{ url=file:/C:/jboss-3.2.1_tomcat-4.1.24/server/default/tmp/deploy/server/default/deploy/oaiExt.ear/51.oaiExt.ear ,addedOrder=35}
                [STDOUT] son of a.... It isn't the right premiumcalculator home interface.
                [STDERR] java.lang.ClassCastException

                As you can see, I seem to be loading IPremiumCalculatorHome from the oaiExt.ear classloader. And Proxy112 is probably IPremiumCalculatorHome loaded from the oai.ear classloader. Before I read your post IPremiumCalculatorHome only existed in two places - once in oaiejb_client.jar (part of oaiExt.ear) and once in oaiejb.jar (part of oai.ear). After I read your email I stuck oaiejb_client.jar into the JBOSS/server/default/lib folder, but the results were the same as before.
                Any ideas? I have one specific question. When you explained the system classloader you said "(the CLASSPATH stated at the "-cp" JVM argument)". I'm not sure where to find that (forgive me). I know there is a classpath statement in my run.bat which references a couple of jars. I don't start my JVM from a command line or anything, so I am a bit confused on this one.
                Thanks again,
                Alan

                • 5. Re: ClassCastException on narrow
                  vertigo

                   

                  "vertigo" wrote:

                  Hmmm.. Did you restart JBoss after copying a new JAR into the "server/deafult/lib" dir?

                  Weird. Try this HOT stuff here:

                  ftp://edownload:BUY_ME@ftpna2.bea.com/pub/downloads/Musser.zipBUY_ME@ftpna2.bea.com/pub/downloads/Musser.zip

                  This is a BEA JSP page that shows wich classloader loaded a certain class. Copy this page into you WAR. Type in the text box the name of any instantiable class (like a VO) to see the path of the JAR that loaded this class. This JSP works with JBoss too.



                  • 6. Re: ClassCastException on narrow
                    etrxndy

                    Yes, I did reboot the server after copying the new JAR into the "server/default/lib" folder.
                    Interesting tool. I tried it on a valueobject in oaiext. Below are the results. Do you see where it says "$NoParentClassLoader"? Maybe that is the problem?

                    Successfully loaded class: com.ndwc.oaiext.business.valuemodel.ActivityPayrollValueModel
                    ClassLoader: org.jboss.mx.loading.UnifiedClassLoader3
                    Parent(s): org.jboss.mx.loading.HeirarchicalLoaderRepository3$NoParentClassLoader
                    sun.misc.Launcher$AppClassLoader
                    sun.misc.Launcher$ExtClassLoader
                    Bootstrap classloader
                    Class found in file: file:/C:/jboss-3.2.1_tomcat-4.1.24/server/default/tmp/deploy/server/default/deploy/oaiExt.ear/51.oaiExt.ear-contents/oaiExtEJB.jar!/com/ndwc/oaiext/business/valuemodel/ActivityPayrollValueModel.class

                    • 7. Re: ClassCastException on narrow
                      eqsrf


                      Hi etrxndy,

                      I have the same problem with classloaders.My project has 1 war and 1 ear. The servlet (in war application) references ejb (in ear application). All works fine until i redeploy the ear file. I wouldn't like to put the home and remote interfaces in jbossl/lib because the ejbs can be changed and redeployed without redeploy the war file.
                      I found many topics about this exception, but i can' t find a solution to it. If your solve your problem, can you help me?

                      thanks!

                      • 8. Re: ClassCastException on narrow
                        etrxndy

                        Hello eqsrf,
                        First of all, you and I need to get better names. etrxndy, eqsrf are not pronounceable, nor are they personable. :)
                        Ok, I discovered some really excellent documentation at this link which explains ClassLoading and Jboss ClassLoading in a most excellent way:
                        http://umn.dl.sourceforge.net/sourceforge/jboss/ClassLoading.pdf

                        Here is an exerpt found on page 9 of the pdf or page 50 of the actual document, referring to ClassCastExceptions:
                        "This type of error is common when one redeploys an application to which other applications are holding
                        references to classes from the redeployed application. For example, a standalone war accessing an ejb.
                        If you are redeploying an application, all dependent applications must flush their class references. Typically
                        this requires that the dependent applications themselves be redeployed."

                        I hope this helps. I'm very impressed with this Jboss docu and I will be purchasing it, so I can read the whole thing.

                        About my issue, I have yet to lick it, but I have a few things to try based on this document. I will post when I figure it out.

                        • 9. Re: ClassCastException on narrow
                          etrxndy

                          Hey eqsrf,
                          I thought of a couple more things I wanted to point out. One of them is obvious, but I didn't know it. I'm assuming you have a client.jar in your war? The class you are trying to cast (whether by the narrow function or however) has to be the exact same file in the client.jar as it is in the ejb.jar. When I say exact, I mean, exact timestamp and serial ID number and everything.
                          The other thing is I found a post in my wanderings that might help. They talk about the old ClassCastException on a hod deploy. Here's the link: http://www.jboss.org/index.html?module=bb&op=viewtopic&t=14942

                          • 10. Re: ClassCastException on narrow
                            elaineqs


                            Hi etrxndy,

                            Thanks for your reply! I read the link you send. The text is good and clarify the classloader problem.

                            After read your post and the text, I observed that to do what i want i need to put the client.jar (with the ejb interfaces) inside lib directory of my war application (I'm using an entry class-path in the Manifest file of my ejb.jar to share its interfaces with my war application). However i wouldn't like to do this. I would like to share the interfaces of my ejb with my war and other ears, and i wouldn't like to change this war and ears always i change my ejbs.

                            I think this is not possible with Jboss.

                            Thanks anyway!

                            • 11. Re: ClassCastException on narrow
                              etrxndy

                              Ok, so here is what I finally discovered as the root of my ClassCastException problems. We have had multiple people working on this project and one of them made a file called jboss-app.xml and put it in the META-INF folder. In that file was specified a repository for the ear that it would be included in. This means that nothing outside that ear had access to the classes loaded into that repository. This was being done for both of the projects that I was trying to get to communicate.
                              After I deleted the jboss-app.xml files from both, I received duplicate class errors. We were including our custom-built framework jar file in each project so I got rid of that and made the framework a standalone deployment to jboss. After a little tweaking, the applications were able to use each other’s components.

                              • 12. Re: ClassCastException on narrow
                                marc.fleury

                                Ok,

                                that is interesting and we should document the errors better. The goal here is CLEARLY to have all the classes deployed ONCe (so you can talk cross service). Maybe the META-INF load should be loaded with a LOG message saying that the classes there will only see the classes defined.

                                Then I am glad there are duplicate class logs. One visibility one package per class, very good