Foros de discusión

How to avoid Class Loader Hell? (Multiple Hibernate instances.)

Nick Straguzzi, modificado hace 12 años.

How to avoid Class Loader Hell? (Multiple Hibernate instances.)

New Member Mensajes: 20 Fecha de incorporación: 29/04/11 Mensajes recientes
Folks:

I need some help and guidance. This is as much a general Tomcat inquiry as it is Liferay specific.

I have a custom Hibernate layer that I need all of my themes, portlets, hooks, etc. to be able to use within my Liferay portal. The plugin utilities Hot Deploy feature means that each plugin has its own Java class loader, etc., etc. This is all very familiar stuff. What I want to do is come up with a good, clean, maintainable way to share Hibernate (and logging utilities and Apache Commons JARs and some POJOs that I've written and other general utilities) across my entire Liferay portal instance.

The obvious way to do this is to move Hibernate and all its associated JAR files to the lib/ext directory. I've tried that. It works sometimes, but every so often it blows up -- I always get an exception regarding the logger utility being loaded by different class loaders, for example. If the first page I visit is a Control Panel page that I've hooked, the application crashes and must be restarted. (It's a class loader issue too, I know what causes it, not a big deal.) Lately I've been getting a Hibernate antlr.jar tokenizer error on a lazy batch-fetched collection; again, it's a class loader thing.

I'm sure that if I continue to work at it, I can set things up just so to avoid the errors. However, this is a real house of cards and it can come crashing down the next time I happen to use a feature that triggers a class loader conflict. We have separate development, qa and prod environments and potentially multiple prod instances -- I fear that if I continue down this path, sooner or later *one* of these environments is going to go out-of-sync and it will take me hours or days to figure out what went wrong.

What is the best way to avoid this problem? Must I move everything that might cause a conflict from ROOT to lib/ext? Is there a way I can set up a bundled library that each plugin can share, and which would automatically be included in each plugin WAR file (including all of the dependency JAR files, e.g. hibernate3.jar, antlr.jar, dom4j.jar, etc., etc.)?

I'm using LR IDE for Eclipse, and I would greatly appreciate any help that anyone can give me.

Regards,
Nick
thumbnail
David H Nebinger, modificado hace 12 años.

RE: How to avoid Class Loader Hell? (Multiple Hibernate instances.)

Liferay Legend Mensajes: 14919 Fecha de incorporación: 2/09/06 Mensajes recientes
Look at the Service Builder stuff. It handles crossing the class loader boundary pretty handily...

Moving your classes to lib/ext only makes those classes available at a global scope, it does nothing for sharing of runtime instances. If you had 50 plugins all using the shared code, you'd still have 50 unique database connections, etc.

Service Builder provides a sharable lib of interface classes and an implementation deployed in a single plugin. Other plugins use the interfaces to make calls against the shared implementation (shared db connection, etc).
Nick Straguzzi, modificado hace 12 años.

RE: How to avoid Class Loader Hell? (Multiple Hibernate instances.)

New Member Mensajes: 20 Fecha de incorporación: 29/04/11 Mensajes recientes
David, thanks for the response. Here's the problem though. I need to connect to a legacy database (using "legacy" loosely since its an external shared MySQL DB that's relatively young and still being built out structurally.) We're not using ServiceBuilder to connect to it. I need to do this outside of the SB framework because what is ultimately produced needs to be shared by multiple webapps in our Tomcat instance -- some of which are plugins running in the Liferay framework, some of which are not.

Bottom line is that I need a solution that does not require me to go through the entire SB process. If SB is already present, however, and I can use its interfaces to make calls from my extensions against a shared implementation, that's perfectly acceptable...provided I don't have to build my extensions in SB, that is.

Regards,
Nick
thumbnail
David H Nebinger, modificado hace 12 años.

RE: How to avoid Class Loader Hell? (Multiple Hibernate instances.)

Liferay Legend Mensajes: 14919 Fecha de incorporación: 2/09/06 Mensajes recientes
In this case, I suggest you bundle all of your plugins together into a single distributable war file...

Will be a pain, but since they live in the same war there's no class loader issues and you can also share runtime instances (i.e. a shared connection pool).

Service builder actually does a lot of magic to handle the class loader issue. In an overly simplified nutshell, instead of creating POJOs that are handed around (as you would do in a normal Hibernate implementation), an interface is defined that a simulated POJO implements and a 'class loader proxy' which wraps the implementation. The other wars can reference the interface and use the implementation class w/o raising the class loader exception because of the CLPs.

If you really dig into the code built by Service Builder, you can see the patterns they use for handling the interfaces, the implementation classes, and the CLPs. There may be enough information that you could gleam there to replicate the same sort of thing in your own implementation.

But that's definitely the hard road; the easy road (dev and maintenance aside) is the one war implementation.
Nick Straguzzi, modificado hace 12 años.

RE: How to avoid Class Loader Hell? (Multiple Hibernate instances.)

New Member Mensajes: 20 Fecha de incorporación: 29/04/11 Mensajes recientes
Thanks David. Neither solution, unfortunately, is palatable. I'm not sure what I'm going to do. I'm wondering if there is a way to solve this problem at the Tomcat level, since I know it uses nested classloaders -- perhaps I can figure out a way to get all of the Hibernate stuff running at something above the webapp level, though I'm afraid that's a bit out of my development league. We'll see. Very disappointed there's no good, clean, cross-platform solution, but that's certainly neither yours nor Liferay's fault.

Regards,
Nick
thumbnail
David H Nebinger, modificado hace 12 años.

RE: How to avoid Class Loader Hell? (Multiple Hibernate instances.)

Liferay Legend Mensajes: 14919 Fecha de incorporación: 2/09/06 Mensajes recientes
Nick Straguzzi:
Very disappointed there's no good, clean, cross-platform solution, but that's certainly neither yours nor Liferay's fault.


It's the standard for application servers and is meant to block one web application from interfering w/ another application.

Like I said, you might be able to gleam some knowledge from how SB handles crossing the boundary and roll your own solution based upon that, but it's definitely a hard road...

Alternatively if you did the hibernate stuff using SB w/ the remote service flag set to yes, you would have remote services that you could call from other tomcat instances/applications...
thumbnail
Mika Koivisto, modificado hace 12 años.

RE: How to avoid Class Loader Hell? (Multiple Hibernate instances.)

Liferay Legend Mensajes: 1519 Fecha de incorporación: 7/08/06 Mensajes recientes
If your other webapps are running in the same tomcat instance as Liferay you could actually use ServiceBuilder to build interface to your "legacy" DB and then just use those services from all the apps. Even though ServiceBuilder is really meant for building both service and persistence layer it can build also just the service layer by defining entities with no columns.