为基于Java的系统创建的OSGi提供了模块化系统的框架。 OSGi使得可以定义每个单独模块与其他模块的依赖关系,并使用户可以控制生命周期并动态更改系统的每个组件。

OSGi是一个规范,最常见的实现可以算作Equinox , Apache Felix和Knoplerfish 。 在本文中,我将尝试举一个在Equinox中创建简单OSGi捆绑包的示例。

OSGi结构 基本的OSGi结构可以在右侧的图中看到。 OSGi实现位于JVM的顶部,并提供用于模块的服务管理,组件定义,执行,管理和生命周期控制的机制。 最基本的OSGi概念描述如下:

在OSGi系统中,用于构建结构的组件的名称为“ Bundle ”。 在部署阶段,每个OSGi捆绑包都是一个jar文件。 但是bundle jar文件与常规jar文件的主要区别可以算作OSGi特定的清单定义和某些OSGi特定的类。 我们将在即将到来的部分和示例中讨论这些差异。

服务

服务提供了结构束之间的交互。 服务作为接口公开,并向执行该接口的实现注册。 与SOA结构并行,通过OSGi服务进行访问使得与基于常规jar的Java结构相比,基于OSGi的系统更加松散耦合。 这些结构还可以在运行时更改系统的更改组件。

OSGi为要注册和访问的服务实现服务目录。 OSGi还提供了服务管理机制。

生命周期

OSGi提供了一个平台,可以控制捆绑软件的生命周期。 在这种结构中,每个捆绑软件都有其自己的OSGi配置,主要是在依赖关系和暴露部分方面,并且系统由OSGi本身运行。 OSGi知道组成系统的捆绑包,该捆绑包带有一个带有顺序的配置文件,并且生命周期管理以给定顺序应用于每个组件。 生命周期管理的包端由“ Activator”类控制,该类实现了OSGi接口,该接口必须存在于每个“常规” OSGi包中。 (不适用于“片段”片段,但这已经超出了本文的讨论范围,请忽略这一点)

如上所述, b undle是一个jar文件,其中至少包含一个Activator类和一个MANIFEST文件,其中包含OSGi特定的标头和信息。下面可以看到一个示例MANIFEST文件。 让我们看一下定义中每个部分的含义。

Bundle-Name: Our Bundle
Bundle-SymbolicName: us.elron.bundles.ours
Bundle-Description: Very own bundle of ours
Bundle-ManifestVersion: 1
Bundle-Version: 1.0.0
Bundle-Activator: us.elron.bundles.ours.BundleActivator
Export-Package: us.elron.bundles.ours.exported; version = "1.0.0"
Import-Package: us.elron.bundles.yours.exported; version = "1.3.0"
  • 捆绑包名称 : 捆绑包的“吸引公众”名称。
  • Bundle-SymbolicName:作为MANIFEST文件中唯一的强制性定义,符号名称定义OSGi生态系统中捆绑软件的唯一名称。 由于此定义应该唯一,因此通常按照约定将其定义为捆绑软件的基本包名称。
  • 捆绑包描述:有关捆绑包 “存在理由”的描述。
  • Bundle-ManifestVersion:捆绑包的清单版本。
  • 捆绑软件版本: OSGi捆绑软件版本。
  • Bundle-Activator:此类用于控制包的生命周期。 OSGi调用此类以启动或停止捆绑软件。
  • 导出包:本节中定义了希望由其他包使用的包。
  • Import-Package:本节中定义了执行当前包所需的包。

OSGi结构提供了必要的机制来控制束的生命周期。 捆绑软件受OSGi的控制,以根据给定的配置控制其生命周期。 此生命周期步骤将在下面详细说明:

组件状态

描述

已安装

此状态表明安装步骤已成功完成。 在这种情况下,既不进行依赖关系分析也不进行类加载。 仅执行必需的步骤,例如定义分析其清单文件的包属性。

解决

当OSGi解析并满足其所有依赖关系并进行类加载操作时,便会在此状态下找到捆绑软件。 这是启动之前和停止之后的状态。

