Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

NetBeans RCP & JPA (Part 2)

DZone's Guide to

NetBeans RCP & JPA (Part 2)

· Java Zone
Free Resource

Learn how to troubleshoot and diagnose some of the most common performance issues in Java today. Brought to you in partnership with AppDynamics.

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.

Smoothing a rough corner

If you read my previous article, you recall that I did things by installing a specific ClassLoaderDecorator across calls to Persistence.createEntityManagerFactory(...), but this required to have a single entry point to JPA. Now, having re-defined Persistence, this is no longer true. You have probably noted, in the listing above, that I wrapped the discovered provider into a PersistenceProviderDecorator:

package it.tidalwave.netbeans.jpa;

import java.util.Map;
import java.util.logging.Logger;
import javax.persistence.EntityManagerFactory;
import javax.persistence.spi.PersistenceProvider;
import javax.persistence.spi.PersistenceUnitInfo;

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

private final PersistenceProvider delegate;

private MultiPersistenceXMLManager multiPersistenceXMLManager;

interface EntityManagerFactoryFactory
{
public EntityManagerFactory createEntityManagerFactory();
}

public PersistenceProviderDecorator (final PersistenceProvider delegate)
{
this.delegate = delegate;
}

public EntityManagerFactory createEntityManagerFactory (final String puName,
final Map properties)
{
return createEntityManagerFactory(new EntityManagerFactoryFactory()
{
public EntityManagerFactory createEntityManagerFactory()
{
return delegate.createEntityManagerFactory(puName, properties);
}
});
}

public EntityManagerFactory createContainerEntityManagerFactory (final PersistenceUnitInfo puInfo,
final Map properties)
{
return createEntityManagerFactory(new EntityManagerFactoryFactory()
{
public EntityManagerFactory createEntityManagerFactory()
{
return delegate.createContainerEntityManagerFactory(puInfo, properties);
}
});
}

private synchronized EntityManagerFactory createEntityManagerFactory (final EntityManagerFactoryFactory factory)
{
if (multiPersistenceXMLManager == null)
{
multiPersistenceXMLManager = new MultiPersistenceXMLManager();
}

final Thread currentThread = Thread.currentThread();
final ClassLoader saveClassLoader = currentThread.getContextClassLoader();
currentThread.setContextClassLoader(new ClassLoaderDecorator(saveClassLoader, multiPersistenceXMLManager));

try
{
return factory.createEntityManagerFactory();
}
finally
{
currentThread.setContextClassLoader(saveClassLoader);
}
}
}

 

As you can see, it's now up to the decorator to install the ClassLoaderDecorator where appropriate. This means that now I can use regular JPA code and automatically get the RCP enhancements, such as the support for multiple persistence.xml files (and other stuff that I might add in future).

But life is not so easy

After the initial excitement for this refactoring, I got stuck into a serious Hibernate bug. When Hibernate boots and tries to create the CGLIB-enhaced entity classes, you get a

java.lang.NoClassDefFoundError: org/hibernate/proxy/HibernateProxy

This is not a problem of RCP Module dependencies: it's rather Hibernate issue HHH-2317 as - if I understand well - it is trying to find HibernateProxy using the classloader of the entity classes. In spite of being two years old, and some people having provided tentative patches, the issues is still open.

 

Don't be too pessimistic: it's possible that you won't experience this problem. In blueMarine I have CGLIB in a separate Library Wrapper, which Hibernate depends on, because I also need direct access to CGLIB in some parts of the application. Maybe if you put CGLIB inside the Hibernate Library Wrapper the thing will disappear...

 

