要运行本文中的示例,请确保已在计算机上安装和设置了以下软件:
接下来让我们看看如何使用基于 Felix 的 OSGi 框架创建订单应用程序包。此应用程序包括两个组件:OrderClient.java(客户端)和 OrderService.java(服务器端)。客户端组件打包为 client.jar,服务器组件打包为 order.jar。接下来让我们首先看看 OrderClient
类。
清单 1. 客户端组件 OrderClient
public class OrderClient implements BundleActivator { private ServiceTracker orderTracker; private OrderService orderService; public void setService(OrderService orderService) { this.orderService = orderService; } public void removeService() { this.orderService = null; } public void start(BundleContext context) throws Exception { orderTracker = new ServiceTracker(context, OrderService.class.getName(), null); orderTracker.open(); OrderService order = (OrderService) orderTracker.getService(); if (order == null) { System.out.println("Order service not available"); } else { order.processOrder(); } } public void stop(BundleContext context) { System.out.println("Bundle stopped"); orderTracker.close(); } }
正如清单 1 中所示,OrderClient
对 OrderService
组件调用 processOrder
方法,以在启动 client.jar 包时打印 orderID
。此类实现 BundleActivator
接口,该接口具有两个回调方法:start()
和 stop()
。启动和停止客户端包时,Felix 容器将调用实现的 start()
和 stop()
方法。
接下来让我们仔细分析一下 start()
方法。在 start()
方法中,首先获得 ServiceTracker
类的引用。您可以将此类视为工厂类。然后通过调用此 getService
方法从此工厂类获取服务引用。ServiceTracker
使用以下参数构造:包上下文和 OrderService
类的名称。
清单 2. OrderService 实现
public class OrderServiceImpl implements OrderService, BundleActivator { private ServiceRegistration registration; public void start(BundleContext context) { registration = context.registerService(OrderService.class.getName(), this, null); System.out.println("Order Service registered"); } public void stop(BundleContext context) { System.out.println("Order Service stopped"); } public void processOrder() { System.out.println("Order id: ORD123") ; } }
服务器端 order.jar 文件包含两个组件:OrderService
接口和 OrderServiceImpl
类。此接口具有由 OrderServiceImpl
类实现的抽象类 processOrder
。此类还实现了 BundleActivator
接口,此接口供 Felix 容器调用来启动和停止 order.jar 包。start()
方法在注册中心注册 OrderService
组件,以供客户端包使用。注册与导出对象供其他包使用的作用一样。
--
通过 Manifest 通信
真正的问题是,客户端包如何知道注册的服务包?此通信通过使用 Manifest 文件处理。在 Manifest 文件中,在导入和导出程序包中提供包的引用。客户端包 Manifest 通常导入服务组件程序包,而服务包 Manifest 导出自己的程序包。请注意,当包 A 导入包 B 的程序包时,包 B 必须导出自己的程序包。没有恰当的导入和导出定义,通信将失败。
清单 3. 客户端清单文件
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Order Service Client Bundle-SymbolicName: orderclient //模块代号名称 Bundle-Version: 1.0.0 Bundle-Activator: order.client.OrderClient //激活器类的名称,激活器类负责在包中调用 start() 和 stop() 方法 Import-Package: org.osgi.framework, org.osgi.util.tracker, order //导入的包 order是OrderService到处的别名
清单 4. 服务器清单文件
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Order Service Bundle-SymbolicName: orderservice Bundle-Version: 1.0.0 Export-Package: order //导出的包名,客户端引入此包 Bundle-Activator: order.impl.OrderServiceImpl //激活器类的名称,激活器类负责在包中调用 start() 和 stop() 方法 Import-Package: org.osgi.framework //导入的包
对于订单应用程序,client.jar 包 Manifest 中包含条目 Import-Package: org.osgi.framework, org.osgi.util.tracker, order
。这个实际上表示,客户端包导入核心 OSGi 程序包和 OrderService
程序包。类似地,order.jar 包清单包含条目 Export-Package: order
。即,包导出其程序包供客户端使用。如果导入和导出未显式声明,OSGi 将引发运行时错误。
Manifest 文件还包含其他信息,如包激活器类的名称等。激活器类负责在包中调用 start()
和 stop()
方法。在本例中,client.jar 包的激活器类为 OrderClient
,order.jar 包的激活器类为 OrderService
。
--
部署
在部署和使用包前,请进行以下工作:
下一步便是部署这些包。请通过以下步骤,使用 Felix OSGi 容器部署客户端和服务包:
下一步便是部署这些包。请通过以下步骤,使用 Felix OSGi 容器部署客户端和服务包:
执行了上述构建文件后,将分别在 client/bin 和 service/bin 文件夹中创建 client.jar 和 order.jar 包。
每次启动 Felix 运行时,都会提示配置文件名称。配置文件名称的作用类似于项目名称。可以提供任何名称作为项目配置文件的名称。对于 Felix 运行时的每次后续启动,如果提供了之前输入的相同配置文件名称,Felix 将会加载与该项目名称关联的所有已安装包。如果提供了新配置名称,则将需要再次显式安装各个包:
install file:service/bin/order.jar
install file: client/bin/client.jar
start service_bundle_id
start client_bundle_id
为了指示包的 ID,可以使用 ps
命令。必须首先启动服务包,然后启动客户端包。在启动相应的包时,将显示以下输出(请参见图 2)。
还可以通过在 Felix Shell 提供 update bundle_id
命令更新包。包将在运行时动态更新。