Bndtools – 03 – 教程

介绍如何使用Bndtools来开发组件(Component)

目录

1. 介绍

在此教程中我们将构建一个样例应用,它会由两个组件(Component)和一个API(译者注:Java 接口)组成。下图展示了该Bundle的架构(简化后的):

20170212_001

在本教程中,我们将创建上面的三个Bundle(圆角矩形框标出的):

● API Bundle导出(export)了一个服务接口:Greeting。

● Provider Bundle导入(import)了服务接口并发布了一个服务实例。

● Command Bundle导入了服务接口并将其绑定到了服务实例,同时还发布了一个“Command”服务,该服务被Felix Shell Bundle调用。

2. 安装Bndtools

请参考安装教程

3. 创建一个API工程

首先我们需要创建一个Bndtools OSGi Project。这是一个标准的Eclipse Java Project,并且带有一个额外的OSGi Bundle构建器。

1. 从File菜单中进入,选择New -> Bndtools OSGi Project菜单项。

20170212_002

2. 在下一个页面中,输入example.api 作为工程名。选择J2SE-1.5版本以上的JRE执行环境。

20170212_003

3. 然后你需要选择一个项目模板。选择Empty Project并点击 Finish按钮。然后将会创建新的工程。

20170212_004

4. 如果你是第一次在当前工作空间(Workspace)中使用Bndtools,会看到“Welcome”对话框。点击Next 按钮,随后点击 Finish 按钮来创建一个配置工程(Configuration Project)并导入一个基本的依赖库(Repository)。依赖库用于存储在你的工程中要用到的Bundle。默认会创建一个远程的“Bndtoos hub”依赖库,其中会包含一些常用的Bundle。

20170212_005

20170212_006

要点:

● Bndtools工程基于标准的Eclipse Java(JDT)工程。

● Bndtools会使用一个cnf工程,其中包含工作空间级别的配置信息,这些信息可以在多个开发者之间共享。其中还可能会包含一个Bundle仓库。

● 在每个Bndtools工程的顶层目录中将会创建一个名为bnd.bnd的文件,它用于控制工程的设置。当进行离线的ANT构建时,也将会使用相同的设置。

