NetBeans RCP & JPA (Part 2)

In my previous post at DZone, I talked about how to make JPA aware of the existence of multiple persistence.xml files, possibly one for each NetBeans RCP module, and able to merge them together. There were some rough corners, and now I've fixed one - furthermore I've also addressed another integration problem with JPA.

 

Please be aware that most of the code I'm showing you is now factored out of blueMarine and is available at https://openbluesky.dev.java.net/svn/openbluesky/trunk/src/Libraries/JPA - the revision used for this article is 225.

 

As you know, JPA is just an API specification, which is implemented by different vendors, e.g. Hibernate, TopLink (TopLink Essentials, part of Glassfish, is the reference implementation) and OpenJPA. This means that you have two groups of JAR files: the one including only the public API classes (that for simplicity I'll name jpa.jar) and the one including all the implementation stuff, plus the required libraries (again, for simplicity, I'll name it vendor.jar, even though usually there are multiple files).

For maximum flexibility and portability you usually do the following things:

  1. compile your application against jpa.jar, ignoring vendor.jar
  2. put in the classpath jpa.jar and the vendor.jar from your favourite implementor

In this way you can just replace the vendor.jar in different deployment contests, and have the thing working all the same.

The problem

When I tried to to that for the first time with blueMarine, a few months ago, I wasn't able to work it out. The basic idea, of course, is to create two NetBeans RCP Library Wrapper Modules, the former named JPA containing jpa.jar and the latter named Hibernate and containing vendor.jar; of course, the Hibernate Wrapper depends on the JPA Wrapper. You could create as well a module named TopLink which again depends on JPA, and so on. Your modules which use the classes in javax.persistence.* would just declare a dependency on JPA only, and you can then add to your RCP application either the Hibernate module or the TopLink module as you wish.

This doesn't work because of the dynamic discovery mechanism implemented in jpa.jar. Basically, the class Persistence searches for the JPA implementation by asking to the classloader to retrieve all the META-INF/services/javax.persistence.spi.PersistenceProvider files which contain the name of che implementation class. While this works in standard Java, under NetBeans RCP it won't discover Hibernate, since... the JPA Wrapper doesn't depend on the Hibernate Wrapper! While introducing this dependency would be formally wrong, you can't either accept it as a hack, otherwise you would get a circular dependency (remember that the Hibernate Wrapper is already depending on the JPA Wrapper) that is not allowed by the NetBeans RCP platform.

So far, I had to put both jpa.jar and vendor.jar in the Hibernate module, and have all of my code depending on it.

The solution

One way to fix the problem is to make a small patch to the JPA sources, more precisely in the javax.persistence.Persistence class and replace the discovery mechanism with the NetBeans RCP one, that is by using Lookup:

package javax.persistence;

import java.util.Map;
import java.util.logging.Logger;
import javax.persistence.spi.PersistenceProvider;
import org.openide.util.Lookup;
import it.tidalwave.netbeans.jpa.PersistenceProviderDecorator;

public class Persistence
{
private static final String CLASS = Persistence.class.getName();
private static final Logger logger = Logger.getLogger(CLASS);

private static PersistenceProvider persistenceProvider;

public Persistence()
{
}

public static EntityManagerFactory createEntityManagerFactory (final String persistenceUnitName)
{
return createEntityManagerFactory(persistenceUnitName, null);
}

public synchronized static EntityManagerFactory createEntityManagerFactory (final String persistenceUnitName,
final Map properties)
{
if (persistenceProvider == null)
{
final PersistenceProvider delegate = Lookup.getDefault().lookup(PersistenceProvider.class);

if (delegate == null)
{
throw new PersistenceException("No PersistenceProvider delegate found");
}

logger.info("PersistenceProvider delegate: " + delegate.getClass());
persistenceProvider = new PersistenceProviderDecorator(delegate);
}

final EntityManagerFactory emf = persistenceProvider.createEntityManagerFactory(persistenceUnitName, properties);

if (emf == null)
{
throw new PersistenceException("No Persistence provider for EntityManager named " + persistenceUnitName);
}

return emf;
}
}

Now, the class will be able to discover JPA implementations in any module, because the default Lookup has global visibility. To create this new JPA module, I've just extracted the javax.persistence.* sources from the Glassfish distribution and patched them - it's ok to do that, since they are release through the GPL v2 + Classpath Exception, so you're only forced to redistribute the thing, which I'm doing.

 

