Reputation: 11
I'm new to OSGi enviroment and maybe I miss something. I can't load my JPA entities contained in a OSGI bundle (ClassNotFoundException).
I'm using Apache Felix and Hibernate. I am not using blueprints.
This is my architecture:
Application A: common interfaces. Not much to say about that.
Application B: is a WebApp. It starts felix framework at the startup that starts the Host bundle. The host Host bundle install all the bundle's jar contained in a specific folder and track a service (the service implemented in Application C). It has Application A as dependency.
Application C: it's the client bundle. It implements the Application A interface and register it (it has App A as dependency). It also contains all the JPA model I need in this bundle.
The problem I have is in the application C implementation: it configures runtime the org.hibernate.cfg.Configuration and load is own JPA model (cfg.addAnnotatedClass(EntityTest.class)). At this point I get this exception:
org.hibernate.AssertionFailure: PersistentClass name cannot be converted into a Class at org.hibernate.cfg.BinderHelper.getPropertyOverriddenByMapperOrMapsId(BinderHelper.java:877) at org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:2179) at org.hibernate.cfg.AnnotationBinder.processIdPropertiesIfNotAlready(AnnotationBinder.java:925) ...
that is caused by
Caused by: org.hibernate.annotations.common.reflection.ClassLoadingException: Unable to load Class [com.osgi.bundle1.model.EntityTest] at org.hibernate.annotations.common.util.StandardClassLoaderDelegateImpl.classForName(StandardClassLoaderDelegateImpl.java:60) at org.hibernate.boot.internal.MetadataBuilderImpl$MetadataBuildingOptionsImpl$4.classForName(MetadataBuilderImpl.java:758) at org.hibernate.annotations.common.reflection.java.JavaReflectionManager.classForName(JavaReflectionManager.java:144) at org.hibernate.cfg.BinderHelper.getPropertyOverriddenByMapperOrMapsId(BinderHelper.java:874) ... 58 common frames omitted Caused by: java.lang.ClassNotFoundException: com.osgi.bundle1.model.EntityTest at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1333) at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1167) at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:348) at org.hibernate.annotations.common.util.StandardClassLoaderDelegateImpl.classForName(StandardClassLoaderDelegateImpl.java:57) ... 61 common frames omitted
The Application C already exports the model, I've used https://docs.gradle.org/current/userguide/osgi_plugin.html.
EDIT: added more details.
Application B is a webapp built with Jersey. It has a ServletContextListener that starts felix:
@WebListener
public class Felixbootstrap implements ServletContextListener{
@Override
public void contextInitialized(ServletContextEvent sce) {
OsgiHost.initialize();
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
public class OsgiHost {
public static void initialize() {
OsgiHost host = new OsgiHost();
host.start();
}
private void start() {
Map<String, Object> config = new HashMap<>();
config.put(FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP, Arrays.asList(
, new HostBundle()//Activator that starts bundles of the project
));
config.put(FelixConstants.FRAMEWORK_STORAGE_CLEAN, Constants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT);
//package to export
String felixPackageExportConfiguration =
//common api
"com.osgi.test.api"
+ ",com.osgi.test.dto"
//hibernate
+ ",javax.persistence;version=2.1.0"
+ ",org.hibernate"
+ ",org.hibernate.cfg"
+ ",javassist.util.proxy"
+ ",org.hibernate.proxy"
+ ",org.hibernate.query"
;
config.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, felixPackageExportConfiguration);
Felix felix = new Felix(config);
try {
felix.start();
} catch (BundleException e) {
e.printStackTrace();
}
}
}
Application C has a method called by a end point on Application B (through the common interface). When it's called this class is initialized (always in application C):
public class HibernateUtil {
private SessionFactory sf;
private static HibernateUtil instance = null;
public static HibernateUtil getInstance() {
if (instance == null) {
instance = new HibernateUtil();
}
return instance;
}
private HibernateUtil() {
}
public Session getSession() {
return getSessionFactory().openSession();
}
private SessionFactory getSessionFactory() {
if (sf == null) {
Configuration cfg = new Configuration();
cfg.addAnnotatedClass(EntityTest.class);
cfg.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQL94Dialect")
.setProperty("hibernate.connection.datasource", "java:comp/env/jdbc/jdbcname")
.setProperty("hibernate.ejb.naming_strategy", "org.hibernate.cfg.DefaultNamingStrategy")
.setProperty("hibernate.archive.autodetection", "class")
.setProperty("hibernate.hbm2ddl.auto", "update")
;
sf = cfg.buildSessionFactory();
}
return sf;
}
}
Upvotes: 1
Views: 768
Reputation: 156
Tim is likely spot-on. Hibernate ORM must be bootstrapped in one of three supported ways. Our docs walk through all of those, and it looks like you'd likely be most interested in "Unmanaged Native" mode. In a nutshell, unless you're using something like Apache Aries to discover your persistence unit and bootstrap things for you, you instead need to manually do so.
https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/chapters/osgi/OSGi.html
Upvotes: 1
Reputation: 1199
From the code snippet:
//package to export
String felixPackageExportConfiguration =
//common api
"com.osgi.test.api"
+ ",com.osgi.test.dto"
//hibernate
+ ",javax.persistence;version=2.1.0"
+ ",org.hibernate"
+ ",org.hibernate.cfg"
+ ",javassist.util.proxy"
+ ",org.hibernate.proxy"
+ ",org.hibernate.query"
;
config.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, felixPackageExportConfiguration);
It looks very much as though Hibernate is outside your OSGi framework. Whilst the naming scheme you use isn't clear, it sounds as though your JPA entities are inside the OSGi framework in an OSGi bundle.
The way that you're bootstrapping Hibernate is then using an API designed for use in a flat Java SE class path. There is no reason to expect that Hibernate will be able to find your entity types because the ClassLoader that Hibernate has access to has no visibility into the OSGi framework.
Moving back to a higher level - what is it that you are trying to achieve by having the OSGi framework? If you want to use OSGi to do data access then why isn't Hibernate running inside the OSGi framework where it has a chance of being able to see the entity classes?
There is a standard for using JPA in an OSGi framework that you could use, but I sense that there's a lot of other things going on in this application that you haven't told us yet - for example there's no code showing how requests get into the OSGi framework, nor is there any code showing how the framework is stopped...
Upvotes: 1
Reputation: 23948
This doesn't look like an OSGi question at all. The stack trace indicates that Hibernate is trying to load the model class from a Web Application class loader, i.e. a WAR file deployed on Tomcat (Apache Catalina is the part of Tomcat that implements the Servlet specification).
Does this WAR file have the relevant class in either its WEB-INF/classes
directory or in one of the JAR files under WEB-INF/lib
?
Upvotes: 1