OSGi fragment bundle with hibernate with runtime pojos

My requirement is to map different databases (specifically SQL Server, MySQl and Postgres) using sleep mode; from the db record create an xml file.

For hibernation, I create hbm and pojos files at runtime using JAssist. My code works fine, for further modulation I implement fragments for each database. So my host package will handle the creation of the execution class and add them to the class creation logic, hbm file and BL. a fragment calls it by passing parameters.

when I create a fragment package for each database, the runtime pojo class created in my host node appears in my fragment, I checked it with "Thread.currentThread (). getContextClassLoader (). loadClass ()" and can create its own instance ,

The problem is when I call the Hibernate functions from the fragment package, I get "Entity not mapping", AFAIK. This exception occurs when hibernate cannot find a mapping class to a table. Therefore, I think Hibernate does not find my pojo classes for the runtime. which he can find in the host.

Host: Runtime Pojo Creation, Logic of creating and updating HBM and CFG BL

Snippet: Hibernation layer, Hibernate function call, XML creation logic

+3
source share
3 answers

This problem always appears if you use Hibernate for more than one package. In the Hibernate configuration, you cannot determine in which Bundle you can find the mapping files and pojo class files. Hibernate does not use the mechanisms that OSGI provides for this. As a result, hibernate only detects mapping files and classes that are in the same package as the Hibernate library.

I do not know if there is any professional solution (third-party product) for this problem.

There are two ways to solve this problem:

  • Forget about your fragment packages and put all the Hibernate libraries, mapping files, pojos, classes using Hibernate / HQL for all databases in one set. You can switch between different databases using different hibernate.cfg.xml files; each database has its own configuration file. These hibernate.cfg.xml files may be located outside the packages.

  • Write your own configuration class that extends org.hibernate.cfg.Configuration. In this class you must

    • write your own classloader that finds pojo classes even in other packages
    • override addResource (String resourceName, ClassLoader classLoader) so that it finds resources also in other packages
    • override doConfigure and buildSessionFactory so that they use the class loader instead of the standard class loader (use Thread.setContextClassLoader and call the method from the superclass, i.e. from the standard Hibernate configuration class).
    • override all other methods that return a Configuration instance so that they return an instance of your Configuration class, not the Hibernate Configuration class.

We made decision 2. It was a little work, but now it works well. (Thought changing a version of Hibernate will require a little work again.)

+3
source

look at org.hibernate.internal.util.ClassLoaderHelper.

All you have to do is replace ClassLoader with ClassLoader, which is able to resolve your entity classes. Hibernate-Osgi also installs it in the OSGI ClassLoader (see Org.hibernate.osgi.HibernateBundleActivator). Therefore, suggest the following:

BundleWideClassLoader cl = new BundleWideClassLoader(); if (ClassLoaderHelper.overridenClassLoader != null && ClassLoaderHelper.overridenClassLoader instanceof OsgiClassLoader) { OsgiClassLoader ocl = (OsgiClassLoader)ClassLoaderHelper.overridenClassLoader; for (Bundle b : cl.getBundles()) { ocl.addBundle(b); } } else { ClassLoaderHelper.overridenClassLoader = new BundleWideClassLoader(); } 

I put this in my HibernateConfiguration class, overriding the buildSessionFactory, which executes this procedure and returns super.buildSessionFactory.

BundleWideClassLoader is as follows:

 public class BundleWideClassLoader extends ClassLoader { @Override protected Class<?> findClass(String name) throws ClassNotFoundException { for (BundleClassLoader cl : this.getAllClassLoader()) { try { Class clazz = cl.findClass(name); return clazz; } catch (Exception ex) { } } throw new ClassNotFoundException(name); } @Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { Class clazz = this.findClass(name); if (resolve) { this.resolveClass(clazz); } return clazz; } @Override public URL findResource(String name) { for (BundleClassLoader cl : this.getAllClassLoader()) { URL ret = cl.findResource(name); if (ret != null) { return ret; } } return null; } /** * Returns a list of all available BundleClassLoader. * * @return classloader */ public HashSet<BundleClassLoader> getAllClassLoader() { // // Do some magic here to get your ClassLoaders from all of your Bundles // } /** * Returns a list of all bundles which are registered in this BundleWideClassLoader. * * @return list of managed bundles */ public HashSet<Bundle> getBundles() { HashSet<Bundle> bundles = new HashSet<>(); for (BundleClassLoader cl : this.getAllClassLoader()) { bundles.add(cl.getBundleContext().getBundle()); } return bundles; } } 

And finally, BundleClassLoader:

 public class BundleClassLoader extends ClassLoader { /** * Bundle context. */ private BundleContext context; /** * Constructor. * @param ctx Bundle Context */ public BundleClassLoader(BundleContext ctx) { this.context = ctx; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { return this.context.getBundle().loadClass(name); } @Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { Class clazz = this.findClass(name); if (resolve) { this.resolveClass(clazz); } return clazz; } @Override public URL findResource(String name) { return this.context.getBundle().getResource(name); } /** * Returns bundle context. * @return bundle context */ public BundleContext getBundleContext() { return this.context; } } 

I suggest creating a new BundleClassLoader in each of your BundleActivators and adding it to some registry so that the BundleWideClassLoader can get a list of BundleClassLoader from there. Remember to remove the BundleClassLoader when the package is stopped or removed.

+1
source

In sleep mode, OSGi currently has several caveats, one of which requires a single client package with persistence. For various reasons, we must use the "requestingBundle" when we create the ClassLoader, which is responsible for handling objects, mappings, and persistence resources.

Take a look: https://github.com/hibernate/hibernate-orm/blob/master/hibernate-osgi/src/main/java/org/hibernate/osgi/OsgiClassLoader.java

OsgiPersistenceProviderService and OsgiSessionFactoryService add a "requestingBundle" to the ClassLoader when the service is called. As Johanna suggested, there is no really decent way to find out which Bundles make up the persistence block other than the Bundle request or the location of the persistence.xml file itself.

However, I would like to hear ideas on how best to support such an installation. I initially played with additional metadata in the manifest to denote "I am part of the persistence unit x," but I really did not have time to think it through.

Definitely do not use the solution recommended above using ClassLoaderHelper. This is a completely temporary hack to be removed in ORM 5. This is purely due to the static nature of ORM 4.

+1
source

Source: https://habr.com/ru/post/1444841/


All Articles