【问题标题】:Java EE Plugin Framework using CDI Instance Iterator使用 CDI 实例迭代器的 Java EE 插件框架
【发布时间】:2017-01-09 23:23:50
【问题描述】:

我有一个应用程序,它由战争、一个核心 ejb 和一个 jar 中的许多服务 bean 和另一个 jar 中的远程接口组成。一切都打包在一个耳朵里,并在 Glassfish 4.1 上运行。

现在我想为核心 ejb 添加扩展点或插件支持。

目标是提供共享相同接口的不同热插拔数据导入服务,因为它们从路透社和彭博等供应商处获取和规范化财务数据。

这些插件应该由核心 ejb jar 中的“插件管理器”bean 检测和管理。插件应支持在运行时加载、卸载和替换。

理想情况下,插件接口位于单独的包中,这样其他人就可以针对它们进行开发,而无需我的应用程序或 Glassfish,即使没有 Java EE 堆栈也可以完美地进行开发。我还想按需部署插件,而不是总是部署整个应用程序。

目前我尝试使用 CDI 实例迭代器,只要它们在核心 ejb 中,它就可以与两个导入服务实现一起正常工作。如果我将一个实现放入单独的 ejb jar 中,那么 CDI 根本找不到它。我想问题是 Glassfish 将每个 ejb jar 作为应用程序加载到单独的类加载器中。

现在是我当前的简化代码!

单独jar包中的插件接口:

package com.photon.extensions;

import java.io.Serializable;

public interface ImportServiceExtension extends Serializable {
    String getImportServiceName();
}

未找到的单独ejb jar包中的插件实现:

package com.photon.services.extensions.vitrex.services;

import com.photon.extensions.ImportServiceExtension;
import javax.ejb.Remote;
import javax.ejb.Stateless;

@Remote(ImportServiceExtension.class)
@Stateless
public class ReutersImportService implements ImportServiceExtension {
    @Override
    public String getImportServiceName() {
        return "Reuters";
    }
}

找到的核心ejb jar包中的插件实现:

package com.photon.services.extensions;

import com.photon.extensions.ImportServiceExtension;
import javax.ejb.Stateless;

@Stateless
public class BloombergImportService implements ImportServiceExtension {
    @Override
    public String getImportServiceName() {
        return "Bloomberg";
    }
}

远程接口jar中“插件管理器”的远程接口:

package com.photon.services.extensions;

import java.util.List;
import javax.ejb.Remote;

@Remote
public interface ImportServiceExtensionsRemote {
    List<String> getImportServiceNames();
}

核心 ejb jar 中的“插件管理器”bean 实现:

package com.photon.services.extensions;

import com.photon.extensions.ImportServiceExtension;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.ejb.Stateless;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;

@Stateless
public class ImportersService implements ImportServiceExtensionsRemote {

    @Inject private Instance<ImportServiceExtension> importServiceExtensions;

    @Override
    public List<String> getImportServiceNames() {
        Iterator<ImportServiceExtension> iter = importServiceExtensions.iterator();
        List<String> names = new ArrayList<>();
        while ( iter.hasNext() ) {
            ImportServiceExtension extension = iter.next();
            names.add(extension.getImportServiceName());
        }
        return names;
    }
}

最后是在战争中将名称呈现给网站的控制器:

package com.photon.website;

import com.photon.services.extensions.ImportServiceExtensionsRemote;
import javax.ejb.EJB;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;

@RequestScoped
@Named
public class ImportController implements Serializable {

    @EJB private ImportServiceExtensionsRemote importServiceExtensions;

    public String getImportServiceNames() {
        String names = "";
        for ( String name : importServiceExtensions.getImportServiceNames() ) {
            names += name;
        }
        return names;
    }
}

最后只渲染了“Bloomberg”。

现在我的问题:

  1. 我走对了吗?

  2. 如果是这样,我在代码中缺少什么?

  3. 是否有更好的解决方案来解决这个问题(OSGI、自定义 clazz.forName, ...)?

【问题讨论】:

  • 如果有人仍然感兴趣:最后我使用 OSGI 管理它。

标签: jakarta-ee plugins cdi hotplugging


【解决方案1】:

我不能给你完整的答案,但这里有一些值得思考的地方......

我猜问题是 Glassfish 将每个 ejb jar 加载为 应用程序在一个单独的类加载器中。

你成功了。遗憾的是,这是每个 JEE 规范,所以这是预期的行为。

我不了解 Glassfish,但可能有一些功能允许您在部署之间共享类加载器(Wildfly 中有一些 - 称为部署隔离)。这可能会解决您的问题。

我从 Wildfly 了解到的另一件事是,您可以将一些应用程序部署为 服务器模块,然后它可以访问所有其他部署(及其类加载器)。如果 Glassfish 中有类似的东西,你可以试试。如果您愿意试一试 Wildfly,here 是一个指向讨论此问题的问题的链接。

现在,从 CDI 的角度来看这种行为也是正确的,恐怕您无法更改它,因为您无权访问 类加载器 从其他部署中(如果你有,你可以为给定的部署加载BeanManager 并搜索相关的bean)。

我希望这至少能给你一些见解。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-11-27
    • 1970-01-01
    • 1970-01-01
    • 2011-10-15
    • 2015-07-29
    • 1970-01-01
    • 1970-01-01
    • 2011-05-03
    相关资源
    最近更新 更多