I had previously confrontated with this issue in blueMarine and found a workaround: disabling the lazy loading. I'm not going to give you many details, but lazy-loading is a feature of many JPA implementations when dealing with relationships: basically, when you fetch an entity which is related to others, you can choose to eagerly fetch the whole set of related entities, or to lazy load them. In the latter case, usually some additional trickery with CGLIB is performed, and this requires a specific enhancer by Hibernate. If you don't need lazy loading (at the moment I'm not having JPA to manage relationships) you can disable the feature, and it turns out that this will have another code enhancer to be selected that is not affected by HHH-2317.

To disable lazy loading, you have to put default-lazy="false" in every Hibernate mapping file for your entities. Too bad that if you are using the plain JPA API, you don't have Hibernate mapping files and it looks like there's no other place, such as the configuation properties, to disable this feature (Hibernate guys, this is really a shame ;-)

What to do?

Well, if you can't use configuration properties, you can always try to programmatically patch the configuration objects inside Hibernate. Looking at the sources of HibernatePersistence, which is the class implementing PersistenceProvider, I've found that:

  1. It creates an instance of Ejb3Configuration
  2. It populates it in the proper way (e.g. by looking at persistence.xml)
  3. It uses it to create an EntityManagerFactory

Fortunately the steps 2 and 3 are separate, so one could insert some code in the middle to patch the configuration:

    private void patch (final Ejb3Configuration config)
{
for (final Iterator i = config.getClassMappings(); i.hasNext(); )
{
final PersistentClass persistentClass = (PersistentClass)i.next();
persistentClass.setLazy(false);
logger.fine("Set lazy=false for " + persistentClass.getEntityName());
}
}

Basically the idea is to replace HibernatePersistence with this class:

package it.tidalwave.bluemarine.persistence;

import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.TreeSet;
import java.util.logging.Logger;
import javax.persistence.EntityManagerFactory;
import javax.persistence.spi.PersistenceProvider;
import javax.persistence.spi.PersistenceUnitInfo;
import org.hibernate.ejb.Ejb3Configuration;
import org.hibernate.mapping.PersistentClass;

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

public EntityManagerFactory createEntityManagerFactory (final String persistenceUnitName, final Map overridenProperties)
{
final Ejb3Configuration cfg = new Ejb3Configuration();
final Ejb3Configuration configured = cfg.configure(persistenceUnitName, overridenProperties);
patch(configured);
final EntityManagerFactory emf = configured != null ? configured.buildEntityManagerFactory() : null;
return emf;
}

public EntityManagerFactory createContainerEntityManagerFactory (final PersistenceUnitInfo info, final Map map)
{
final Ejb3Configuration cfg = new Ejb3Configuration();
final Ejb3Configuration configured = cfg.configure(info, map);
patch(configured);
final EntityManagerFactory emf = configured != null ? configured.buildEntityManagerFactory() : null;
return emf;
}

private void patch (final Ejb3Configuration config)
{
for (final Iterator i = config.getClassMappings(); i.hasNext(); )
{
final PersistentClass persistentClass = (PersistentClass)i.next();
persistentClass.setLazy(false);
logger.fine("Set lazy=false for " + persistentClass.getEntityName());
}
}
}

 

BTW, it's not technically a decorator since it doesn't call the original service, but it's doing a similar thing so at the moment I'm leaving the name as is.

 

Now, how could I tell the system to use my service implementation instead of the original one? The point is that I don't want to put this fix in the JPA module, which must be generic; it should rather go in blueMarine code.

Lookup to the rescue

Lookup is helping now, since it allows a syntax extension to the META-INF/services stuff to replace an existing implementation. So I just have to add to blueMarine a file META-INF/services/javax.persistence.spi.PersistenceProvider which contains:

#-org.hibernate.ejb.HibernatePersistence
it.tidalwave.bluemarine.persistence.HibernatePersistenceDecorator

and voilà, the game is over.

So, the lesson is that you now can use JPA in a NetBeans RCP application as you would in the plain Java SE/EE context and, by plugging in the Lookup facility from NetBeans, you can enjoy some extra features. Now the whole blueMarine has been refactored and the code only depends on the JPA module; if I want to replace the JPA provider, I just need to create a new Library Wrapper and put it in place of the Hibernate one (supposing that I don't find other classloader-related issues...).

For part three I'll try to adress another problem. At the moment, I need to add the database Library Wrapper (Derby in my case) as a dependency of Hibernate, otherwise it won't be able to open the connection. Of course I don't like that, since I prefer to have an agnostic JPA provider implementation and then plug in the proper database JDBC Wrapper as I wish. Since this is a classloader issue and I already have a custom classloader in my PersistenceProviderDecorator, I think that the thing is doable.

As I said in the opening, the JPA part of this code is now refactored out of the blueMarine project, and is inside of OpenBlueSky. If there is interest to it, I could submit it to the platformX project to become an extension of the platform. Let me hear your feedback.

Understand the needs and benefits around implementing the right monitoring solution for a growing containerized market. Brought to you in partnership with AppDynamics.

Topics:

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}