【问题标题】:OSGI Classloader can't see JPA entities in the bundleOSGI Classloader 看不到包中的 JPA 实体
【发布时间】:2018-05-31 14:51:24
【问题描述】:

我是 OSGi 环境的新手,也许我错过了一些东西。我无法加载包含在 OSGI 包中的 JPA 实体 (ClassNotFoundException)。

我正在使用 Apache Felix 和 Hibernate。我没有使用蓝图。

这是我的架构:

  • 应用 A:通用接口。没什么好说的。

  • 应用程序 B:是一个 WebApp。它在启动主机包的启动时启动 felix 框架。主机主机包安装包含在特定文件夹中的所有包的 jar 并跟踪服务(在应用程序 C 中实现的服务)。它具有应用程序 A 作为依赖项。

  • 应用程序 C:它是客户端捆绑包。它实现 Application A 接口并注册它(它具有 App A 作为依赖项)。它还包含我在这个包中需要的所有 JPA 模型。

我遇到的问题在于应用程序 C 实现:它配置运行时 org.hibernate.cfg.Configuration 并加载自己的 JPA 模型(cfg.addAnnotatedClass(EntityTest.class))。 此时我得到了这个异常:

org.hibernate.AssertionFailure: PersistentClass 名称不能 转换成一个类 在 org.hibernate.cfg.BinderHelper.getPropertyOverriddenByMapperOrMapsId(BinderHelper.java:877) 在 org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:2179) 在 org.hibernate.cfg.AnnotationBinder.processIdPropertiesIfNotAlready(AnnotationBinder.java:925) ...

那是因为

原因:org.hibernate.annotations.common.reflection.ClassLoadingException:无法加载类 [com.osgi.bundle1.model.EntityTest] 在 org.hibernate.annotations.common.util.StandardClassLoaderDelegateImpl.classForName(StandardClassLoaderDelegateImpl.java:60) 在 org.hibernate.boot.internal.MetadataBuilderImpl$MetadataBuildingOptionsImpl$4.classForName(MetadataBuilderImpl.java:758) 在 org.hibernate.annotations.common.reflection.java.JavaReflectionManager.classForName(JavaReflectionManager.java:144) 在 org.hibernate.cfg.BinderHelper.getPropertyOverriddenByMapperOrMapsId(BinderHelper.java:874) ...省略了58个常用框架 引起:java.lang.ClassNotFoundException:com.osgi.bundle1.model.EntityTest 在 org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1333) 在 org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1167) 在 java.lang.Class.forName0(本机方法) 在 java.lang.Class.forName(Class.java:348) 在 org.hibernate.annotations.common.util.StandardClassLoaderDelegateImpl.classForName(StandardClassLoaderDelegateImpl.java:57) ...省略了61个常用框架

应用程序 C 已经导出模型,我使用了https://docs.gradle.org/current/userguide/osgi_plugin.html

编辑:添加了更多细节。

应用程序 B 是使用 Jersey 构建的 web 应用程序。它有一个启动 felix 的 ServletContextListener:

@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();
        }
    }
 }

应用程序 C 有一个由应用程序 B 上的端点调用的方法(通过公共接口)。当它被调用时,这个类被初始化(总是在应用程序 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;
    }
}

【问题讨论】:

  • 我有一个小的 working example(在 Karaf 和 OpenJPA 上),但问题看起来非常相似,恕我直言,您应该尝试将实体类作为片段注入 Hibernate 包主机以被识别/找到

标签: hibernate jpa osgi apache-felix


【解决方案1】:

这看起来根本不像一个 OSGi 问题。堆栈跟踪表明 Hibernate 正在尝试从 Web 应用程序类加载器加载模型类,即部署在 Tomcat 上的 WAR 文件(Apache Catalina 是实现 Servlet 规范的 Tomcat 的一部分)。

此 WAR 文件是否在其 WEB-INF/classes 目录或 WEB-INF/lib 下的其中一个 JAR 文件中具有相关类?

【讨论】:

  • 我忘了说应用程序 B 是一个 web 应用程序。当我在我的 Tomcat 中启动它时,它会在启动时启动我的 Felix 框架,启动安装包含 JPA 模型的包(JAR)的主机。
  • 但是没有迹象表明 Hibernate 正在尝试从 OSGi 包加载。它似乎是直接从 Web 应用程序加载的。这无疑是问题所在,但如果没有更多关于您启动 OSGi 的方式以及如何配置 Hibernate 的更多信息,就不可能提供更多帮助。
  • 我添加了一些细节
  • 您的术语中的“应用程序”是什么? Web 应用程序 (WAR)?如果是这种情况,您是否有 Hibernate 和单独的应用程序中的实体类?
【解决方案2】:

来自代码sn-p:

    //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);

Hibernate 看起来非常你的 OSGi 框架之外。虽然您使用的命名方案不清楚,但听起来您的 JPA 实体在 OSGi 包中的 OSGi 框架内。

然后,您引导 Hibernate 的方式是使用专为在平面 Java SE 类路径中使用而设计的 API。没有理由期望 Hibernate 能够找到您的实体类型,因为 Hibernate 可以访问的 ClassLoader 对 OSGi 框架没有可见性。

回到更高的层次——你想通过拥有 OSGi 框架来实现什么?如果您想使用 OSGi 进行数据访问,那么为什么 Hibernate 不在 OSGi 框架内运行,它有机会看到实体类?

您可以使用在 OSGi 框架中使用 JPA 的标准,但我感觉在此应用程序中还有很多其他事情您还没有告诉我们 - 例如,没有代码显示如何请求进入 OSGi 框架,也没有任何代码显示框架如何停止...

【讨论】:

    【解决方案3】:

    蒂姆很可能是正确的。 Hibernate ORM 必须以三种支持的方式之一进行引导。我们的文档介绍了所有这些,看起来您可能对“非托管本机”模式最感兴趣。简而言之,除非您使用 Apache Aries 之类的工具来发现您的持久性单元并为您引导事物,否则您需要手动执行此操作。

    https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/chapters/osgi/OSGi.html

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-12-21
      • 2013-10-29
      • 2013-03-12
      • 2016-09-23
      • 1970-01-01
      • 1970-01-01
      • 2013-02-23
      • 2013-06-30
      相关资源
      最近更新 更多