为基于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