Indeed, I think there could be a way to implement this thing _without_ patching the existing files: you could just embed in the JPA Wrapper a proxy provider, which in turn would call Lookup. I'll try it before writing Parth Three of this series.

 

Having the standard Lookup plugged in will bring us additional flexibility, as you'll discover by reading the last paragraph of this post.

0
Average: 5 (1 vote)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Fabrizio Giudici replied on Mon, 2008/06/09 - 5:14am

BTW, a smaller remark. I don't know whether the JPA specifications allow for multi-threading invocations of Persistence, in any case I protected them with synchronized since I'm lazily instantiating an object that must exist in a single instance. I used the bad practice of putting synchronized in the method declaration; I've just committed some changes where synchronized only protects the relevant part of the code.

Fabrizio Giudici replied on Mon, 2008/06/09 - 5:36am

BTW, while discussing about HHH-2317, John Leach from JugTorino pointed me to a lab project about testing JPA implementations: Spikes. If you look at the sources, there's a patch for Hibernate that introduces a specific ConfigurationCustomizer that would allow you to plug in an external class to have a chance to patch the configuration between Hibernate actualizes it. I hope that the Hibernate guys accept it, as it would have made my work easier - even though I wouldn't have had a good chance to show how Lookup can be useful to apply customizations :o)

Fabrizio Giudici replied on Mon, 2008/06/09 - 6:23am

Sorry for the third addendum :-) but John has also a document that explains the problem with more details: http://www.syger.it/Tutorials/JPA10Gotchas.html. I've found it inspiring, I think that I'll incorporate some of the work, or the ideas, in the Part Three...

redgreeneyes replied on Thu, 2008/07/24 - 7:47am

Hi,

I have this error: " Phase two maintainance started - now starting JPA...
SEVERE [org.netbeans.core.modules]
java.lang.ClassNotFoundException: javax.persistence.spi.PersistenceProvider
        at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
"

 Can you tell me why the Lookup can't find my class?

 "final PersistenceProvider delegate = (PersistenceProvider)Lookup.getDefault().lookup(javax.persistence.spi.PersistenceProvider.class);"

 

Best regards,

Valentin.

Fabrizio Giudici replied on Thu, 2008/07/24 - 8:17am

Hi Valentin, thanks for trying the code.

How did you packed the PersistenceProvider?

redgreeneyes replied on Thu, 2008/07/24 - 9:45am in response to: fabriziogiudici

problem is solved if I add a cglib wrapper.

Now it seams that I can't find "java.lang.ClassNotFoundException: org.hibernate.proxy.EntityNotFoundDelegate
"....

 I've create a netbeans wrapper for Hibernate lib, all jars are in the classpath. Don't know what to do.

The exception is thrown when I create an instance of "Ejb3Configuration" ....

Fabrizio Giudici replied on Thu, 2008/07/24 - 9:57am

Maybe is it a problem similar to HHH-2317 described in page 3 of my post? It seems Hibernate has some evil interactions with the ClassLoader...

redgreeneyes replied on Thu, 2008/07/24 - 10:07am in response to: fabriziogiudici

How did you managed to create an instance of "Ejb3Configuration"?

redgreeneyes replied on Fri, 2008/07/25 - 2:26pm in response to:

http://bluemarine.tidalwave.it/issues/browse/BM-666

 "Resolved with a workaround in it.tidalwave.bluemarine.persistence.PatchedHibernatePersistence. "

Can you tell me where do I find this class? It seams that is not in the repository...

 

...found it under different name....

redgreeneyes replied on Sat, 2008/07/26 - 10:09am

hi,

Is anybody reading this comments? 

I've learned that  this error "org.hibernate.proxy.EntityNotFoundDelegate" occurs when I have a duplicate jpa.jar in classpath...first from hibernate libs and another from the wrapper module.

"How did you packed the PersistenceProvider? "

Just like you, I've created a regular netbeans module with jpa sources...

If I remove the jpa.jar from the hibernate wrapper module I receive this error: "java.lang.ClassNotFoundException: javax.persistence.spi.PersistenceProvider".

My module that contains the persistence.xml depends on my jpa module, I've added cglib wrapper too but with not much use...

Please give me any feedback...how to fix this...

If I replace the my modified JPA module with the default jpa.jar the lookup error is gone.

there is something that I missout...

now I found IT...there was a missing dependency between hibernate module and my jpa module....now everything works FINE....

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.