`
switchlau
  • 浏览: 54117 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
最近访客 更多访客>>
社区版块
存档分类
最新评论

(转) OSGi入门:Declarative Services和依赖[译]

阅读更多

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”你认为会将会发生什么呢?尝试着验证你的想象。

That's all for this lesson. Remember that the OSGi Alliance Community Event is happening next week in Munich, Germany, and I believe it's still not too late to register . See you there!

分享到:
评论

相关推荐

    OSGI入门和整合Spring

    学习OSGI入门和整合Spring,对于开发复杂的企业级应用,或者想要提升系统灵活性和可维护性的开发者来说,是非常有价值的。通过理解OSGI的模块化机制和Spring的依赖注入原理,可以构建出更加高效和可扩展的Java应用。

    OSGI资料,OSGI进阶,OSGI实战,OSGI入门和整合Spring

    《OSGI入门和整合Spring》则关注OSGI与Spring框架的结合,主要讨论: 1. **Spring与OSGI集成原理**:Spring的bean管理如何与OSGI服务机制相结合,实现更灵活的依赖注入。 2. **Declarative Services(DS)**:利用...

    spring-osgi 入门手册和代码

    - **Declarative Services (DS)**:DS 是一种声明式服务配置机制,它允许开发者通过元数据定义服务及其依赖关系,而无需编写额外的代码。 - **Blueprint Container**:Spring OSGi 提供了一个基于 Blueprint 的...

    OSGi 入门+进阶+实战

    5. **Blueprint或Declarative Services**:这两种是OSGi中的服务配置方式,Blueprint更接近XML,而Declarative Services使用注解,简化了服务的声明和管理。 6. **远程服务**:OSGi Remote Services允许Bundle之间...

    Spring OSGi 入门.pdf

    同时,了解如何与其他 OSGi 组件如 Blueprint 和 Declarative Services 结合使用,提升应用的灵活性和可维护性。 总结,Spring OSGi 为开发者提供了一种强大的工具,可以在模块化、动态的环境中构建和管理 Java ...

    Spring OSGI 快速入门中文教程

    - **Blueprint** 和 **Declarative Services** 是两种常见的Spring OSGi服务配置方式,它们都允许在OSGi中声明性地定义Bean和服务。 3. **Spring OSGi的应用场景** - **动态性**:OSGi环境下的Spring应用能更好地...

    OSGi Technology 教程

    最后,"Getting Started with OSGi 8 Declarative Services and Dependencies.doc"详细阐述了DS的用法和优势,包括如何声明服务组件、依赖关系以及处理组件间的交互。 通过这个系列的教程,开发者将全面了解OSGi...

    osgi相关文档、及学习资料

    可能会包括如何创建和打包OSGi Bundle、配置Manifest文件(包含Bundle的元数据)、使用Blueprint或Declarative Services进行服务声明、理解OSGi的生命周期管理以及如何在实际项目中应用OSGi技术。 **OSGi原理与最佳...

    tomcat-osgi压缩包

    这些bundle可以是第三方库、应用程序模块或者系统服务,通过声明式服务(Declarative Services,DS)或 Blueprint等配置机制,来实现服务间的依赖管理和通信。 在提供的压缩包文件名称“Tomcat-OSGi-QuickStart”中...

    osgi实战(pdf超请版)

    - **7.2 OSGI R4规范**:详细解析OSGI R4规范,包括核心框架(Core Framework)、启动级别服务(Start Level Service)、声明式服务(Declarative Services)、配置管理服务(Configuration Admin Service)、事件管理服务...

    OSGi传说beta1.1.pdf

    OSGi可以与Spring框架整合,利用Spring的强大功能进行依赖注入和配置管理。 **步骤**: 1. 添加Spring DM支持库。 2. 配置Spring Bean。 3. 注册服务。 #### 十、OSGi与Hibernate框架的整合 OSGi也可以与...

    konpflerfish konpflerfish tutorial

    Declarative Services 是一种更高级的方法,可以自动处理服务跟踪。创建一个名为 DSActivator.java 的文件,并添加以下内容: ```java package com.example.myfirstbundle; import ...

Global site tag (gtag.js) - Google Analytics