http://230996.blog.chinajavaworld.com/entry/3719/0/
欢迎回来EclipseZone“OSGi入门”系列讲座。进入到今天这节课之前我希望你能找到所有讲座以前的部分在我的个人博客上。
上次我们首次接触了Declarative Service。这次我们将看看Declarative Service的消费者那边。记得以前我们注册了一个服务在java.lang.Runnable接口下;现在我们将创建一个组件依赖在这个服务上。
正如讨论的那样,Declarative Services规范是所有关于让你聚焦于你的代码的应用逻辑上,而不是在以前的课程中写的OSGi“glue”代码。鉴于此,我们只是潜入到代码中,但是因此我们需要创建一个项目。以下是最后课程中同样的步骤,但是使用“SimpleImporter”作为项目名称。
现在从你的浏览器复制以下代码并粘帖到新建的Eclipse工程中的src文件夹里面:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
package org.example.ds;
import org.eclipse.osgi.framework.console.CommandInterpreter;
import org.eclipse.osgi.framework.console.CommandProvider;
public class SampleCommandProvider1 implements CommandProvider {
private Runnable runnable;
public synchronized void setRunnable(Runnable r) {
runnable = r;
}
public synchronized void unsetRunnable(Runnable r) {
runnable = null;
}
public synchronized void _run(CommandInterpreter ci) {
if(runnable != null) {
runnable.run();
} else {
ci.println("Error, no Runnable available");
}
}
public String getHelp() {
return "\trun - execute a Runnable service";
}
}
|
这个类实现了CommandProvider接口,这是个用来扩展当你运行Equinox的时候在“osgi>”提示符中有效的命令集合。写一个CommandProvider的原因是用来提供一个方便我们测试代码互动性的方法。在IBM developerWork上的Chris Aniszczyk的文章中有更多命令提供器的详细讨论。
注意我们在这个类中没有调用任何OSGi API,事实上我们甚至无需从org.osgi.*包导入任何东西。我们依赖的这个服务——在这个情况中,一个java.lang.Runnable的实例——我们通过setRunnable方法提供使用并使用unsetRunnable方法带走它。我们可以认为这是一种依赖注入的形式。
其他的两个方法,getHelp和_run是为命令提供器实现的。没错,“_run”是个有趣的名称,它以下划线开头,但是那仅仅是一个Equinox控制台API的奇怪的特性,并且OSGi或者Declarative Service什么都不做。使用下划线开头模式的方法在Equinox控制台中变成命令,所以通过提供一个叫做_run的方法我们添加了一个命令“run”。另外注意的事情关于这个类我们需要关心确保runnable字段能够以线程安全的方式被更新和访问。线程安全在OSGi中 相当重要,因为它本质上就是多线程的。坦率的说,我们始终应该把我们的代码写成线程安全的。
如以前一样,我们需要提供一个XML文件包含DS声明得到这些。复制以下到OSGI-INF/commandprovider1.xml到你的plug-in项目中:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<?xml version="1.0" encoding="UTF-8"?>
<component name="commandprovider1">
<implementation class="org.example.ds.SampleCommandProvider1"/>
<service>
<provide interface="org.eclipse.osgi.framework.console.CommandProvider"/>
</service>
<reference name="RUNNABLE"
interface="java.lang.Runnable"
bind="setRunnable"
unbind="unsetRunnable"
cardinality="0..1"
policy="dynamic"/>
</component>
|
这是我们上次叙述疏忽了的另一个重要的步骤。(感谢Seamus Venasse指出来。)你需要编辑build.properties文件到在你的plug-in项目中,并且检查OSGI-INF文件夹是否选中。当Bundle使用Eclipse的导出向导导出时必须保证文件夹被包含在里面,或者使用PDE Build来建立。
那么我们需要添加下面一行到Bundle的manifest中:
1
|
Service-Component: OSGI-INF/commandprovider1.xml
|
这个定义有我们之前看过的同样的两个元素,implementation和service节点。implementation节点提供实现组件的类的名称,service节点告诉DS像一个服务那样注册这个组件。这种情况下我们注册到CommandProvider接口下,这个是我们如何让Equinox控制台获知关于我们的命令提供器的存在。
下个节点叫做reference,是我们以前没有见过的——它对DS声明了我们的组件已经依赖在一个服务上。name属性只是一个依赖的名称的随意的字符串(我们仍然不需要担心这个是用来干什么的)并且interface属性指定我们依赖的接口的名称。bind属性是当一个服务有效时将调用实现的类中的一个方法名称,换句话说,当一个Runnable服务用Service Registry注册了,DS将获取一个此新服务的引用并提供使用这个指定的方法提供给我们的组件。同样的unbind属性是当我们使用的一个服务变成无效的时候通过DS调用它。
Cardinality属性显示了DS真正的强大。这个属性控制是否这个依赖是可选的或是必需的,并且是否它是单独的或多个的。可能的值有:
引用原文:
? 0..1:可选和单个,“0或1”
? 1..1:有且仅有一个,“只有一个”
? 0..n:可选和多个,“0到多”
? 1..n:必须或者多个,“0到多”或“至少一个”
在这个例子中我们选择可选和单个,意味着我们的命令提供器能够应付依赖服务变成无效的情况。回来看看_run方法的代码,你可以看到它必须处理检查到null的情况。
让我们看看如果我们运行了这个Bundle会发生什么。如果你的SimpleExporterBundle从上次一直存在,当你在命令行提示符osgi>输入“run”你将看到如下响应:
1
|
Hello from SampleRunnable
|
太好了!这次确定了我们成功的导入了上节课我们写的Runnable服务。现在试着使用“stop”命令关闭SampleExporter的Bundle。当你再次输入“run”命令,你将看到:
1
|
Error, no Runnable available
|
DS通知的意思是Runnable服务消失了,并且调用了我们的unsetRunnable方法让我们知道。
再看看cardinality属性,如果我们改变成“1..1”你认为将会发生什么,例如从一个可选到必须的依赖切换过来?试着换下并重启动Equinox。如果SampleExporter的Bundle是激活的当我们输入“run”之后我们将看到和之前一样的信息“Hello from SampleRunnable”。但是,如果SampleExporter是未激活的之后我们将看到一个与之前非常不同的错误信息。事实上,我们将看到Equinox的控制台帮助信息,那是对于未被识别的命令的标准响应。这表示我们的命令提供其它自己已经通过DS注销了!直到组件有一个必须的依赖前都不会满足,DS只能被迫撤消它提供的任何服务。反过来说Equinox控制台忘掉了“run”命令。
你可能会对policy属性感到奇怪,知道现在我仍然没有提及。这个值是“static”或“dynamic”中的一个,它指出是否这个组件能够应付服务的动态切换。如果不是动态的,那么每次目标服务改变了它必须为DS撤消组件并创建一个新的实例。正如你可能期望的,这相当于一个重量级的方法,最好编码你的组件来支持任何时候可能的动态切换。不幸的是默认值是static,所以你一定要记得明确的设置它为dynamic。
那么我们看过了可选单一和仅一个,但是什么是多重依赖呢?可能在Service Registry中存在多余一个的Runnable,并且如果我们只绑定它们中间的一个之后选择的那一个我们得到的是任意的。。或许我们喜欢实现一个“runall”命令来运行所有的当前注册了的Runnable。
如果我们改变cardinality为“0..n”的话将会发生什么?在某一方面它将总是运作:代替只调用一次setRunnable方法,DS将为每个Registry中的Runnable实例调用setRunnable一次。问题是我们写的类将会混乱。代替设置一个单独的Runnable字段,我们需要储存Runnable到一个集合。在这里类的版本有细小的改变,即将重新复制并粘贴到你的项目的src文件夹中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
package org.example.ds;
import java.util.*;
import org.eclipse.osgi.framework.console.CommandInterpreter;
import org.eclipse.osgi.framework.console.CommandProvider;
public class SampleCommandProvider2 implements CommandProvider {
private List<Runnable> runnables =
Collections.synchronizedList(new ArrayList<Runnable>());
public void addRunnable(Runnable r) {
runnables.add(r);
}
public void removeRunnable(Runnable r) {
runnables.remove(r);
}
public void _runall(CommandInterpreter ci) {
synchronized(runnables) {
for(Runnable r : runnables) {
r.run();
}
}
}
public String getHelp() {
return "\trunall - Run all registered Runnables";
}
}
|
现在创建OSGI-INF/commandprovider2.xml并复制以下内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<?xml version="1.0" encoding="UTF-8"?>
<component name="commandprovider2">
<implementation class="org.example.ds.SampleCommandProvider2"/>
<service>
<provide interface="org.eclipse.osgi.framework.console.CommandProvider"/>
</service>
<reference name="RUNNABLE"
interface="java.lang.Runnable"
bind="addRunnable"
unbind="removeRunnable"
cardinality="0..n"
policy="dynamic"/>
</component>
|
最终添加这个文件到manifest的Service-Component头去,看起来就像这样:
1
2
|
Service-Component: OSGI-INF/commandprovider1.xml,
OSGI-INF/commandprovider2.xml
|
这个声明同之前的一样不可缺少,除了我们对bind和unbind方法重命名并改变cardinality为“0..n”。就像练习一样,试着注册一些附加的Runnable服务并检查“runall”命令执行它们全部。下次,如果我们改变cardinality为“1..n”你认为会将会发生什么呢?尝试着验证你的想象。
分享到:
相关推荐
在OSGi框架中,服务层是位于应用程序之上的一层,它为服务提供者和消费者之间的交互提供了一种机制。这一层的主要目的是实现组件之间的松耦合通信,即组件不需要知道彼此的具体实现细节,仅通过服务接口进行通信。 ...
在本入门资料中,我们将探讨OSGI的关键概念、优势以及如何通过实战和最佳实践来掌握它。 1. OSGI原理: OSGI的核心在于它的模块系统,称为“bundle”。一个bundle是一个自包含的Java模块,包含了类、资源和元数据...
- **导出OSGi服务:**可以将普通的Spring Bean导出为OSGi服务,示例中`<osgi:service>`元素定义了服务的实现类和服务接口。 #### 五、导出OSGi服务 - **将普通Spring Bean导出为OSGi服务:**如上所示,通过`<osgi:...
OSGi(Open Services Gateway Initiative)是一个基于Java语言的服务平台,提供了一种动态化、模块化的应用程序架构。在OSGi架构中,整个生命周期管理是十分重要的组成部分,它保证了应用能够动态地进行安装、启动、...
在OSGi入门篇:模块层这篇文章中,作者静默虚空深入探讨了OSGi框架中模块层的基础知识以及设计模块层时OSGi联盟所做的考虑。OSGi模块层是框架中最基础的部分,它实现了Java的模块化特性,但又与Java现有的模块化特性...
OSGi(Open Services Gateway Initiative)是一种Java平台上的模块化服务框架,它定义了一种标准,使得开发者能够构建可互操作的、动态的、模块化的软件系统。OSGi的核心概念是基于Java的模块化,它的主要目标是为...
OSGi的入门资料,网上找的,初探OSGi 的全文
标题"OSGI入门和例子"意味着我们将探讨OSGI的基本概念以及如何通过实例来学习和理解这个框架。下面,我们将深入讨论OSGI的关键知识点: 1. **模块系统**:OSGI的核心是模块化,它定义了一种基于Java导出和导入包的...
2. OSGi:OSGi是一套用于Java平台的动态模块系统,它定义了一种标准的模块化体系结构,使得组件之间可以互相发现、加载和卸载,同时提供了服务注册和发现机制,增强了软件的可维护性和可扩展性。 3. Spring OSGi:...
标题中的“tomcat嵌入OSGI容器”是指在Apache Tomcat服务器中集成OSGI(Open Service Gateway Initiative)框架,使得Tomcat能够支持模块化的应用程序部署和管理。OSGI是一种Java平台上的服务导向架构,它允许动态地...
3. **使用Declarative Services(DS)**:OSGI DS提供了一种声明式的方式来管理OSGI服务的生命周期,使得Spring的bean可以与OSGI服务无缝集成。 4. **动态依赖注入**:由于OSGI的动态性,服务可以在运行时添加或...
OSGi(Open Service Gateway Initiative)是一种Java模块化系统,它为创建、部署和管理软件组件提供了一种强大而灵活的方式。在Java世界中,OSGi被视为解决大型复杂应用系统中模块化问题的有效解决方案。下面我们将...
**OSGi入门** OSGi(Open Service Gateway Initiative)是一个开放标准,它定义了一种模块化系统,用于在Java平台上构建动态、可管理的应用程序。这个技术最初的目标是为家庭网络设备提供服务网关,但随着时间的...
2. **Service Registry**:服务注册表是 OSGi 的关键组件之一,负责管理和存储服务对象的信息,使服务消费者能够查找并使用这些服务。 3. **Dynamic Import Package**:动态导入包机制允许 OSGi 束(bundle)在运行...
Spring OSGi 是一个将 Spring 框架与 OSGi(Open Service Gateway Initiative)容器相结合的开源项目,旨在提供一种在 OSGi 环境下使用 Spring 的方式。OSGi 是一种模块化系统,它允许开发人员创建可热部署、可升级...
<osgi:service ref="avgProcessor" interface="org.ccsoft.processor.DataProcessor"/> ``` #### 五、导出OSGi服务 1. **服务注册**: 可以通过Spring配置文件中的`<osgi:service>`元素将普通Spring Bean注册为...
5. **Blueprint或Declarative Services**:这两种是OSGi中的服务配置方式,Blueprint更接近XML,而Declarative Services使用注解,简化了服务的声明和管理。 6. **远程服务**:OSGi Remote Services允许Bundle之间...
### OSGi 入门教程(mini) 关键知识点概览 #### 1. OSGi 概述 - **定义**:OSGi (Open Service Gateway Initiative) 是一种用于构建模块化 Java 应用程序的标准框架。它允许将应用程序划分为独立的、可重用的模块,...