What is wrong with the current design?
The main problem with jpa.container is that it is too complicated. Especially the Quiesce support and wrapping of DataSources make it complex and by this fragile.
This module takes care of providing a managed EM (EntityManager).
The basic problem with the EM in general is that in JEE each EJB instance runs in its own thread. So the specs defines that an EntityManager is injected into the EJB class directly. In OSGi as well as in plain Java the problem is that one instance of aclass using the EM is typically used by many threads. The EntityManager is not thread safe though. To work around this jpa.container creates an EntityManager proxy that internally makes sure each thread has its own instance.
Parses the blueprint xml elements for jpa:context and jpa:unit and create corresponding EMF or EM for injection.
Parse the blueprint elements for tx:transaction and create an interceptor for the blueprint beans if necessary.
Support for non blueprint like declarative services?
The current solution is quite specific for aries blueprint and can not be used easily in other frameworks like declarative services.
Aries JPA 2.0
jpa.container manages EntityManagerFactory service
The jpa.container module tracks bundles and creates one EntityManagerFactory for each persistence unit it finds. It acts a bit different from the old aries jpa container though:
It uses a hierarchy of PersistenceBundleTracker -> PeristenceProviderTracker -> DataSourceTracker. The EntityManagerFactory service is only created when all are present. If one of persistence bundle, PersitenceProvider or the used DataSource goes away then the EMF service is unregistered. So it makes sure all is nicely cleaned up.
The DataSource is looked up as a service and given to the PersistenceProvider as a object. So there is no jndi involved. Creating datasources from persistence properties is supported thorugh DataSourceFactory services. The XA wrapping is done by pax-jdbc-pool the new jpa.container does not wrap XA resources itself.
The module creates no proxies. The EntityManagerFactory is published as is. So it is much simpler.
EM supplier service
The basic idea for the new design was to have an indirection in the EM usage. So the EM is used through an interface EmSupplier like emSupplier.get().persist(myEntity). The advantage is that inside the get() you can easily hold one EM per thread without using a proxy.
This can then be used in the blueprint handler as well as in other frameworks.
To avoid on demand creation of the EM which can be confusing the EMSupplier interface also has the methods preCall() and postCall() like the aries Synchronization interface. Before the first usage of get() in a method preCall() has to be called. At the end of the method postCall() has to be called.
PreCall makes sure the EntityManager is created and postCall makes sure it is destroyed. If cascaded calls happen the EM is created / destroyed only at the outermost level.
The blueprint handler reacts on the @PersistenceContext annotation and injects injects either an EmSupplier or an EntityManger depending on the field type.
The handler also takes care of the transaction management. There I largely reused the aries transaction code but also added a RESOURCE-LOCAL support. In the later case the transaction is managed using the EM transactions.
Currently transactions are handled together with the EM management. For later the idea would be to handle transactions sepearately by reacting on the JTA 1.2 @Transactional annotation.
JPA Templates and closure support
As we can not create a transaction handling proxy for declarative services I created a JpaTemplate class to support a closure based approach for transactions.
The idea is to support this style of marking transactions:
So per persistence unit there will be one JpaTemplate with RESOURCE_LOCAL or JTA support depending on the setting in persistence.xml. The user simply binds the template as a service and can then write his code inside closures. This way the EM is accessed local to the closure and does not need additional measures to be thread safe.
Internally the JpaTemplate uses the EmSupplier of the persistence unit and the JTA TransactonManager as services. So this allows to reuse the EnityManager even between different frameworks. For example you can start your transaction in a DS service and from there also call another service created using blueprint. Still both will transparently use the same transaction if the transaction type is e.g. REQUIRED. If the use the same persistence unit then both will also use the same EM instance and so take advantage of internal caching.
The full code is available in a github repo. The README should allow to run it in karaf 3 within some minutes.
Further enhancements, how to move on
My colleagues at Talend