In my last post I mentioned the issue with using CFGroovy's Hibernate when Hibernate is already loaded by the app server, such as the case with JBoss 4.2+ and a certain unreleased CFML runtime (cough … Centaur … cough). The gist of it is that Hibernate appears to create some static references to itself that circumvent the RootLoader that CFGroovy uses to isolate it's Hibernate environment. These static references get used because they already exist, so the classloader doesn't bother to load the classes from the RootLoader. As such Class comparisons fail within Hibernate's class hierarchy, because Class equality is both on the Class name and the ClassLoader used to load it.
Fortunately, this problem is mostly a "getting started" issue. I can't think of many places where you'd want to use CFGroovy's Hibernate and some other Hibernate environment in the same webapp (a partial port or an app being the one I can think of). As such, it's very unlikely you actually need multiple copies of Hibernate in your webapp classpath. And that means that you can get CFGroovy's Hibernate support to work simply by removing some unneeded JAR files.
For the unreleased CFML runtime that bundles Hibernate, simply removing /WEB-INF/cfusion/lib/hibernate3.jar will allow CFGroovy's Hibernate support to work. This means you can't use that runtime's Hibernate-backed features, but if you're using CFGroovy's Hibernate support, you probably don't need it.
For JBoss, Patrick Santora did some digging and had this to say:
Removed a few jars from within the jboss node I have cfgroovy running in "{node}\lib": antlr.jar hibernate3.jar hibernate-annotations.jar hibernate-entitymanager.jar Removed a few jars from within the cfgroovy hibernate_lib node: cglib-nodep-2.1_3.jar commons-collections.jar commons-httpclient.jar commons-logging.jar dom4j-1.6.1.jar ejb3-persistence.jar Start the jboss node and done. The removed jboss jar's were in the cfgroovy folder and vice versa so I just needed to do a comparison. Removing those conflicts fixed the problem. I am running on jboss 4.3.2 and Railo 3 war (community edition license)
I don't have a JBoss environment to confirm this on, but he said he's now up and running.
Keep it up Barney! We're following CF/Groovy integration intently.
For those who may have issues getting this going feel free to ping me and I will assist however I can.
Keep it up Barney, this is some slick stuff!!
By removing the hibernate jars from the {node}\lib folder, you're actually removing them from the server's classpath, not just a web applicaiton, right?
I use JBoss clustering, which uses hibernate for some stuff, so I don't think removing the hibernate jars on the jboss side will work for me.
I'm sure there is some way to set the context classloader, or something similar (perhaps there is another way to load a resource which will bypass the classloading error?), to get around the cfgroovy-hibernate classloading issue, but so far, I haven't found the answer.
Glad to see you're keeping folks posted about this, Barney! If I stumble upon something "pretty" that works I'll holler.
denny,
Yeah, if you remove it from the server, it won't be available to any applications. But that shouldn't prevent you from adding it to the applications that needed. You just can't have the server providing it to ALL applications. That's really the way you want to be doing it anyway so that each app can depend on its own version of Hibernate and upgrade separately from everything else.
Hmm… I'm pretty sure that the clustering and cache stuff is like a level above WAR and EAR deployment… I'm pretty sure JBoss itself is doing things with hibernate, before it ever tries to fire up WARs and such (at least if you're using clustering and cache whatnots).
And, other apps can include hibernate and work, so I think that the main problem is how the classloading/dynamic stuff is resolving things, or some such.
Changing to a child first classloading deal for the EAR I have railo in doesn't do anything different for cfgroovy, but cfgroovy isn't being deployed the way a WAR or EAR would be, so… eh.
'course, there's always the route of compiling the model sources first, and including them while loading hibernate; which probably wouldn't be too bad, as that's sorta what happens anyways… eh*2.
It might have taken me 8 years or so to figure out how to include commons-logging without messing up other applications, but I did it. This seems similar, but not as inherently broken, so hopefully in like 4 or so, perhaps 6 to be safe… :-)
denny,
Yeah, entirely possible. I've never used JBoss for anything. The issue with classloading is that Hibernate and the entity classes have to be loaded in the SAME classloader. So if Hibernate is already loaded anywhere, CFGroovy can't hope to work, since the entity classes don't exist until CFGroovy gets it's hands on them.
Does that mean I cannot use CF9's hibernate features together with cfgroovy + hibernate in the future?
Henry,
Yeah, that's the gist of it. You can use CFGroovy on CF9 without issue, but you can't use the HibernatePlugin on CF9 unless you delete the Hibernate JAR from the CF classpath, breaking the in-build CF9 Hibernate stuff. It seems unlikely that you'd want to use both CFGroovy's Hibernate and the in-built stuff on a single CF9 instance, so I'm not sure how much of a problem it'd be in real life, but certainly undesirable.
I hope I can deal with this issue in the future (since it affects JBoss 5 as well), but right now it's a couple items down the list. First priority is resurrecting seamless scriptlets, since that seems to be the bigger long-term win, as well as the original objective.
seamless scriptlets? oh, I thought it is already implemented… What does it do? :)
Hmm… I just don't want my app to be broken when upgraded to CF9. Hope things will work out better later.
In the very early releases, scriptlets were really seamless. You just dropped the JAR, imported the taglib and went for it. In order to get Hibernate to work, I had to make some internal changes so while you can still use scriptlets that way, you really want to use a managed runtime. Doing it without a managed runtime causes performance issues and can deplete PermGen. Fixing that, without sacrificing Hibernate, is the top priority.
More specifically, importing the taglib and running it should be production worthy without any additional configuration. If you want custom classloading, Hibernate, etc., then you have to do config work, but for the base use (scriptlets) it should be 100% transparent.
Using a URL classloader that's not in the server scope generally leads to threads that cannot be garbage collected… dunno if that's part of what's going on, but it's a possibility, maybe.
I think the problem with Hibernate is that it uses the thread local context or whatever, and you're swapping the thread contexts around with the classloading, so the context that loads Hibernate isn't the same one using it (or something like that perhaps).
I'm pretty sure I've gotten hibernate to load in a classloader (had to be real careful that it didn't go "outside", like for dom4j (to read config.xml — I think I used properties instead)), so IIRC, it's doable, at least.
Had any luck using pre-compiled entities?
denny,
yep, that's exactly it. Unfortunately to get Hibernate to load new stuff, you have to tear it and it's classloader down each time, and one of the big goals of the Hibernate integration was to avoid having to restart your container every time. In development I've found I usually get 20-30 Hibernate reloads before I run out of PermGen and have to restart the container. Far from ideal, but way better than having to restart every time. And since it's not a factor in production (where you're not modifying files), I've deemed it "ok enough" to this point.
I haven't tried pre-compiled entities, but if you've got those, why not just use Spring (or a Configuration directly)? CFGroovy's stuff isn't going to buy you much, except for less flexibility. :) Behind the scenes, CFGroovy just compiles all your Groovy entities into .class files and loads them as if they were pre-compiled. The magic is in the ability to do it repeatedly without restarting the container.
I was thinking the pre-compiled entities would only be pre-compiled for the groovyLoader, not for the application as a whole, so you could re-compile and re-load on the fly.
I had some relative success doing that for Hibernate alone, in a classloader, so as to get the "on the fly" loveliness. I was doing a lot with the entity-based hibernate objects (hashmaps), to get a lot of dynamicalness out of it, too. Haven't decided which is better… hibernate is basically a whole framework-ish-deal, when you get really into it– but then your model is hibernate-based, which might kinda suck sometimes…
I was thinking the groovyLoader, being designed for crazy classloading, might not have such a rough time with garbage collection and whatnot. Eh.
Been experimenting here and there (mostly checking out GORM-without-Grails stuff lately, as it sounds nifty), but I haven't really messed with a spring-based java model… after all the stuff I've seen lately tho, spring is at least as cool as ColdSpring ;-), might not be a bad thing to look into…
If the server isn't running out of memory while you're doing all your groovy stuff, then it looks like garbage collection *is* better when using a groovy classloader…. are you sure the reason it's running out of memory after a while isn't the URLClassLoader you're using to put groovy itself into play?
I've been doing a lot of load testing lately, guess I can slap some experiments together to see what happens.