3

我正在开发一个基于 OSGi 的系统,我打算定期“更新”而不关闭整个系统。我打算使用“更新”来促进捆绑更改,因此编写了一个小应用程序(2 个捆绑包)来尝试证明该理论。

我的最终目标:我正在尝试实现一个可以使用 OSGi 动态更新捆绑包的平台。

到目前为止:我已经制作了 2 个捆绑包;一个数学包(有 2 个可以相加和相乘的方法)和一个显示包,它有一个每秒运行的线程,生成 2 个随机数并使用前面提到的数学包对它们进行相加和相乘(并显示结果)。我正在使用声明性服务,因此在数学包中有一个组件定义,它导出由接口 IMath 定义的服务。同样,我在显示包中有一个组件定义,它订阅(1:1 静态)由 IMath 接口定义的服务。我在每个组件的启动/关闭的每个阶段都有典型的调试消息。

当项目启动时,我通常会看到:

Starting up Math...
Starting up Display...
Running the Display thread...

然后每一秒我都会看到显示线程进行计算。此外,我可以执行以下操作(假设数学是捆绑 1,显示是捆绑 2)。

> stop 1
Stopping the Display thread...
Display bundle has been shut down.
Math bundle has been shut down
> start 1
Starting up Math...
Starting up Display...
Running the Display thread...

问题:到目前为止一切都很好,对吧?在我尝试使用“更新”命令之前,一切都很好。在这种情况下,我想更新数学包,因为我在乘法计算中出错了。

> update 1
Stopping the Display thread...
Display bundle has been shut down.
Math bundle has been shut down
Starting up Math...

什么?为什么 ds 没有调用我的启动方法来重新启动显示包?我还尝试更新显示包,它似乎工作正常。我的感觉是,如果您更新捆绑包,它将重新启动,但是从更新的捆绑包订阅服务的任何捆绑包都将处于不确定状态。

更糟糕的是,如果我停止并启动显示包,它仍然无法启动!

我相当确定我正在以错误的方式看待某些事情,所以如果有人能对我的问题有所了解,那就太好了。如果有人想要源代码,请告诉我,我可以附上一些基本的 java 文件来演示问题。

如果我对我的问题不够具体,请告诉我,我会推断。

谢谢阅读!亚伦

4

1 回答 1

4

我猜您在更新后没有进行刷新,并且数学捆绑包导出了 IMath?

如果你更新一个包,OSGi 不会让旧的类加载器消失。该加载器只能通过垃圾收集离开,即不再存在类引用。所以当bundle 1更新时,它会创建一个bundle 1',一个新的类加载器。但是,您的 bundle 2 仍然绑定到 bundle 1 类加载器。为防止类路径异常,OSGi 不会向 bundle 2 显示任何不兼容的服务。由于 bundle 1' 现在注册了一个 IMath' 服务 bundle 2 不能再看到这个服务,因为它寻找 bundle 1 的 IMath,它的类加载器仍然绑定到 bundle 1 的 IMath。所以这是工作情况:

     +----+                +----+
     | b1 |-------<|-------| b2 |                  <| service
     +----+       v        +----+                  E  exports package
        \___E___[IMath]___I__/                     I  imports package

现在我们进行更新:

        /----E--[IMath]---I--\
     +----+        v       +----+
     | b1 |       <|       | b2 |
     +----+       |        +----+
                  |           |
                  +-----------+

     +----+         
     | b1'|-------<|
     +----+       v 
        \___E___[IMath']

刷新操作查看所有捆绑包并找出哪些捆绑包连接到“陈旧”捆绑包,在本例中为 b2。然后它将停止捆绑 2,确保删除其对 b2 类加载器的所有引用,然后使用新的类加载器再次启动 b2,以便它可以解析为 b1'。然后它将再次启动 b2,因为它是在刷新操作之前启动的:

                           +----+
                      +----| b2 |
                      |    +----+
                      |       |
     +----+           |       |
     | b1'|-------<|--+       |
     +----+       v           |
        \_______[IMath']______/

这通常会给人们留下这样的问题:什么......?为什么不结合更新/刷新?我该如何处理。

在 OSGi 1.0 中,我们对这个边缘阶段(我猜我们没有意识到它的存在)争论不休。因此,在 OSGi 2 中,我们发现一些供应商“渴望”(将更新与刷新结合起来),而一些供应商很懒惰(还有一些根本没有做任何事情)。更深入地思考它,我们意识到如果我们急切地进行大型更新集将变得非常低效,因为刷新需要大量工作。所以我们假设更新会像这样完成:

  1. 停止更新捆绑包
  2. 更新要更新的捆绑包
  3. 刷新
  4. 启动所有更新的捆绑包

通过这种方式,您可以最大限度地减少中断(更新的捆绑包仅停止/启动一次)并且捆绑包仅刷新一次。如果您查看bnd 启动器,您将详细看到此模式(bnd 会自动更新在 IDE 中更改的捆绑包)。

Now, this way of updating is pretty foolproof. However, some people like to live on the edge and use 'optimizations'. First you, won't have this problem if you have a bundle 3 that exports IMath:

     +----+
     | b1 |-I-\
     +----+   |
        |     |      +----+
        ^  [IMath]-E-| b3 |           
        |     |      +----+
     +----+   |
     | b2 |-I-/
     +----+

In this constellation, an update of b1 will be fine, when b1' resolves it finds IMath and will this register an IMath service, which happens to match b2's class loader for IMath (i.e. b3). Though this is less disruptive, it adds an extra bundle for no 'logical' reason. Personally, I think providers (i.e. b1) should export their contract (IMath) since they are very tightly coupled to this contract, unlike consumers that usually enjoy backward compatibility.

于 2013-07-29T08:00:59.880 回答