使用Datanucleus JDO和蓝图在OSGi环境中持久化

时间:2013-09-25 12:36:05

标签: osgi jdo datanucleus apache-karaf blueprint-osgi

我正在尝试在尽可能多的模块化的OSGi环境(运行Felix的Karaf)中进行持久化。我选择JDO作为其附加功能(主要是获取组)而不是JPA。实施是Datanucleus。我使用Maven来构建整个项目。

由于我之前没有任何JDO或OSGi的经验,因此让它们中的任何一个工作都是一个相当大的挑战。我现在能够在Java SE环境中进行JDO持久化(单元测试工作没有问题),我知道如何使用蓝图容器在OSGi环境中提供服务。但我无法让这两件事情协同工作。我正在加载类加载问题。

我甚至无法构建一个能够在Karaf上执行JDO持久化的简单应用程序(我尝试了this tutorial但它使用了Spring DM而我无法重写它以使用OSGi蓝图代替)

我最困惑的是:

  • 我应该将 datanucleus.primaryClassLoader 属性设置为什么值?
  • 将哪个类加载器作为参数传递给 JDOHelper.getPersistenceManagerFactory 方法?
  • 使用 maven-bundle-plugin 明确导入哪些包? (看起来至少 javax.jdo org.datanucleus.api.jdo org.osgi.framework 可能是必需的)
  • 除了对 PersistenceManagerFactory 的引用之外,其他捆绑包还需要什么?

此外:

  • 是否可以将持久性信息与值类分开?如果我理解正确的话,只有使用运行时增强才能实现,如果可行的话,这将是非常复杂的。
  • 是否可以在多个捆绑包中定义相互依赖的持久性功能类?比如在一个包中定义用户,在另一个包中定义他们的地址吗?

我非常感谢一个简单的多包项目的例子,它只使用Datanucleus,JDO API和OSGi蓝图来处理持久性。

谢谢

1 个答案:

答案 0 :(得分:0)

我只能提供一些关于让JDO / datanucleus在Karaf上工作的基本提示。

正如教程中所指出的,您需要扩展LocalPersistenceManagerFactoryBean,同时实现BundleContextAware接口。

这里的关键点是类加载:LocalPersistenceManagerFactoryBean期望所有类都由一个类加载器加载,而在OSGi运行时则不是这样。

为了让它运转起来,你需要:

  1. 明确导入清单文件中的org.datanucleus.api.jdo
  2. datanucleus.primaryClassLoader属性可以设置为您传递给JDOHelper.getPersistenceManagerFactory方法的同一个类加载器。类加载器是org.datanucleus.api.jdo包使用的类(参见下面的示例)
  3. 您需要将datanucleus.plugin.pluginRegistryClassName属性设置为org.datanucleus.plugin.OSGiPluginRegistry
  4. 停止/卸载捆绑包时,您必须刷新javax.jdo捆绑包以避免在重新创建持久性管理器工厂时出现错误(检查主题上的this question
  5. 示例自定义LocalPersistenceManagerFactoryBean:

    public class OSGiLocalPersistenceManagerFactoryBean
        extends LocalPersistenceManagerFactoryBean implements BundleContextAware {
    
        public static final String JDO_BUNDLE_NAME    = "org.datanucleus.api.jdo";
        public static final String JDO_PMF_CLASS_NAME = "org.datanucleus.api.jdo.JDOPersistenceManagerFactory";
    
        private BundleContext bundleContext;
    
        @Override
        protected PersistenceManagerFactory newPersistenceManagerFactory(String name) {
            return JDOHelper.getPersistenceManagerFactory(name, getClassLoader());
        }
    
        @Override
        protected PersistenceManagerFactory newPersistenceManagerFactory(Map props) {
            ClassLoader classLoader = getClassLoader();
    
            props.put("datanucleus.primaryClassLoader", classLoader);
    
            if (FrameworkUtil.getBundle(this.getClass()) != null) { // running in OSGi
                props.put("datanucleus.plugin.pluginRegistryClassName", "org.datanucleus.plugin.OSGiPluginRegistry");
            }
    
            PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory(props, classLoader);
    
            return pmf;
        }
    
        private ClassLoader getClassLoader() {
            ClassLoader classLoader = null;
            Bundle thisBundle = FrameworkUtil.getBundle(this.getClass());
    
            if (thisBundle != null) { // on OSGi runtime
                Bundle[] bundles = bundleContext.getBundles();
    
                for (Bundle bundle : bundles) {
                    if (JDO_BUNDLE_NAME.equals(bundle.getSymbolicName())) {
                        try {
                            classLoader = bundle.loadClass(JDO_PMF_CLASS_NAME).getClassLoader();
                        } catch (ClassNotFoundException e) {
                            // do something fancy here ...
                        }
                        break;
                    }
                }
            } else { // on Java runtime
                classLoader = this.getClass().getClassLoader();
            }
            return classLoader;
        }
    
        @Override
        public void setBundleContext(BundleContext bundleContext) {
            this.bundleContext = bundleContext;
        }
    }