JBoss comes shipped with Hibernate by default. Upgrading Hibernate is similar to upgrading any other 3rd party library in your application deployed on JBoss. As long as you understand how the classloading works in JBoss, the upgrading should be pretty straightforward.
For a brief (well not so brief) background about classloaders in JBoss, have a look at these wiki articles:
How classloading works in JBoss
How to configure classloaders in JBoss
Once you read through these wiki articles, you will understand that if your application needs to have its own version of a library (does not matter if it is Hibernate or some other 3rd party library), you will have to configure classloader scoping through the xml file.
So why am i writing this stuff all over again, when these two wiki articles have enough details about classloading scoping? Its mainly because of some tricky issues, which have been reported in the JBoss forums, with upgrading Hibernate (specifically to Hibernate version 3.2.6) on JBoss-4.2.x (specifically JBoss-4.2.2 GA). The rest of the article tries to explain these issues and way to fix them. Though this is written to be more oriented towards upgrading Hibernate, whatever has been explained here will apply to almost every 3rd party library upgrade on JBoss.
So let's start then!
Details about the default installation of JBoss-4.2.2 GA:
JBoss-4.2.2 GA ships with
What we intend to do is, upgrade Hibernate to use 3.2.6 GA. Let's assume, we have an EAR which will be deployed to JBoss:
So first step would be package the upgraded Hibernate jar files in the application (MyApp.ear). Its crucial to understand that you have to be absolutely sure that you have packaged all the required hibernate jars and the correct versions of those jars in your application. This Hibernate compatibility matrix will help you in picking up the correct versions. However, you still have to know "which" hibernate jars you need to package in the application.
Based on what i have seen in the forums, the issues faced while upgrading Hibernate were more related to users missing out certain dependent hibernate jar files. Debugging such issues was not very easy since, the errors that got thrown were not simple ClassNotFoundException (which you usually associate with a missing jar). Various errors like ClassCastException, NoSuchMethodException were thrown mainly because Hibernate in this version (3.2.6 GA) refactored a lot of their code to move them to different "projects". For example, the org.hibernate.search package was earlier in the "Hibernate Annotations" project (hibernate-annotations.jar) but with this new release, it was moved to a separate "Hibernate Search" project (hibernate-search.jar). Same applies to org.hibernate.validator package which earlier was in the "Hibernate Annotations" project (hibernate-annotations.jar) but with this new release, it was moved to a separate "Hibernate Validator" project.
So how does it matter if those hibernate packages were moved to a different project (jar)? Here's a very brief explanation of what happens:
- JBoss, in its lib folder, has an older version of Hibernate (3.2.4) and other hibernate related jar files, including the hibernate-annotations.jar. In this version, the hibernate-annotations.jar contained the org.hibernate.search and org.hibernate.validator and various other packages.
- You decide to upgrade Hibernate in your application by packaging the hibernate jars in your application and enabling classloader configuration. You package *only* the latest version of core hibernate jar, the hibernate-annotations.jar and maybe even the hibernate-entitymanager.jar.
Note: You have NOT packaged the hibernate-validator.jar nor the hibernate-search.jar.
- You start JBoss and the server tries to deploy your application. While deploying, for configuring Hibernate, various Hibernate classes are used, which includes the classes belonging to core hibernate jar and also org.hibernate.validator and org.hibernate.search packages.
- Since you have configured classloader scoping for your application, JBoss loads the hibernate core classes, the hibernate entitymanager classes and the hibernate annotation classes from the upgraded jars packaged in your application.
- But when a class belonging to org.hibernate.validator or org.hibernate.search package is being requested for, JBoss sees that these classes are not present the jars packaged in your application. So it delegates the classloading to the parent classloader which looks for the classes in the jar files present in the JBoss lib folder (%JBOSS_HOME%/server/< serverName>/lib folder). Here it finds that these packages are present in the hibernate-annotations.jar (older version) and loads those classes from there. While doing so, it also loads the related classes from various other hibernate packages (which might already have been loaded by a different classloader - remember the classes loaded from the jars in your application). Ultimately, this results to the same classes being loaded twice by different classloaders. Later on when you access these classes in your application you might run into ClassCastExceptions.
This is just one example of what might go wrong. Infact, you might not get a clear picture based on this brief explanation. So if you are interested in understanding better (and have some time), then go through these forum discussions which have a lot more details (and which actually made me come up with this article):
ClassCastException for org.hibernate.search.event.FullTextIndexEventListener
Again the ClassCastException for org.hibernate.search.event.FullTextIndexEventListener
This time a NoSuchMethodException: org.hibernate.validator.ClassValidator.
So now that we have seen what kind of issues you might run into while upgrading, let's now come back to our original plan of upgrading hibernate :)
1) Enable classloader scoping through jboss-app.xml:
Note: The string org.myapp:loader=SomeClassloader is any unique ObjectName
2) Include the following jar files in the application package:
Note: Please follow this page for downloading and figuring out the correct version of hibernate jars required (compatibility matrix).
So this is how your application packaging will look like finally:
That's it! The upgrade itself is simple enough. Note that, in this article, i have used an EAR as an example, but this applies to WAR files too. In WAR files, the jars will be placed in the WEB-INF/lib folder and the classloader configuration will be done through the jboss-web.xml file which will be in WEB-INF folder.
For a brief (well not so brief) background about classloaders in JBoss, have a look at these wiki articles:
How classloading works in JBoss
How to configure classloaders in JBoss
Once you read through these wiki articles, you will understand that if your application needs to have its own version of a library (does not matter if it is Hibernate or some other 3rd party library), you will have to configure classloader scoping through the xml file.
So why am i writing this stuff all over again, when these two wiki articles have enough details about classloading scoping? Its mainly because of some tricky issues, which have been reported in the JBoss forums, with upgrading Hibernate (specifically to Hibernate version 3.2.6) on JBoss-4.2.x (specifically JBoss-4.2.2 GA). The rest of the article tries to explain these issues and way to fix them. Though this is written to be more oriented towards upgrading Hibernate, whatever has been explained here will apply to almost every 3rd party library upgrade on JBoss.
So let's start then!
Details about the default installation of JBoss-4.2.2 GA:
JBoss-4.2.2 GA ships with
Hibernate EntityManager 3.2.1.GA Hibernate Annotations 3.2.1.GA Hibernate 3.2.4.sp1
What we intend to do is, upgrade Hibernate to use 3.2.6 GA. Let's assume, we have an EAR which will be deployed to JBoss:
MyApp.ear | |--- META-INF | | | | | |--- application.xml | | | |--- jboss-app.xml | | |--- lib | | | |--- [some jar files required by my app] | | |--- MyApp.war
So first step would be package the upgraded Hibernate jar files in the application (MyApp.ear). Its crucial to understand that you have to be absolutely sure that you have packaged all the required hibernate jars and the correct versions of those jars in your application. This Hibernate compatibility matrix will help you in picking up the correct versions. However, you still have to know "which" hibernate jars you need to package in the application.
Based on what i have seen in the forums, the issues faced while upgrading Hibernate were more related to users missing out certain dependent hibernate jar files. Debugging such issues was not very easy since, the errors that got thrown were not simple ClassNotFoundException (which you usually associate with a missing jar). Various errors like ClassCastException, NoSuchMethodException were thrown mainly because Hibernate in this version (3.2.6 GA) refactored a lot of their code to move them to different "projects". For example, the org.hibernate.search package was earlier in the "Hibernate Annotations" project (hibernate-annotations.jar) but with this new release, it was moved to a separate "Hibernate Search" project (hibernate-search.jar). Same applies to org.hibernate.validator package which earlier was in the "Hibernate Annotations" project (hibernate-annotations.jar) but with this new release, it was moved to a separate "Hibernate Validator" project.
So how does it matter if those hibernate packages were moved to a different project (jar)? Here's a very brief explanation of what happens:
- JBoss, in its lib folder, has an older version of Hibernate (3.2.4) and other hibernate related jar files, including the hibernate-annotations.jar. In this version, the hibernate-annotations.jar contained the org.hibernate.search and org.hibernate.validator and various other packages.
- You decide to upgrade Hibernate in your application by packaging the hibernate jars in your application and enabling classloader configuration. You package *only* the latest version of core hibernate jar, the hibernate-annotations.jar and maybe even the hibernate-entitymanager.jar.
Note: You have NOT packaged the hibernate-validator.jar nor the hibernate-search.jar.
- You start JBoss and the server tries to deploy your application. While deploying, for configuring Hibernate, various Hibernate classes are used, which includes the classes belonging to core hibernate jar and also org.hibernate.validator and org.hibernate.search packages.
- Since you have configured classloader scoping for your application, JBoss loads the hibernate core classes, the hibernate entitymanager classes and the hibernate annotation classes from the upgraded jars packaged in your application.
- But when a class belonging to org.hibernate.validator or org.hibernate.search package is being requested for, JBoss sees that these classes are not present the jars packaged in your application. So it delegates the classloading to the parent classloader which looks for the classes in the jar files present in the JBoss lib folder (%JBOSS_HOME%/server/< serverName>/lib folder). Here it finds that these packages are present in the hibernate-annotations.jar (older version) and loads those classes from there. While doing so, it also loads the related classes from various other hibernate packages (which might already have been loaded by a different classloader - remember the classes loaded from the jars in your application). Ultimately, this results to the same classes being loaded twice by different classloaders. Later on when you access these classes in your application you might run into ClassCastExceptions.
This is just one example of what might go wrong. Infact, you might not get a clear picture based on this brief explanation. So if you are interested in understanding better (and have some time), then go through these forum discussions which have a lot more details (and which actually made me come up with this article):
ClassCastException for org.hibernate.search.event.FullTextIndexEventListener
Again the ClassCastException for org.hibernate.search.event.FullTextIndexEventListener
This time a NoSuchMethodException: org.hibernate.validator.ClassValidator.
So now that we have seen what kind of issues you might run into while upgrading, let's now come back to our original plan of upgrading hibernate :)
1) Enable classloader scoping through jboss-app.xml:
<jboss-app> <loader-repository> org.myapp:loader=SomeClassloader <loader-repository-config> java2ParentDelegation=false </loader-repository-config> </loader-repository> </jboss-app>
Note: The string org.myapp:loader=SomeClassloader is any unique ObjectName
2) Include the following jar files in the application package:
Hibernate Core jar (3.2.6 GA) Hibernate Annotations jar (3.2.x or 3.3.x) Hibernate EntityManager jar (3.2.x or 3.3.x) Hibernate Validator jar (3.0.x) Hibernate Search jar (3.0.x) and maybe even Lucene Core jar (lucene-core-2.2.0.jar)
Note: Please follow this page for downloading and figuring out the correct version of hibernate jars required (compatibility matrix).
So this is how your application packaging will look like finally:
MyApp.ear | |--- META-INF | | | | | |--- application.xml | | | |--- jboss-app.xml | | |--- lib | | | |--- [some jar files required by my app] | | | |--- hibernate3.jar (the hibernate core jar) | | | |--- hibernate-annotations.jar | | | |--- hibernate-entitymanager.jar | | | |--- hibernate-validator.jar | | | |--- hibernate-search.jar | | | |--- lucene-core-2.2.0.jar | | |--- MyApp.war
That's it! The upgrade itself is simple enough. Note that, in this article, i have used an EAR as an example, but this applies to WAR files too. In WAR files, the jars will be placed in the WEB-INF/lib folder and the classloader configuration will be done through the jboss-web.xml file which will be in WEB-INF folder.
12 comments:
How about you explain more on libraries configuration in war files by overriding or solely depends on WEB-INF/lib .
Just a short remark as I really found your article very useful.
As of 2.4 the loader-repository-config tag should not be inside the loader-repository tag of the pom.xml but after it. In the resultig jboss-app.xml everything is fine then.
The ClassLoading Configuration article has moved to
http://community.jboss.org/wiki/ClassLoadingConfiguration
Hi,
I want to upgrade hibernate in jboss 4.2.3 with hibernate 3.5.0.
I try to follow your tips with enabled classloader scoping through jboss-app.xml and include hibernate jar files in my ear but im still facing a classcastexception when i start jboss. I can see my loader through the jmx console but it seems like isolation not working. i also try to set up isolation in the ear-deployer.xml file. Am i missing something ?
Thanks for your reply.
@Anonymous,
Could you please create a new thread in the JBoss AS forum with all the details including the ear contents and the exception stacktrace, so that we can discuss it there?
@Jaikiran
Thanks for your quick reply.
I ve just create a new thread in jboss as forum.
See you there.
Very Helpful post, but the links in the beginning of the article are stale the new links are:
http://community.jboss.org/wiki/JBossClassLoader
and
http://community.jboss.org/wiki/classloadingconfiguration
another useful Jboss article is:
http://community.jboss.org/wiki/JBossClassLoadingUseCases
Hi I had the same problem. But my project is not enterprise application. Can you describe the steps for .war file. I found some diff configuration for .war file in http://community.jboss.org/wiki/classloadingconfiguration.
But that is not working for me.
I'm trying to solve a similar problem on jboss 4.0.5 but it doesn't seem to have a jboss-app.xml. Should I be using jboss-web.xml instead?
If you are using a .ear file then you should be using jboss-app.xml in the META-INF of the .ear. If you don't have such a file then you can create one.
However, if you are deploying a standalone .war (which is *not* part of a .war) then you can use the jboss-web.xml in the .war/WEB-INF folder.
the compatibility matrix link is broken it is here community.jboss.org/wiki/HibernateCompatibilityMatrix
Post a Comment