12 Replies Latest reply on Oct 11, 2007 11:45 AM by Torsten Römer

    ClassCastException/instanceof fails when getting object from

    Torsten Römer Newbie

      I am replicating a custom type in my JBossAS 4.0.5 cluster. It is working just great, but when I get an object put in the cache by one node on another, instanceof fails and if I just cast to the type I get a ClassCastException.
      In the debugger I can see it is the expected type - but somehow it is not the same.
      The type in question is a simple bean with some fields and getters/setters. I added a serialVersionUID to it and packed it in a JAR that I added to both the WAR which puts/gets the objects in/from the cache as well as to JBOSS_HOME/server/default/lib, since I get a NoClassDefFoundError when the objects a deserialized if I don't put it there.

      I'm sure it's me who is doing something wrong - but what?

      In general - is it wise at all to put custom types in the cache or should it better be just a Map with Strings and so?

      Torsten

        • 1. Re: ClassCastException/instanceof fails when getting object
          Jorge Morales Master

          I have encountered such a problem before, and mine was due to an Object being moved from one jar to another. While the object is in the same package, same serialVersionUID, classloader is different, and as far as I know, real class identifies by it`s classloader, package, class file, serialVersionUID. So if different classloaders load the class, in reality it is a different class, and you get a ClassCastException.

          We`ve been having hard times updating a running application that have objects in cache that have to be updated. For this we have done a real freaky solution.

          It`s real hard to update object model of an application running clustered, so good luck.

          • 2. Re: ClassCastException/instanceof fails when getting object
            Torsten Römer Newbie

            Different classloaders - I think that must be it. I guess it is different classloaders, the Tomcat one loading the webapp and another JBoss one loading the JAR.

            And thanks for the warning - I think I will skip the custom type and just put a Map in the cache with nothing else than Strings and Integers and such.

            Anyway, I wonder how to solve this classloading issue. Since the class is used by the webapp, it is in a JAR in WEB-INF/lib. But if it is only there, the cache cannot find it when it tries to deserialize the object. So were should I put the class? Is this possible at all?

            Thanks!
            Torsten

            • 3. Re: ClassCastException/instanceof fails when getting object
              Jorge Morales Master

              We have relied on default flat classloaders for JBoss AS, so all of our applications can have access to all others. You only have to take care of just bundling the jar file with model classes in one of your apps.

              In our case we have:
              core.ear --> With model classes, jboss caches, and core infraestructure.
              app1.ear --> Which uses cache. No model classes bundled.
              app2.ear --> Which uses cache. No model classes bundled.
              app3.war --> Which uses cache. No model classes bundled.

              Before we have had problems with this kind of layout, because we used model classes, used JBC persistance to DB, and as applications started, some model classes were not found. So we decided just to put in cache Maps of primitive wrappers, or classes bundled in core, which was the application deserializing from cache.

              Hope this helps.

              • 4. Re: ClassCastException/instanceof fails when getting object
                Torsten Römer Newbie

                You were absolutely right, that was an issue of different classloaders.
                Since I have my webservice and the model classes in a WAR file, I just set UseJBossWebLoader to true in jbossweb-tomcat55.sar/META-INF/jboss-service.xml - and see there, it works!

                But I wonder if setting UseJBossWebLoader to true is such a good idea, and I like your approach of putting the model classes in an EAR and the webapp in a WAR. But how would I create an EAR that contains nothing but my few class files? I tried to put both my classes as JAR and the WAR in an EAR and it deployed fine, but then I got an "...No ClassLoaders found for..." error when the cache unmarshalled my model class.

                Anyway - Thanks, you already helped me a lot! Whenever you happen to be in the south of Sweden let me know, and I'll buy you a beer ;-)

                Torsten

                • 5. Re: ClassCastException/instanceof fails when getting object
                  Torsten Römer Newbie

                  Naaawww, that "trick" with setting UseJBossWebLoader to true in jbossweb-tomcat55.sar/META-INF/jboss-service.xml doesn't really work either - as soon as I redeploy the webapp I get a ClassCastException again. It seems upon redeploying a new classloader is used so it is again not the same as the one used by the cache.
                  After a restart it works fine again - until I redeploy the webapp.

                  Torsten

                  • 7. Re: ClassCastException/instanceof fails when getting object
                    Jason Greene Master

                     

                    "dode" wrote:
                    Naaawww, that "trick" with setting UseJBossWebLoader to true in jbossweb-tomcat55.sar/META-INF/jboss-service.xml doesn't really work either - as soon as I redeploy the webapp I get a ClassCastException again. It seems upon redeploying a new classloader is used so it is again not the same as the one used by the cache.
                    After a restart it works fine again - until I redeploy the webapp.

                    Torsten


                    Since the cache holds a reference to the object you ask it to store, and since that reference is tied to a class, then the lifespan of the class has to last as long as the cache entry exists. So in other words, if you are going to put stuff in the cache that outlives your deployments, then you need to move those custom types to a deployment that lasts as long as your cache entry. A separate jar file would do.

                    Also you might want to stay away from UseJBossWebLoader=true, especially if you are using jsps (since classname conflicts are high).

                    -Jason

                    • 8. Re: ClassCastException/instanceof fails when getting object
                      Torsten Römer Newbie

                      What a great forum this is... one of the reasons why I like to work with JBoss!

                      Jason, thanks for the explanation. I have in the meantime packed my classes to be cached/replicated in a JAR, but I wasn't sure where to put it, so I put it in server/default/lib. Now all works fine, even after redeploying the WAR. Is this the right place to put the JAR? I think I can live with the fact that I have to restart the AS after a change of these classes, since this is not so likely to happen.

                      Brian, the TreeCacheMarshaller sounds promising - I'll give it a try as well.

                      Thanks!

                      Torsten

                      • 9. Re: ClassCastException/instanceof fails when getting object
                        Jason Greene Master

                         

                        "dode" wrote:

                        I have in the meantime packed my classes to be cached/replicated in a JAR, but I wasn't sure where to put it, so I put it in server/default/lib. Now all works fine, even after redeploying the WAR. Is this the right place to put the JAR?


                        You could also put it in deploy. The default config deploys jars before EARS and WARS. Just keep in mind that when you redeploy this jar, anything that depends on it will need to be cycled as well. This includes your war, and the actual cache entry that refers to it.

                        -Jason

                        • 10. Re: ClassCastException/instanceof fails when getting object
                          Brian Stansberry Master

                          A quick note about using the marshaller vs. locating your classes as Jason's suggested:

                          If you follow the approach Jason outlined, you can leave your objects in the cache instance in that VM. When you redeploy your webapp, it will be able to access those existing objects, with no problem.

                          With the marshaller, if you look at the example I mentioned you'll see it calls inactivateRegion() when you stop the webapp. That's the key thing. That call basically empties the local cache -- no objects left behind whose classes were loaded by the old classloader. When you deploy the webapp again, activateRegion() gets called, which will trigger a state transfer from another node in the cluster. When the transfer occurs, as part of deserializing the transferred objects, the required classes will be loaded from the new classloader.

                          That state transfer is a more complex process than simply using objects that are already cached locally. It also won't work if you simultaneously redeploy the webapp on all nodes in the cluster -- no one will have state to transfer if you do that!

                          • 11. Re: ClassCastException/instanceof fails when getting object
                            Jorge Morales Master

                            Dode,
                            As jason states, this is the same approach we took. But we didn't deploy in as a jar file in deploy dir, but as a war, because our core had some jboss-services, cache definitions, and more.
                            To make it start before other ear files that depend on it, we used this in server/x/conf/jboss-service.xml:

                            <attribute name="URLComparator">org.jboss.deployment.scanner.PrefixDeploymentSorter</attribute>
                            


                            And prefixed our applications with numbers, like this:

                            1core.ear
                            20app1.ear
                            30app2.ear


                            • 12. Re: ClassCastException/instanceof fails when getting object
                              Torsten Römer Newbie

                              Interesting, I didn't know the PrefixDeploymentSorter.

                              And I didn't know that it is possible to put a simple JAR in the deploy dir so it would just load the classes inside, either.

                              Anyway, I have put the JAR in the lib dir, since if I put it in deploy, it seems to get loaded after the cache tries to unmarshal objects it recieved from another node during startup, and a NoClassDefFoundError is thrown.

                              It is simple user sessions that I put in the cache, so it is nice that they "survive" a restart of a node.

                              I'm really happy with the setup now - I can restart any node, redeploy the webservice - no problems at all and the replication incl. database persistence through the CacheLoader works perfectly!

                              The farming deployment is BTW also really cool, since I have 4 nodes in the cluster.

                              Thanks again for all the help! Now I just have to convince the management... but I'm confident :-)

                              Torsten