18. OSGi
18.1. OSGi Specification and Environment
Hibernate targets the OSGi 4.3 spec or later. It was necessary to start with 4.3, over 4.2, due to our dependency on OSGi’s BundleWiring
for entity/mapping scanning.
Hibernate supports three types of configurations within OSGi.
Container-Managed JPA Container-Managed JPA
Unmanaged JPA Unmanaged JPA
Unmanaged Native Unmanaged Native
18.2. hibernate-osgi
Rather than embed OSGi capabilities into hibernate-core, hibernate-entitymanager, and sub-modules, hibernate-osgi was created. It’s purposefully separated, isolating all OSGi dependencies. It provides an OSGi-specific ClassLoader
(aggregates the container’s ClassLoader
with core and EntityManager
ClassLoader
s), JPA persistence provider, SessionFactory
/EntityManagerFactory
bootstrapping, entities/mappings scanner, and service management.
18.3. features.xml
Apache Karaf environments tend to make heavy use of its "features" concept, where a feature is a set of order-specific bundles focused on a concise capability. These features are typically defined in a features.xml
file. Hibernate produces and releases its own features.xml
that defines a core hibernate-orm
, as well as additional features for optional functionality (caching, Envers, etc.). This is included in the binary distribution, as well as deployed to the JBoss Nexus repository (using the org.hibernate
groupId and hibernate-osgi
with the karaf.xml
classifier).
Note that our features are versioned using the same ORM artifact versions they wrap. Also note that the features are heavily tested against Karaf 3.0.3 as a part of our PaxExam-based integration tests. However, they’ll likely work on other versions as well.
hibernate-osgi, theoretically, supports a variety of OSGi containers, such as Equinox. In that case, please use features.xm
l as a reference for necessary bundles to activate and their correct ordering. However, note that Karaf starts a number of bundles automatically, several of which would need to be installed manually on alternatives.
18.4. QuickStarts/Demos
All three configurations have a QuickStart/Demo available in the hibernate-demos project:
18.5. Container-Managed JPA
The Enterprise OSGi specification includes container-managed JPA. The container is responsible for discovering persistence units in bundles and automatically creating the EntityManagerFactory
(one EntityManagerFactory
per PersistenceUnit
). It uses the JPA provider (hibernate-osgi) that has registered itself with the OSGi PersistenceProvider
service.
18.6. Enterprise OSGi JPA Container
In order to utilize container-managed JPA, an Enterprise OSGi JPA container must be active in the runtime. In Karaf, this means Aries JPA, which is included out-of-the-box (simply activate the jpa
and transaction
features). Originally, we intended to include those dependencies within our own features.xml
. However, after guidance from the Karaf and Aries teams, it was pulled out. This allows Hibernate OSGi to be portable and not be directly tied to Aries versions, instead having the user choose which to use.
That being said, the QuickStart/Demo projects include a sample features.xml showing which features need activated in Karaf in order to support this environment. As mentioned, use this purely as a reference!
18.7. persistence.xml
Similar to any other JPA setup, your bundle must include a persistence.xml
file. This is typically located in META-INF
.
18.8. DataSource
Typical Enterprise OSGi JPA usage includes a DataSource
installed in the container. Your bundle’s persistence.xml
calls out the DataSource
through JNDI. For example, you could install the following H2 DataSource
. You can deploy the DataSource
manually (Karaf has a deploy
dir), or through a "blueprint bundle" (blueprint:file:/[PATH]/datasource-h2.xml
).
Example 378. datasource-h2.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
First install the H2 driver using:
> install -s mvn:com.h2database/h2/1.3.163
Then copy this file to the deploy folder
-->
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<bean id="dataSource" class="org.h2.jdbcx.JdbcDataSource">
<property name="URL" value="jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE"/>
<property name="user" value="sa"/>
<property name="password" value=""/>
</bean>
<service interface="javax.sql.DataSource" ref="dataSource">
<service-properties>
<entry key="osgi.jndi.service.name" value="jdbc/h2ds"/>
</service-properties>
</service>
</blueprint>
That DataSource
is then used by your persistence.xml
persistence-unit. The following works in Karaf, but the names may need tweaked in alternative containers.
Example 379. META-INF/persistence.xml
<jta-data-source>osgi:service/javax.sql.DataSource/(osgi.jndi.service.name=jdbc/h2ds)</jta-data-source>
18.9. Bundle Package Imports
Your bundle’s manifest will need to import, at a minimum,
javax.persistence
org.hibernate.proxy
andjavassist.util.proxy
, due to Hibernate’s ability to return proxies for lazy initialization (Javassist enhancement occurs on the entity’sClassLoader
during runtime).
18.10. Obtaining an EntityManger
The easiest, and most supported, method of obtaining an EntityManager
utilizes OSGi’s OSGI-INF/blueprint/blueprint.xml
in your bundle. The container takes the name of your persistence unit, then automatically injects an EntityManager
instance into your given bean attribute.
Example 380. OSGI-INF/blueprint/blueprint.xml
<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns:jpa="http://aries.apache.org/xmlns/jpa/v1.0.0"
xmlns:tx="http://aries.apache.org/xmlns/transactions/v1.0.0"
default-activation="eager"
xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<!-- This gets the container-managed EntityManager and injects it into the DataPointServiceImpl bean.
Assumes DataPointServiceImpl has an "entityManager" field with a getter and setter. -->
<bean id="dpService" class="org.hibernate.osgitest.DataPointServiceImpl">
<jpa:context unitname="managed-jpa" property="entityManager"/>
<tx:transaction method="*" value="Required"/>
</bean>
<service ref="dpService" interface="org.hibernate.osgitest.DataPointService"/>
</blueprint>
18.11. Unmanaged JPA
Hibernate also supports the use of JPA through hibernate-entitymanager, unmanaged by the OSGi container. The client bundle is responsible for managing the EntityManagerFactory
and EntityManager
s.
18.12. persistence.xml
Similar to any other JPA setup, your bundle must include a persistence.xml
file. This is typically located in META-INF
.
18.13. Bundle Package Imports
Your bundle’s manifest will need to import, at a minimum,
javax.persistence
org.hibernate.proxy
andjavassist.util.proxy
, due to Hibernate’s ability to return proxies for lazy initialization (Javassist enhancement occurs on the entity’sClassLoader
during runtime)JDBC driver package (example:
org.h2
)org.osgi.framework
, necessary to discover theEntityManagerFactory
(described below)
18.14. Obtaining an EntityMangerFactory
hibernate-osgi
registers an OSGi service, using the JPA PersistenceProvider
interface name, that bootstraps and creates an EntityManagerFactory
specific for OSGi environments.
It is VITAL that your EntityManagerFactory be obtained through the service, rather than creating it manually. The service handles the OSGi ClassLoader , discovered extension points, scanning, etc. Manually creating an EntityManagerFactory is guaranteed to NOT work during runtime! |
|
---|---|
Example 381. Discover/Use EntityManagerFactory
public class HibernateUtil {
private EntityManagerFactory emf;
public EntityManager getEntityManager() {
return getEntityManagerFactory().createEntityManager();
}
private EntityManagerFactory getEntityManagerFactory() {
if ( emf == null ) {
Bundle thisBundle = FrameworkUtil.getBundle( HibernateUtil.class );
BundleContext context = thisBundle.getBundleContext();
ServiceReference serviceReference = context.getServiceReference( PersistenceProvider.class.getName() );
PersistenceProvider persistenceProvider = ( PersistenceProvider ) context.getService( serviceReference );
emf = persistenceProvider.createEntityManagerFactory( "YourPersistenceUnitName", null );
}
return emf;
}
}
18.15. Unmanaged Native
Native Hibernate use is also supported. The client bundle is responsible for managing the SessionFactory
and Session
s.
18.16. Bundle Package Imports
Your bundle’s manifest will need to import, at a minimum,
javax.persistence
org.hibernate.proxy
andjavassist.util.proxy
, due to Hibernate’s ability to return proxies for lazy initialization (Javassist enhancement occurs on the entity’sClassLoader
during runtime)JDBC driver package (example:
org.h2
)org.osgi.framework
, necessary to discover theSessionFactory
(described below)org.hibernate.*
packages, as necessary (ex: cfg, criterion, service, etc.)
18.17. Obtaining an SessionFactory
hibernate-osgi
registers an OSGi service, using the SessionFactory
interface name, that bootstraps and creates an SessionFactory
specific for OSGi environments.
It is VITAL that your SessionFactory be obtained through the service, rather than creating it manually. The service handles the OSGi ClassLoader , discovered extension points, scanning, etc. Manually creating an SessionFactory is guaranteed to NOT work during runtime! |
|
---|---|
Example 382. Discover/Use SessionFactory
public class HibernateUtil {
private SessionFactory sf;
public Session getSession() {
return getSessionFactory().openSession();
}
private SessionFactory getSessionFactory() {
if ( sf == null ) {
Bundle thisBundle = FrameworkUtil.getBundle( HibernateUtil.class );
BundleContext context = thisBundle.getBundleContext();
ServiceReference sr = context.getServiceReference( SessionFactory.class.getName() );
sf = ( SessionFactory ) context.getService( sr );
}
return sf;
}
}
18.18. Optional Modules
The unmanaged-native demo project displays the use of optional Hibernate modules. Each module adds additional dependency bundles that must first be activated, either manually or through an additional feature. As of ORM 4.2, Envers is fully supported. Support for C3P0, Proxool, EhCache, and Infinispan were added in 4.3, however none of their 3rd party libraries currently work in OSGi (lots of ClassLoader
problems, etc.). We’re tracking the issues in JIRA.
18.19. Extension Points
Multiple contracts exist to allow applications to integrate with and extend Hibernate capabilities. Most apps utilize JDK services to provide their implementations. hibernate-osgi
supports the same extensions through OSGi services. Implement and register them in any of the three configurations. hibernate-osgi
will discover and integrate them during EntityManagerFactory
/SessionFactory
bootstrapping. Supported extension points are as follows. The specified interface should be used during service registration.
org.hibernate.integrator.spi.Integrator
(as of 4.2)
org.hibernate.boot.registry.selector.StrategyRegistrationProvider
(as of 4.3)
org.hibernate.boot.model.TypeContributor
(as of 4.3)
JTA’s
javax.transaction.TransactionManager
andjavax.transaction.UserTransaction
(as of 4.2), however these are typically provided by the OSGi container.
The easiest way to register extension point implementations is through a blueprint.xml
file. Add OSGI-INF/blueprint/blueprint.xml
to your classpath. Envers' blueprint is a great example:
Example 383. Example extension point registrations in blueprint.xml
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ License: GNU Lesser General Public License (LGPL), version 2.1 or later.
~ See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
-->
<blueprint default-activation="eager"
xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<bean id="integrator" class="org.hibernate.envers.boot.internal.EnversIntegrator"/>
<service ref="integrator" interface="org.hibernate.integrator.spi.Integrator"/>
<bean id="typeContributor"
class="org.hibernate.envers.boot.internal.TypeContributorImpl"/>
<service ref="typeContributor" interface="org.hibernate.boot.model.TypeContributor"/>
</blueprint>
Extension points can also be registered programmatically with BundleContext#registerService
, typically within your BundleActivator#start
.
18.20. Caveats
Technically, multiple persistence units are supported by Enterprise OSGi JPA and unmanaged Hibernate JPA use. However, we cannot currently support this in OSGi. In Hibernate 4, only one instance of the OSGi-specific
ClassLoader
is used per Hibernate bundle, mainly due to heavy use of static TCCL utilities. We hope to support one OSGiClassLoader
per persistence unit in Hibernate 5.Scanning is supported to find non-explicitly listed entities and mappings. However, they MUST be in the same bundle as your persistence unit (fairly typical anyway). Our OSGi
ClassLoader
only considers the "requesting bundle" (hence the requirement on using services to createEntityManagerFactory
/SessionFactory
), rather than attempting to scan all available bundles. This is primarily for versioning considerations, collision protections, etc.Some containers (ex: Aries) always return true for
PersistenceUnitInfo#excludeUnlistedClasses
, even if yourpersistence.xml
explicitly hasexclude-unlisted-classes
set tofalse
. They claim it’s to protect JPA providers from having to implement scanning ("we handle it for you"), even though we still want to support it in many cases. The work around is to sethibernate.archive.autodetection
to, for example,hbm,class
. This tells hibernate to ignore theexcludeUnlistedClasses
value and scan for*.hbm.xml
and entities regardless.Scanning does not currently support annotated packages on
package-info.java
.Currently, Hibernate OSGi is primarily tested using Apache Karaf and Apache Aries JPA. Additional testing is needed with Equinox, Gemini, and other container providers.
Hibernate ORM has many dependencies that do not currently provide OSGi manifests. The QuickStart tutorials make heavy use of 3rd party bundles (SpringSource, ServiceMix) or the
wrap:…
operator.