【问题标题】:Injecting singleton OSGi Declarative Service in Eclipse RCP在 Eclipse RCP 中注入单例 OSGi 声明式服务
【发布时间】:2021-01-05 16:42:32
【问题描述】:

我正在尝试定义一个单例 OSGi 服务,该服务将由我的 Eclipse RCP 应用程序中的其他插件使用(=共享),但每个插件都有自己的 version(来自本地类加载器)并且只有排名最高的一个版本被注入。

服务是这样定义的(在com.test.taskmodel 包中)(暂时不使用单独的接口和实现):

@Component(scope=ServiceScope.SINGLETON, service=TaskService.class)
public final class TaskService {

    public TaskService() {}

    @Activate
    void activate(BundleContext bundleContext) {
        //activate
    }
    
    public Result doStuff() {
        return null;
    }
}

当我使用 bnd 构建它时,生成的 jar 清单包含 Service-Component: OSGI-INF/com.test.TaskService.xml 并且 xml 是:

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.3.0" name="com.test.TaskService" activate="activate" deactivate="deactivate">
  <service scope="singleton">
    <provide interface="com.test.TaskService"/>
  </service>
  <implementation class="com.test.TaskService"/>
</scr:component>

有三个 Eclipse RCP 插件使用该服务:taskbrowsertaskfiltertaskdetail。它们现在几乎都是相同的,每个只有一个像这样的部分:

public class TaskBrowserPart {
    
    @Inject @Service @Optional private TaskService taskService;

    @PostConstruct
    public void createControls(Composite parent) {
        if (taskService == null) {
            System.out.println("TaskBrowser TaskService null");
        } else {
            System.out.println("TaskBrowser TaskService non-null");
        }
    }
}

当我运行应用程序时,我得到以下输出:

TaskBrowser TaskService non-null
TaskFilter TaskService null
TaskDetail TaskService null

在 osgi 控制台中调用service 命令会显示四个已注册的服务,只有第一个被所有树插件使用:

{com.test.taskmodel.TaskService}={service.id=63, service.bundleid=155, service.scope=bundle, component.name=com.test.taskmodel.TaskService, component.id=34}
  "Registered by bundle:" com.test.taskbrowser [155]
  "Bundles using service"
    com.test.taskfilter [165]
    com.test.taskdetail [158]
    com.test.taskbrowser [155]
{com.test.taskmodel.TaskService}={service.id=65, service.bundleid=158, service.scope=bundle, component.name=com.test.taskmodel.TaskService, component.id=36}
  "Registered by bundle:" com.test.taskdetail [158]
  "No bundles using service."
{com.test.taskmodel.TaskService}={service.id=66, service.bundleid=159, service.scope=bundle, component.name=com.test.taskmodel.TaskService, component.id=37}
  "Registered by bundle:" com.test.taskmodel [159]
  "No bundles using service."
{com.test.taskmodel.TaskService}={service.id=87, service.bundleid=165, service.scope=bundle, component.name=com.test.taskmodel.TaskService, component.id=39}
  "Registered by bundle:" com.test.taskfilter [165]
  "No bundles using service."

当我使用scope="singleton" 定义服务时,为什么会有多个带有service.scope=bundle 的服务实例?如何以仅存在一个服务实例并且所有插件都使用它的方式定义它?为什么 Eclipse 尝试只注入第一个服务,然后在 taskfiltertaskdetail 插件中失败,因为它们使用不同的类加载器,因此服务类型不匹配?为什么选择服务时不考虑类加载器,而只在注入时考虑?在那种情况下,我至少在每个插件中都有一个单独的实例,这不是我想要的,但至少是我可以使用的。任何帮助表示赞赏,谢谢。

【问题讨论】:

    标签: java osgi eclipse-rcp bnd bndtools


    【解决方案1】:

    看起来您在所有包中声明服务,导致每个包都有自己的 TaskService 实现。然后它们彼此不兼容,因此当第一个实例返回给非所有者时,它来自不同的类加载器。

    正如@Patrick Paulin 在他的回答中提到的,您可以在您的服务打印输出中看到这一点

    "Registered by bundle:" com.test.taskbrowser [155]
    "Registered by bundle:" com.test.taskdetail [158]
    "Registered by bundle:" com.test.taskmodel [159]
    "Registered by bundle:" com.test.taskfilter [165]
    

    Registered 意味着一个类定义是由它“提交”的(而不是它创建了一个实例)。

    当您使用广泛的选择器定义 jar 内容时,可能会在 Bnd 中发生,例如

    -private-package: com.test.*
    Private-Package: com.test.*
    Export-Package: com.test.*
    

    导致 com.test.taskservice 无处不在。 DS 会找到您的声明并在每个捆绑包中创建相同的 scr:component xml,相互竞争。

    https://bnd.bndtools.org/heads/private_package.html

    Bnd 遍历类路径上的包,并根据 Export-Package 和 Private-Package 标头给出的指令将它们复制到输出。

    【讨论】:

    • 这正是问题所在。我检查了 taskmodel jar,它 确实 包含它应该包含的内容,但没有考虑检查其他 jar,以防它们包含他们 不应该 的内容。感谢您的解决。
    【解决方案2】:

    您想要完成的实际上是声明式服务的默认行为。您不需要指定 ServiceScope.SINGLETON 属性,也不需要在此处使用额外的 @Service 注释(尽管这些都不会导致问题)。

    这是否发生在 Eclipse IDE 本身使用工具创建 DS XML 文件?我注意到您在问题中使用 Bnd 引用。您是否看到任何旧的 XML 文件或服务组件条目?或者 OSGi 缓存中是否可能存在陈旧数据?

    假设 Service-Component 条目仅在 TaskModel 包中,您真的不应该看到创建 TaskService 的 TaskBrowser、TaskDetail 和 TaskFilter 包。

    【讨论】:

      猜你喜欢
      • 2013-10-09
      • 1970-01-01
      • 2018-05-20
      • 1970-01-01
      • 1970-01-01
      • 2012-11-06
      • 2012-04-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多