3.1 编写并导出(ExportAPI

对于功能的生产者和消费者,OSGi提供了非常强大的解耦能力。它采用了一种基于API(在Java编程中通常称为基于接口)的编程模型,在此模型中功能的生产者实现API,而功能的消费者仅与API绑定,但不与具体的实现绑定。在我们的样例中将会使用一个非常简单的API。

在新工程的src文件夹中我们将会创建一个名为org.example.api的包。在其中创建一个名为Greeting的Java接口,其代码如下所示:

3.2 定义Bundle

在刚刚创建的工程中我们定义了一个 Bundle Symbolic Name (BSN)为org.example.api(也即与工程名一致)的Bundle。在我们创建工程的同时,在generated文件夹中也创建了一个名为 org.example.api.jar的Bundle文件,并且它会在每次我们改变Bundle的定义或源码时被重新构建。

不过,目前我们的Bundle还是一个空Bundle,因为我们还没有在Bundel中定义任何Java包。这是Bndtools与其它工具相比非常不同的一点:在我们明确地向Bundle中添加内容之前,Bundle一直是空的。你可以通过双击对应的Bundle文件并查看其中的内容来验证:此时其中仅有一个META-INF/MANIFEST.MF文件。

我们希望将org.example.api 包添加到Bundle的导出包(exported packages)信息中。因此打开工程顶级目录中的bnd.bnd文件并选择Content选项卡。随后可以通过如下两种方式来添加包:

● 点击位于 Export Packages区域顶部的 “+”按钮,然后从弹出的对话框中选择“org.example.api”并点击 OK。或者:

● 将org.example.api包从Eclipse的包浏览视图中拖放至 Export Packages 列表中。

(TIP:高级用户可能会更喜欢在 Source 选项卡中手工输入 Export-Package: org.example.api)。

在完成这些操作之后,将会弹出一个“缺少包信息(Missing Package Info)”的对话框。这个对话框与包的版本有关:它要求我们声明被导出的包的版本。然后点击 OK按钮。

20170212_007

Contents 选项卡的内容将会如下图所示:

20170212_008

保存文件后此Bundle将会被重新构建,其中会包含刚刚选择的导出内容。我们可以通过打开Imports/Exports视图并选择在包浏览器(Package Explorer 中的Bundle文件来确认之。注意对应的包的版本被设置为了1.0.0:

20170212_009

要点:

● 工程的配置信息和Bundle的内容都在bnd.bnd中进行定义。

● Budnle的唯一标识——它的”Bundle Symbolic Name”(BSN)——是由工程名称控制的。在本例中,Bundle的BSN与工程名称一样。

● 在我们明确地添加内容之前,Bundle的内容是空的。将包添加到Export Packages面板之中会将对应的包添加到Bundle之中,并且会在META-INF/MANIFEST.MF中将其声明为一个导出包。

● 通常Bundle中会包含多个接口。本例中特意进行了简化。

4. 创建一个实现工程(Implementation Project

接下来我们来创建另外声明两个Bundle的工程:一个生产者以及一个Greeting API的客户(消费者)。

4.1 创建工程

创建另外一个名为org.example.impls的Bndtools工程。在Projet Templates步骤中选择Component Development (Declarative Services)并点击Finish按钮。

20170212_010

4.2 添加API作为构建依赖(Build Dependency

我们需要将API工程作为构建时的依赖项添加到此工程。

新创建工程后将会自动打开bnd.bnd文件。点击Build选项卡并使用如下的方法之一来添加org.example.api包:

● 点击位于Build Path面板的工具栏上的“+”图标。双击在接下来打开的对话框中的”Workspace”下方的org.example.api;它会移到右侧列表中。点击Finish按钮。

20170212_011

● 或者将org.example.api从Repositories视图中拖放到Build Path面板中。

无论使用哪种方式,org.example.api Bundle都将会出现在Build Path面板之中,并且版本标识为“latest”:

20170212_012

保存文件。

要点:

● 工程的构建时依赖可以通过bnd.bnd编辑器添加到Build Path面板。

● 使用此种方式来添加依赖(而不是使用Eclipse中已有的“ Add to Build Path”菜单)能确保在进行离线ANT构建时会使用同样的依赖。

4.3 编写一个实现(ImplementationBundle

我们将编写一个实现了Greeting接口的Java类。在通过模板创建项目时,将会创建一个名为org.example.ExampleComponent的Java类源码。现在打开此源文件并使其实现Greeting接口:

注意使用了@Component注解。这使得我们的Bundle使用OSGi Declarative Service(DS)来声明API实现类。这意味着该类的实例将会自动被创建并注册到OSGi服务注册表(Service Registry)中。这个注解仅在构建时有用,它并不会引入任何运行时的依赖——换而言之,这是一个POJO(Plain Old Java Object)。

4.4 测试实现Bundle

接下来我们将编写一个测试用例来确保实现类能够正常运行。在test文件夹中已经产生了一个名为org.example.ExampleComponentTest的测试用例类。按照下面的代码来编写测试方法:

在文件上右键单击并选择Run As > JUnit Test菜单项。

20170212_013

确认在JUnit视图中显示了一个绿色的长条。否则请检查你的代码是否正确!

注意这是一个单元测试而不是集成测试,因此我们不需要运行一个OSGi框架;此时使用了标准的JUnit launcher。再次强调,之所以这样可行是因为待测试的组件是一个POJO。

4.5 构建实现Bundle

如前所述,一个Bundle的内容将会根据bnd.bnd的内容进行自动构建。但在当前的工程中,我们希望构建两个独立的Bundle。因此我们需要启用名为“Sub-bundles”的特性。

在org.example.impls工程上右键单击并选择New > Bundle Descriptor菜单项。在随后的对话框中输入名称provider并点击Finish按钮。

接下来的弹出对话框将会询问是否启用sub-bundles特性。点击OK按钮。

20170212_014

一些新的设置项将会从bnd.bnd文件中移到新的provider.bnd文件中。你现在应该可以找到一个新生成的名为org.example.impls.provider的Bundle,其中包含了一个名为org.example的包和Declarative Services 组件(component)声明文件OSGI-INF/org.example.ExampleComponent.xml。

要点:

● Bndtools工程可以输出一个或多个Bundle。

● 在单Bundle模式的工程中,Bundle的内容在bnd.bnd中进行定义。

● 在多Bundle模式的工程中,各个Bundle的内容都在独立的.bnd文件中进行定义。bnd.bnd文件仅用于定义工程级别的设置,例如构建依赖等。

5. 运行OSGi框架

现在来试试运行OSGi。我们需要创建一个运行描述符(Run Descriptor),它用于定义需要运行的Bundle集合以及其它运行时设置。

在org.example.impls工程上右键单击并选择New > Run Descriptor菜单项。在后面的对话框中输入run作为名称并点击Next按钮。后续的对话框会要求我们选择一个模板;选择Apache Felix 4 with Gogo Shell并点击Finish按钮。

20170212_015

在新生成的run.bndrun文件的编辑器中,点击右上角的Run OSGi按钮。稍后你将会在Console视图中看到Felix Shell的提示符”g!”。输入lb命令可以查看Bundle清单:

下面我们希望将org.example.impls.provider和osgi.cmpn Bundle都包含进来。可以按如下步骤进行操作:

● 点击位于Run Requirements面板的工具栏上的”+”图标打开’添加需要的Bundle(Add Bundle Requirement)’ 对话框。

● 在”Workspace”下面,双击org.example.impls.provider。

● 在”Bndtools Hub”下面,双击osgi.cmpn。

● 点击 Finish按钮。

Run Requirements面板现在看起来应该如下图所示:

20170212_016

勾选Auto-resolve on save选项并保存文件。返回到Console视图,再次输入lb命令:

此时,provider Bundle已经被动态添加到了运行时之中。注意API Bundle和Apache Felix Declarative Services Bundle也都被添加了,因为它们被解析为provider Bundle的依赖项。

现在我们可以使用命令inspect capability service 8来查看由我们的provider Bundle所发布的服务:

现在我们的Bundle在Greeting接口下发布了一个服务。

要点:

● 在.bndrun 文件中可以定义运行时配置,可以使用多个不同的配置,产生不同的Bundle集合,使用不同的OSGI框架等。

● 需要包含的Bundle可以从Run Requirements清单中选择。Bndtools使用OBR解析器来解析Bundle清单并自动包含其静态依赖。

● 如果在OSGi框架运行的时候保存bndrun文件,会动态更新Bundle清单。因此我们可以添加或移除Bundle而不需要重启框架。

● 编辑一个已有的Bundle——包括编辑其Java代码——将会在运行时触发该Bundle的动态更新。

6. 编写一个命令组件(Command Component

最后我们来编写一个消费Greeting服务组件(Component),该组件同时还会发布一个shell命令,使得我们可以通过Felix shell来调用该组件。

首先我们需要使得Felix shell API变得可用。打开bnd.bnd文件,选择Build选项卡。将org.apache.felix.gogo.runtime添加到构建依赖清单之中,然后保存文件:

20170212_017

然后在src文件夹中创建一个新的Java包,将其命名为 org.example.command。在其中创建一个GreetingCommand类,代码如下所示:

6.1 为命令组件创建一个Bundle

这个命令组件并不是provider Bundle的一部分,因此它所在的包并未被包含进来。我们可以将其添加到provider Bundle之中,但为其创建一个单独的Bundle会更好一些。

再次在 org.example.impls工程上右键单击并再次选择New > Bundle Descriptor 菜单项。输入command作为名称并单击Finish按钮。

将org.example.command包添加到新创建的文件的Private Packages面板之中。如前所述,可以通过工具栏中的”+”按钮或拖放来完成此操作。

我们还需要声明此Bundle包含了Declarative Services组件。切换到编辑器的Contents选项卡,并且在Declarative Services下拉列表中选择Bnd Annotations,然后保存文件。

6.2 将命令组件添加到运行时(Runtime

切换到run.bndrun的编辑器中。在Run Requirements选项卡中,添加org.example.impls.command Bundle,然后保存文件。

在输入lb命令后,将会在控制台上看到command Bundle出现在了Bundle清单之中:

现在我们终于可以通过Gogo shell调用greet命令了:

打赏一下
支付宝
微信
除非注明,博客文章均为原创,转载请标明文章地址
本文地址: http://www.javafxchina.net/blog/2017/02/bndtools-03-tutorial/
百度已收录