Unfortunately, at the moment Hibernate does not have the necessary integration mechanisms for working in the OSGi environment, although progress in this direction is noticeable (the initial OSGi-fikatsiya by separating packages in the 4th branch). This encourages the development of their own mechanisms, which requires considerable additional effort.
This article is intended for those developers who are interested: how to use Hibernate with a bunch of Spring + OSGi; What is the Extender pattern; how to implement a ClassLoader with specific behavior; how Hibernate is supported in the Spring Framework and a little bit about extending this code. Of course, to read the article, it is necessary to understand the technologies Spring, Hibernate, OSGi, and also to understand the main problems of code execution in a multithreaded environment. Those unfamiliar with using the Spring Framework in an OSGi environment can refer to the introductory article
“Using Spring in an OSGi Container” .
All submitted code is part of the educational project, the link to which is located at the end of the article. Therefore, please consider all the examples presented as a prototype rather than as ready-to-use fragments.
Pattern Extender
')
Software running in the OSGi-container consists of separate modules (bundle), each of which performs its specific duties. What to do when one module must use any complex runtime mechanisms located in another module? The answer is obvious - use OSGi services. And what to do when these mechanisms require, for example, initialization (registration of a servlet in a servlet-container or, as in our case, registration of mapped classes of Hibernate-entities). The answer to this question is less obvious. In fact, there are only two main options: the first - each module that needs such initialization calls it independently (for example, from the BundleActivator); the second is that a module that needs such initialization has some characteristic metadata on which another module can initiate the execution of all the necessary work, since OSGi has a well-developed event system. The second option has an undoubted advantage - we do not distribute a bunch of the same code across the modules and do not burden them with additional work, but transfer this responsibility into a separate module. Actually by this principle works Spring DM. This principle of operation is the Extender pattern [http://www.aqute.biz/Snippets/Extender] and it is very common in the OSGi world.

Description of the overall picture of the work of Hibernate-Extender
We proceed to discuss the general scheme of work Hibernate-Extender. First of all, it is necessary to choose metadata due to which our extender will distinguish modules that need to be expanded from non-extensions. Let this be the Entity-Classes header in META-INF / MANIFEST.MF containing a list of classes of Hibernate-entities (fans of xml-mapping will remain out of work for now). Next, you need to receive events from modules that go to the Active state and from modules that go out of this state. To do this, use the OSGi event mechanism and implement the SynchronousBundleListener interface. We use a synchronous event handler from the modules in order to immediately respond to changes in the states of the modules before calling the asynchronous BundleListener. At the same time, we must ensure the shortest possible event handling time, so as not to block the thread that caused our handler. This is achieved by implementing the Producer-Consumer pattern (Producer-Consumer), where Producer is our event handler, which sends information about the modules to Consumer, which performs all long-running operations in a separate thread. In our case, it reinitializes the runtime structure of Hibernate.
The Extender mechanism is fairly generic, so the implementation presented below follows Dependency Inversion Principle (SOLID) to facilitate code reuse.
public class Extender implements SynchronousBundleListener { private BundleContext bundleContext; private Executor executor; private BlockingQueue<ExtendedBundle> bundles = new LinkedBlockingQueue<ExtendedBundle>(); private BundleConsumer bundleConsumer; private ExtendedBundleFactory extendedBundleFactory;
The Extender implementation involves: BundleContext in which the Extender itself is registered as a listener for events from the modules; Executor implementation of the artist from the java.util.concurrent framework, in which the consumer performs long-running operations; BlockingQueue The blocking queue with which the Producer-Consumer execution threads are synchronized; BundleConsumer is a concrete implementation of the event consumer of the modules; ExtendedBundleFactory factory "extended" modules.
The public void initialize () method is called immediately after the object is constructed. In this method, we register our Extender as a handler, start the runtime of the consumer and process the modules that are already in the OSGi container, in the event that the Extender starts after the extensible modules.
BundleConsumer
BundleConsumer is an interface, an object with a type that implements this interface must perform processing of modules queued in the Extender queue.
public interface BundleConsumer extends Runnable { void run(); void initialize( BlockingQueue<ExtendedBundle> newBundles ); }
From the code above, it is clear that this is just Runnable with an additional initialization method. All interesting things should happen in the implementation of the void run () method. Let's look at the concrete implementation of this interface, which performs the main work for our Hibernate-Extender.
public class SessionFactoryCreator implements BundleConsumer { private volatile Extendable extendable; private volatile ExtenderClassLoader extenderClassLoader; private volatile BlockingQueue<ExtendedBundle> newBundles;
The main loop in the void run () method handles the newBundles queue. If there are no extensible modules in the queue, execution is blocked in the call to the newBundles.take () method. When expandable modules appear, they move one by one. On each of these modules, the isValidForExtending () method is called, which should return a true value if the module is suitable for extension. This verification method can take considerable time when accessing internal packet resources (for example, to trigger I / O operations), so it is called here, and not in the Extender. If the module is suitable for expansion, then meta information is checked about the action that needs to be performed on it by Actions.ADDING or Actions.REMOVING. Depending on the required action, an add or delete operation is performed and the modules are marked as processed.
I want to draw the reader’s attention to the conditions for exiting the main processing loop! Thread.currentThread (). IsInterrupted (); it ensures the correct termination of the thread when the interrupt flag is set. Another place where this flag is involved is the handling of the exception thrown by the blocking method newBundles.take () when it occurs, setting the stream interrupt flag and exiting the main loop.
Class attributes are marked volatile to guarantee visibility in a multi-threaded environment. The creation of an object occurs in one stream, and further use in another. The implementation of BlockingQueue is thread-safe, so its use in a multi-threaded environment does not require additional synchronization.
HibernateSessionFactoryBean
A large variety of frameworks are not accidental. Using a third-party, well-tested, ready-for-use code greatly facilitates development. Therefore, we also try not to invent too much. The Spring Framework supports integration with Hibernate. One of the main classes of this integration is LocalSessionFactoryBean. It initializes and creates a session factory, which is then used in any code using the Hibernate API. This class has a descendant of AnnotationSessionFactoryBean, which adds some functionality with annotation support. From the last we will inherit our implementation, the code of which is presented below.
public class HibernateSessionFactoryBean extends AnnotationSessionFactoryBean { private SessionFactory sessionFactoryProxy; private Lock sessionFactoryAccessLock = new ReentrantLock(true); private ClassLoader classLoader; private Set<Class<?>> annotatedClasses = new CopyOnWriteArraySet<Class<?>>(); public void setClassLoader(ClassLoader classLoader) { super.setBeanClassLoader(classLoader); this.classLoader = classLoader; } @Override public void setBeanClassLoader(ClassLoader beanClassLoader) { } @Override public void afterPropertiesSet() throws Exception {
The main execution plan for this code is as follows. AbstractSessionFactoryBean implements the FactoryBean interface; the creation of a class object that implements this interface in a Spring container is handled in a special way. See the Spring Framework documentation for more details. It is worth mentioning here that it is just a factory of SessionFactory objects, and not its concrete implementation. Instead of the SessionFactory object created in LocalSessionFactoryBean, the code returns a proxy object that redirects almost all calls to the native SessionFactory methods while protecting any access to it by global blocking (this architecture can be reworked from the viewpoint of performance, in addition, the possibility of re-creating SessionFactory at the time of using Session objects is not taken into account ). Each time the set of extensible modules changes, the SessionFactory object is re-created taking this into account.
The callback handler for the object's proxy method uses the Thread Context Classloader (TCCL) override. As a TCCL, at the time of the method call, our own Classloader sets about this later.
OSGi and ClassLoader
In a typical application running in the jvm environment, class loaders are hierarchically related (link to parent) and load classes and resources first by delegating to their parent and, if unsuccessful, by themselves. For OSGi environments, this behavior is not appropriate. Similarly, it is not suitable for our case with Extender. In the OSGi container, each module receives its own class loader which fully satisfies its (module) needs. And the module class loaders are connected to a more complex delegation network. Each such loader has a link to the framework class loader, as well as to the module loaders from which the import is performed. This delegation network is built as a result of the Resolving process in the OSGi container.

In the case of Extender, the generic module class loader is not suitable, because there is no possibility of declaring imported classes of entities in MANIFEST.MF. About such classes is simply unknown at the time of development Extender. Therefore, you need to implement your own bootloader with the behavior we need. The class loader code for the Extender is shown below.
public class ExtenderClassLoader extends ClassLoader { private volatile ClassLoader defaultClassLoader = null; private Set<ExtendedBundle> bundles = new CopyOnWriteArraySet<ExtendedBundle>(); public ClassLoader getDefaultClassLoader() { return defaultClassLoader; } public void setDefaultClassLoader( ClassLoader defaultClassLoader) { this.defaultClassLoader = defaultClassLoader; } public void addBundle( ExtendedBundle bundle) { bundles.add(bundle); } public void removeBundle(ExtendedBundle bundle) { bundles.remove(bundle); } @Override public Class<?> loadClass( String name) throws ClassNotFoundException { Class<?> c = null; for (ExtendedBundle bundle: bundles) { try { c = bundle.loadClass(name); break; } catch (ClassNotFoundException e) {} } if (c == null && getDefaultClassLoader() != null) { try { c = getDefaultClassLoader().loadClass(name); } catch (ClassNotFoundException e) {} } if (c == null) throw new ClassNotFoundException(name); return c; }
Let's take a closer look at the presented code. The main logic of his work is in the loadClass method. It should be emphasized that the methods for loading resources are omitted in the given fragment in a similar way. ExtenderClassLoader contains a collection of modules containing entity classes and delegates the loading of classes and resources to them. This provides access to the necessary code without the need for import. Loading the remaining required classes ExtenderClassLoader delegates to the default boot loader. Thread safety of this code is ensured by using the non-blocking implementation of the CopyOnWriteArraySet set and thread safe class loaders.
Putting it all together
At this point, the reader should have an understanding of how the main elements of the Hibernate-Extender work. Now you need to put all the pieces together. To do this, you need to configure the module to work in the OSGi environment with spring dm. For a better understanding of the configuration of objects, consider the class diagram of our system.
According to established canons, the spring dm module configuration is split into at least two files. The first is the actual configuration of the spring-container. The second is a configuration specific to the OSGi-environment. In our case, these are the context.xml and osgi-context.xml files.
The Hibernate-Extender configuration for spring dm is identical to the class diagram. At the beginning of the context.xml configuration file, a dataSource object is described - this is the data source with which all interaction with the database is performed. Next comes the configuration of the transactionManager object that provides transaction support in a Spring environment. The sessionFactory object is an instance of the HibernateSessionFactoryBean class described above that is responsible for creating the SessionFactory. The sessionFactory link is passed to the transactionManager. The next object in the configuration is the extenderClassLoader in which, when created, the default class loader is set as the defaultClassLoader in the Spring environment. The sessionFactoryCreator object does all the basic work of initiating the reconfiguration of the Hibernate environment, taking into account the changed data on the modules. The central object of our module is the Extender which, in the process of creation, receives links to the objects: bundleContext, new unnamed Executor, new unnamed ExtendedBundleFactory and sessionFactoryCreator. In the second configuration file osgi-context.xml, OSGi services are declared: dataSource, transactionManager, sessionFactory.
context.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:osgi="http://www.springframework.org/schema/osgi" xmlns:osgi-compendium="http://www.springframework.org/schema/osgi-compendium" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" scope="singleton"> <property name="driverClass" value="org.hsqldb.jdbc.JDBCDriver" /> <property name="jdbcUrl" value="jdbc:hsqldb:hsql://localhost/test" /> <property name="user" value="SA" /> <property name="password" value="" /> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager" scope="singleton"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="sessionFactory" class="iconcerto.hibernate.HibernateSessionFactoryBean" scope="singleton"> <property name="dataSource" ref="dataSource" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> <property name="classLoader" ref="extenderClassLoader" /> </bean> <bean id="extenderClassLoader" class="iconcerto.extender.ExtenderClassLoader" scope="singleton"> <property name="defaultClassLoader"> <bean class="org.springframework.util.ClassUtils" factory-method="getDefaultClassLoader"/> </property> </bean> <bean id="sessionFactoryCreator" class="iconcerto.hibernate.extender.SessionFactoryCreator" scope="singleton"> <property name="extenderClassLoader" ref="extenderClassLoader" /> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="extender" class="iconcerto.extender.Extender" scope="singleton" init-method="initialize"> <property name="bundleContext" ref="bundleContext" /> <property name="executor"> <bean class="java.util.concurrent.Executors" factory-method="newSingleThreadExecutor" /> </property> <property name="extendedBundleFactory"> <bean class="iconcerto.hibernate.extender.HibernateBundleFactory"/> </property> <property name="bundleConsumer" ref="sessionFactoryCreator" /> </bean> </beans>
osgi-context.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:osgi="http://www.springframework.org/schema/osgi" xmlns:osgi-compendium="http://www.springframework.org/schema/osgi-compendium" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi-1.2.xsd http://www.springframework.org/schema/osgi-compendium http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium-1.2.xsd"> <osgi:service ref="dataSource" interface="javax.sql.DataSource" /> <osgi:service ref="transactionManager"> <osgi:interfaces> <value> org.springframework.transaction.support.ResourceTransactionManager </value> </osgi:interfaces> </osgi:service> <osgi:service ref="sessionFactory" interface="org.hibernate.SessionFactory" /> </beans>
Prototype flaws
Like any prototype, Hibernate-Extender has a number of flaws. On the one hand, it is difficult to convey the main idea if it is mixed with a large number of minor details. On the other hand, it is impossible not to mention quite obvious flaws that prevent the use of this code in a real application.
From the spring configuration, the first thing that catches your eye is the presence of setting the properties of the data source directly in the context.xml file itself. Any real application requires changing this. For example, setting this data from an external property file using the Spring property-placeholder mechanism or implementing a more complex system for changing the connection parameters to the database at run time.
In the above listings, the code responsible for logging is omitted, without which virtually any use of the module is unthinkable.
The described implementation has the following serious disadvantage. What happens if a module is added to an extension at the moment when another, already expanded module, performs an operation using the Hibernate API and is inside a transaction? Such an operation in the current implementation is doomed to failure, although if it is taken into account in the code, then the handling of specific exceptions associated with invoking operations on the already out of date Session object will cause inconvenience.
And the last drawback that I want to highlight is the problem from the point of view of performance arising from the reconfiguration of the Hibernate runtime environment that occurs after each change in the set of extensible modules.
OSGi-fikatsiya third-party libraries
Many java libraries are already delivered as ready-to-run in OSGi, but unfortunately, not all. There are two ways out of this situation, either use OSG-based versions prepared by a third party (for example, the SpringSource Enterprise Bundle Repository [http://ebr.springsource.com/repository/app/]), or prepare such versions of the libraries yourself. In the project with the source code of the examples, such preparation is performed (lib subproject). I would like to note that for a complex library this is not a trivial task. You must understand the library structure for exporting packages that can be used by third-party code. Additional problems may arise due to the internal mechanisms of libraries that are poorly compatible or incompatible with the OSGi environment. But all this, in most cases, solvable problems. The technical problems of the library architecture are solved only manually, while the export question can be automated with the help of various utilities (bnd, maven-plugins, etc.)
Conclusion
This completes the description of the Extender approach to working with Hibernate in OSGi. The only thing left is to add how to use the resulting module. The extensible module in the Entity-Classes header in META-INF / MANIFEST.MF should place a list of the full names of the Hibernate entity classes. Further, when this module is launched, the Extender will load these classes using our implementation of the ClassLoader and reinitialize the Hibernate runtime environment. In addition, the Hibernate API that needs to use it must import the OSGi service SessionFactory and ResourceTransactionManager. After that, the module can use the Hibernate API in Spring environment in the usual manner (according to the official manual).
The full Extender code together with modular and integration tests is placed in the googlecode repository link to which is located at the end of the article. This code will be developed, so everyone who is interested in it can follow the changes. Whoever wants to participate in the development of this code can contact me. Well, as usual, who has any questions, welcome to the comments!
Additional sources
code.google.com/p/iconcerto/source/browsewww.aqute.biz/Bnd/Bndwww.aqute.biz/Snippets/Extenderstatic.springsource.org/spring/docs/3.1.x/spring-framework-reference/htmldocs.jboss.org/hibernate/core/3.6/reference/en-US/htmlstatic.springsource.org/osgi/docs/1.2.1/reference/html