开始

这是在调用捆绑软件的激活器的“启动”方法但尚未成功或未成功完成时找到捆绑软件的状态。

活性

该捆绑包已成功启动并正在运行,这意味着Activator的“启动”方法导致成功。

停止

这是在调用捆绑软件的激活器的“停止”方法但尚未成功或未成功完成时找到捆绑软件的状态。

未安装

这是从系统中删除捆绑软件时的状态。 在这种情况下,不会过渡到另一个状态。 必须再次安装该组件。

上图中可以看到所描述的生命周期步骤之间的过渡。

让我们做一个简单的例子来阐明上面提到的概念和步骤。 在我们的示例中,将有两个捆绑包,其中一个捆绑包提供一个随机数生成器服务以生成随机数,另一个捆绑包将使用此服务每秒打印一个随机数,并进行单独的处理。 (没有道理吗?对我来说也一样,但足以掌握概念

现在,让我们使用Eclipse和Equinox一起开发此示例项目(最好)。 在Eclipse中,使用“新建插件项目”向导开发OSGi捆绑包,如下所示:

使用向导创建两个项目( us.elron.osgi.random和us.elron.osgi.user ),按照所需的步骤进行操作 , 并按如下所示命名包和激活器( RandomActivator , UserActivator )。 该项目的最终结果也应该是这样的:

下面给出了随机数生成包( us.elron.osgi.random )的服务定义,实现和清单定义。 接口(IRandomGenerator):

package us.elron.osgi.random;

public interface IRandomGenerator {

    int generate ();

    int generate(int upperBound);

}

服务(RandomGenerator):

package us.elron.osgi.random.generator;

import java.util.Random;

import us.elron.osgi.random.IRandomGenerator;

public class RandomGenerator implements IRandomGenerator {

    private final Random random;

    public RandomGenerator () {
        this.random = new Random();
    }

    @ Override
    public int generate () {
        return this.random.nextInt();
    }

    @ Override
    public int generate (final int upperBound) {
        return this.random.nextInt (upperBound);
    }

}

激活器(RandomActivator) :

package us.elron.osgi.random;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

import us.elron.osgi.random.generator.RandomGenerator;

public class RandomActivator implements BundleActivator {

    public void start(final BundleContext context) throws Exception {
        System.out.println("[Random] Let's 'Random'!");
        RandomGenerator randomGenerator = new RandomGenerator();
        context.registerService(IRandomGenerator.class.getName (), randomGenerator, null);
        System.out.println("[Random] Random services were registered.");
    }

    public void stop(final BundleContext context) throws Exception {
        System.out.println("[Random] Bundle is being stopped !");
    }

}

组件的MANIFEST.MF说明如下。 捆绑软件至少应导出具有其服务接口的软件包,以便其他捆绑软件使用它们。 因为松散耦合是SOA和OSGi系统的最重要目标之一,所以仅应从任何捆绑包中导出最少的必需类集。

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Random
Bundle-SymbolicName: us.elron.osgi.random
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: us.elron.osgi.random.RandomActivator
Bundle-Vendor: ELRON.US
Require-Bundle: org.eclipse.core.runtime
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-ActivationPolicy: lazy
Export-Package: us.elron.osgi.random

可以看出,服务是注册为OSGi服务的Java接口的实现。 激活程序类是捆绑软件的OSGi访问点。 OSGi使用捆绑软件的Activator类来管理其生命周期。 在此过程中,OSGi将“ org.osgi.framework.BundleContext ”接口的实现发送到分发包。 该接口使捆绑软件可以与OSGi层进行交互,并且可以在代码中看到,从而进行诸如注册和获取OSGi服务之类的操作。 现在让我们看一下用户包类: 这是打印由随机生成器服务生成的随机数的类。

package us.elron.osgi.user;

import us.elron.osgi.random.IRandomGenerator;

public class RandomPrinter extends Thread {

    private final IRandomGenerator random;
    private volatile boolean run = true;

    public RandomPrinter (final IRandomGenerator random) {
        this.random = random;
    }

    @ Override
    public void run () {
        while (this.run) {
            System.out.println ("[User] new random number: " + this.random.generate (300));
            try {
                Thread.sleep (1000);
            } catch (final InterruptedException e) {
                break;
            }
        }
        System.out.println ("[User] The process was terminated.");
    }

    public void close () {
        this.run = false;
    }

}

这是Activator的实现:

package us.elron.osgi.user;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;

import us.elron.osgi.random.IRandomGenerator;

public class UserActivator implements BundleActivator {

    private RandomPrinter randomPrinter;

    public void start (final BundleContext context) throws Exception {
        System.out.println ("[User] Here we go ..");
        ServiceReference randSrvRef = context.getServiceReference (IRandomGenerator.class.getName ());
        IRandomGenerator randService = (IRandomGenerator) context.getService (randSrvRef);
        if (randService == null) {
            throw new Exception ("[User] Error! Random service could not be found!");
        }
        this.randomPrinter = new RandomPrinter(randService);
        this.randomPrinter.start();
    }

    public void stop (final BundleContext bundleContext) throws Exception {
        System.out.println ("[User] finish ..");
        this.randomPrinter.close ();
    }

}

“用户”捆绑包的MANIFEST.MF描述如下。 我们应该使用随机服务接口所在的随机生成器束的“ us.elron.osgi.random”包定义依赖项。 可以在包或包级别定义依赖关系,但是,为了减少包之间的依赖关系,最好尽可能地选择包级别的依赖关系。

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: User
Bundle-SymbolicName: us.elron.osgi.user
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: us.elron.osgi.user.UserActivator
Bundle-Vendor: ELRON.US
Require-Bundle: org.eclipse.core.runtime
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-ActivationPolicy: lazy
Import-Package: us.elron.osgi.random

要使用Eclipse在OSGi上运行这些项目,应定义运行配置,如下所示。 在OSGi Framework下的“ 运行 (或调试 ) 配置 ”步骤中,应创建一个新配置(右键单击),并在此配置中选择我们的新捆绑包。 要为选定的包提供所需的依赖关系,我们可以使用“添加所需的包” ”按钮。 通过这种方式,Eclipse将解决依赖关系层次结构,并为选定的依赖关系添加所需的包。 我们还应该定义捆绑包的开始顺序。 应根据捆绑包的依赖关系定义此顺序。 依赖束应在它们依赖的束之后开始。 因此,在我们的示例中,我们将“ us.elron.osgi.random”的级别设置为1 ,同时将“ us.elron.osgi.user ”的级别设置为2 。

以这种形状运行项目会生成如下输出:

OSGi> [Random] Let's 'Random'!
[Random] Random services were registered.
[User] Here we go ..
[User] new random number: 38
[User] new random number: 250
[User] new random number: 94
[User] new random number: 150
[User] new random number: 215
[User] new random number: 124
[User] new random number: 195
[User] new random number: 260
[User] new random number: 276
[User] new random number: 129

OSGi运行时提供了一个控制台界面供我们与自己进行交互。 运行控制台应用程序窗口时,我们看到一个“ osgi>”脚本,表示可以访问控制台。 提到您可以在控制台中执行的一些重要命令之后,我将让您一个人呆在控制台上,让您发现可以从“帮助”命令开始的操作。

“ ss”命令,显示所有注册到OSGi的组件及其ID , 状态和捆绑包名称值以及版本部件。 id值表示OSGi给每个捆绑软件的唯一标识符。 即使重新安装并安装了捆绑软件(发现一件事),该数字在JVM执行中也保持不变,但是可以在新的执行中进行更改。 状态值指示捆绑软件的状态(在上表中详细说明), 名称和版本值指示其名称对我们的影响。 对于当前系统, “ ss”命令的输出如下:

OSGi> ss

Framework is launched.

id State Bundle
0  ACTIVE org.eclipse.osgi_3.6.0.v20100517
          Fragments = 4
2  ACTIVE org.eclipse.core.jobs_3.5.0.v20100515
3  ACTIVE javax.servlet_2.5.0.v200910301333
          Resolved javax.transaction_1.1.1.v201006150915 4
          Master = 0
5  ACTIVE org.eclipse.core.runtime_3.6.0.v20100505
6  ACTIVE org.eclipse.equinox.preferences_3.3.0.v20100503
7  ACTIVE org.eclipse.osgi.services_3.2.100.v20100503
8  ACTIVE org.eclipse.core.runtime.compatibility.auth_3.2.200.v20100517
9  ACTIVE us.elron.osgi.random_1.0.0.qualifier
          Resolved org.eclipse.core.runtime.compatibility.registry_3.3.0.v20100520 10
          Master = 11
11 ACTIVE org.eclipse.equinox.registry_3.5.0.v20100503
          Fragments = 10
12 ACTIVE org.eclipse.equinox.app_1.3.0.v20100512
13 ACTIVE org.eclipse.equinox.common_3.6.0.v20100503
14 ACTIVE org.eclipse.core.contenttype_3.4.100.v20100505 14-1235
15 ACTIVE us.elron.osgi.user_1.0.0.qualifier
OSGi>

假设我们要关闭用户捆绑包。 在这种情况下,我们需要使用要停止的分发包的ID(在这种情况下为15)执行“停止”命令。

[User] a new random number is: 48
[User] a new random number is: 49
OSGi> stop 15
[User] finish ..
[User] The process was terminated.

当我们再次查看“ ss”命令的输出时,

Framework is launched.

id State Bundle
0  ACTIVE org.eclipse.osgi_3.6.0.v20100517
          Fragments = 4
2  ACTIVE org.eclipse.core.jobs_3.5.0.v20100515
3  ACTIVE javax.servlet_2.5.0.v200910301333
          Resolved javax.transaction_1.1.1.v201006150915 4
          Master = 0
5  ACTIVE org.eclipse.core.runtime_3.6.0.v20100505
6  ACTIVE org.eclipse.equinox.preferences_3.3.0.v20100503
7  ACTIVE org.eclipse.osgi.services_3.2.100.v20100503
8  ACTIVE org.eclipse.core.runtime.compatibility.auth_3.2.200.v20100517
9  ACTIVE us.elron.osgi.random_1.0.0.qualifier
          Resolved org.eclipse.core.runtime.compatibility.registry_3.3.0.v20100520 10
          Master = 11
11 ACTIVE org.eclipse.equinox.registry_3.5.0.v20100503 Fragments = 10
12 ACTIVE org.eclipse.equinox.app_1.3.0.v20100512
13 ACTIVE org.eclipse.equinox.common_3.6.0.v20100503
14 ACTIVE org.eclipse.core.contenttype_3.4.100.v20100505-1235
15 RESOLVED us.elron.osgi.user_1.0.0.qualifier

我们看到ID为15的User bundle的状态已解决 (请参见生命周期部分)。 同样,我们可以执行启动命令(启动15)来启动捆绑软件并观察该过程再次开始工作,或者执行“ s ”命令查看注册到OSGi的所有服务,或者使用uninstall命令从OSGi删除捆绑软件。 您可以自由发现!

在本文中,我试图简单地解释一下OSGi是什么,它如何工作以及可以用它做什么。 希望你喜欢它。 您可以在此处下载资源。

随时通过elron [at] elron.us发表评论或联系。 我很高兴收到您的来信。

参考: OSGi:我们的JCG合作伙伴 Elron在Ender Ayd?n Orak博客上的介绍。

相关文章 :

  • OSGi将Maven与Equinox结合使用
  • OSGI和Spring动态模块–简单的Hello World
  • OSGi –带有服务的简单Hello World
  • Java EE6 CDI,命名组件和限定符

翻译自: https://www.javacodegeeks.com/2012/01/osgi-introduction.html