Upgrading EclipseLink JPA in an OSGi environment
Up until now, Opencast was using EclipseLink JPA 2.0 which is now more than 5 years old. During this period of time, significant improvements have been made: the JPA Queries API evolved and with the announcement of the OSGi Enterprise specification the usage of JPA within an OSGi container was standardized (not to mention a huge number of bugs that have been squashed).
As Opencast is known to be heavy on the database, updating this central piece in the set of libraries will prove beneficial for both developers and adopters and lead to increased efficiency while executing queries as well as during setup of Opencast with an empty database.
How Opencast previously used JPA in OSGi
Having its origins with the organization that is best known for building a complete development environment based on OSGi components, EclipseLink shipped with minimal OSGi support since its very first version. When EclipseLink 2.0 is started within an OSGi container, an instance of javax.persistence.spi.PersistenceProvider
is registered in the OSGi service registry. In order to create an EntityManagerFactory, a java.util.Map
with connection information and other persistence properties needed to be passed to the PersistenceProvider instance. In Opencast, this Map was registered as an OSGi service as well.
Each component that needed to use JPA had to bind to the PersistenceProvider as well as to the persistence properties Map. It was each component’s own duty to create the EntityManagerFactory using the required persistence unit as well as carefully shutting it down once the component was deactivated.
private PersistenceProvider persistenceProvider;
private Map persistenceInformation;
private EntityManagerFactory emf;
/** OSGi PersistenceProvider bind method */
void setPersistenceProvider(PersistenceProvider persistenceProvider) {
this.persistenceProvider = persistenceProvider;
}
/** OSGi persistence information bind method */
void setPersistenceInformation(Map persistenceInformation) {
this.persistenceInformation = persistenceInformation;
}
/** OSGi component activation */
void activate() {
emf = persistenceProvider.createEntityManagerFactory(
“org.opencastproject.persistenceunit.name”,
persistenceInformation);
}
/** OSGi component deactivation */
void deactivate() {
if (emf != null && emf.isOpen())
emf.close();
}
Using JPA as defined by the OSGi Enterprise specification
As of EclipseLink 2.4, the legacy OSGi support was dropped and with Gemini JPA the Eclipse Foundation released its own implementation of the OSGi JPA Service Specification. The JPA implementation (EclipseLink) and the implementation of the OSGi JPA Service Specification (Gemini) are completely independent from each other and can easily be replaced by other implementations (like Apache OpenJPA and Apache Aries).
We won’t go into the details of the OSGi JPA Service Specification here but the most important difference to the legacy system is the fact that the EntityManagerFactory is fully managed. As soon as a bundle with a Persistence Descriptor is loaded, the JPA service implementation instantiates an EntityManagerFactory instance for the Persistence Unit and registers it in the OSGi service registry. Instead of creating and managing the EntityManagerFactory itself, a component now only needs to depend on the EntityManagerFactory service it intends to use.
private EntityManagerFactory emf;
/** OSGi PersistenceProvider bind method */
void setEntityManagerFactory(EntityManagerFactory emf) {
this.emf = emf;
}
Data source binding
Attentive readers might ask “But where is the database connection configured and initialized?”. With the JDBC and the JNDI Service Specification the OSGi Enterprise Platform Specifications defines two more concepts that are of great value.
Instead of passing around database connection properties and dealing with the instantiation of the proper JDBC driver implementation, OSGi offers a way more convenient way to work with database connections: org.osgi.service.jdbc.DataSourceFactory
services.
A DataSourceFactory is an abstraction for a specific database driver implementation (like MySQL or H2). An application can query the OSGi service registry to get a DataSourceFactory for the database system of their choice. This is turns out to be a big deal: You no longer have to make sure the specific database driver classes are on your bundles classpath! As you can imagine, this was quite painful with the existing implementation where all the connection setup was made in countless bundles. Some JDBC drivers like the MySQL Connector are already OSGi compliant and register a DataSourceFactory as soon as they are started within the OSGi environment. For other database systems corresponding wrappers exist.
“OK”, you say, “but we still need to configure the connection details somewhere, right?”. Of course! As soon as you have a DataSourceFactory instance it’s a breeze to create a DataSource (which acts as a factory for connections to a database) by passing the required connection information to the createDataSource method. This DataSource instance can then be registered in the OSGi service registry as well and therefore can be utilized by any other component.
But how does Gemini JPA map a specific DataSource to a Persistence Unit during its setup? This is where JNDI comes into the game. In the Persistence Unit descriptor the data source is defined like this:
<non-jta-data-source>osgi:service/jdbc/matterhorn</non-jta-data-source>
Gemini JPA performs a JNDI lookup to find an OSGi service that was registered with the value jdbc/matterhorn
for the service property osgi.jndi.service.name
. That’s it!
Conclusion
With the update to EclipseLink 2.6, Opencast not only receives an up-to-date JPA implementation, it gains a lot of flexibility by using the OSGi Enterprise Specifications. With this, things like database connection management or JPA container setup are now separated. This will enable community developers to improve these components without the need to touch “the whole system” which is a significant improvement.