Marco
Marco

Reputation: 11

OSGI Classloader can't see JPA entities in the bundle

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:

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

Answers (3)

Brett Meyer
Brett Meyer

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

Tim Ward
Tim Ward

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

Neil Bartlett
Neil Bartlett

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

Related Questions