以 OSGi 包的形式开发和部署 Web 服务

部署和支持多版本 Web 服务的 SOA 策略

本文描述了把 Web 服务组件作为 OSGi 包进行开发和部署的详细方法。cxf-dosgi(Apache CXF 的分布式 OSGi 框架)将和 Eclipse 的 Equinox OSGi 框架一起用于开发和部署服务包。为了访问这个分布式的服务包,开发了一个简单的 Web 应用程序客户机。同时支持一个服务的多个版本是 Web 服务提供商经常面临的挑战。本文还展示了 OSGi 是如何提供一个干净、整洁的环境以满足这类需求。

Nalla Senthilnathan, IT 顾问, Greater Detroit

http://www.ibm.com/developerworks/i/p-nsenthilnathan.jpg自从 2000 年以来,Nalla 一直从事 Metro Detroit 地区的 IT 顾问工作。他为大型汽车制造商设计和开发了几个企业级 IT 项目。Nalla 拥有新加坡国立大学机械工程学的博士学位。



2010 年 3 月 29 日

简介

OSGi 是一个面向 Java 的动态模块系统。OSGi Alliance(请参见 参考资料)发布了模块系统的规范。一些受欢迎的 OSGi 容器包括 Eclipse Equinox(请参见 参考资料)和 Apache Felix 等等(请参见 参考资料)。作为一种用于开发和部署模块化的、可重用的 Java 程序的框架,OSGi 呈现出强劲的发展势头。

OSGi 容器允许以 Jar 格式部署 Java 模块(OSGi 将其称为 “包”)。OSGi 的一个有趣的功能就是能够把一个服务包的多个版本部署到同一个容器中。所有部署到 OSGi 容器中的包在一个 JVM 中运行。如果一个服务的客户端位于这个 OSGi 容器的外部,那么这个服务包需要有分布式功能。Apache cxf-dosgi(请参见 参考资料)是一个新的服务框架,支持用于 OSGi 包的分布式功能。

当 Web 服务提供商开发了一个新版本的服务时,通常需要继续支持现有的客户机。因此,Web 服务提供商需要同时部署和维护多个版本的服务。OSGi 自然成为满足这一需求的出色选择。

自从 CXF 团队发布了一个叫作 cxf-dosgi(支持对 OSGi 包进行分布)的新的框架以来,我选择 Eclipse Equinox 作为 OSGi 的容器,并选择 Apache CXF 作为 Web 服务框架。通过使用这个框架,我们可以把 Web 服务作为 OSGi 包进行开发和部署。由于一个包的多个版本能够共存,因此人们可以同时部署和维护多个版本的 Web 服务。我将把 Apache Tomcat 作为一个 servlet 容器使用,用于部署客户机。

在本文中,我将描述开发 cxf-dosgi 服务包的详细方法以及如何在 OSGi 容器中进行部署,并使用一个简单的 Web 客户机(和 OSGi 容器运行在不同的 JVM 中)对其进行访问。我还将描述开发同一个服务的不同版本并把它部署到同一个 OSGi 容器中,以及演示该服务的两个不同版本能够共存并为多个类型的客户机服务。


先决条件

首先下载并安装 Eclipse 3.5(Galileo)。Eclipse 3.5 包含了一个叫作 Equinox(请参见 参考资料)的 OSGi 框架

然后下载 cxf-dosgi 单包发行版和 osgi compendium 包。下载这两个包并保存到本地的同一个目录中。请参见下面的 参考资料,以获取下载链接。

下载并安装 Apache Tomcat 5.5.9。我们将使用在 OSGi 容器(Eclipse)外部的 servlet 容器,用来安装和运行我们的服务客户机。

准备 OSGi 容器

在开发一个分布式服务包之前,首先启动容器并把 cxf-dosgi 注册为服务提供商 enabler,从而为 OSGi 容器做准备。

使用一个空的工作空间启动 Eclipse 3.5。把 Perspective 设置为 “Plug-In Development”。一个 Eclipse 插件基本上就是一个 OSGi 包。

接下来使用菜单选项导入 cxf-dosgi osgi compendium 包以及 osgi compendium 包:

File/Import/Plug-In Development/Plug-ins and Fragments

然后选择下载包所在的目录。请参见 下面的图 1

图 1. 导入所需的包。指定目录位置
指定目录位置

单击 Next。在下一个对话框中,Eclipse 会显示已下载的包。请参见 下面的图 2

图 2. 导入所需的包,选择已下载的包
选择已下载的包

单击 Add AllFinish。Eclipse 会自动创建两个 Plug-In Development 项目,叫作 org.osgi.compendiumcxf-dosgi-ri-singlebundle-distribution。接下来我们需要把 osgi compendium 包作为所需的包指定到 dosgi 包中。双击 cxf-dosgi-ri-singlebundle-distribution 项目中的 META-INF/MANIFEST.MF 文件。当 Eclipse 在设计模式中打开清单文件时,选择 Dependencies 选项卡,然后添加 org.osgi.compendium 作为 “Required Plug-ins”。现在您的 Eclipse 环境应该如 图 3 所示。

图 3. 导入所需的包
所需的包

OSGi 容器现在已经为一些分布式服务部署做好了准备。


开发一个服务包

接下来,我们将使用一个方法创建一个基于 POJO 的简单 Web 服务,叫作 DictionaryService。这个方法就是 lookupWord(string),它能返回一个字符串(单词的含义)作为响应。

要在 Eclipse 中创建一个服务包,首先要确保 Perspective 被设置为 “Plug-in Development”。创建一个叫作 DictionaryService 的新 Plug-in 项目。在创建项目时,选择 com.demo.cxfdemo.Activator 作为包结构。您可以在附带包(cxf-dosgi-dw-article.zip 文件中的 DictionaryService_1.0.0.200908011529.jar)(请参见下面的 下载 链接)中找到 Activator.java, DictionaryService.java and DictionaryServiceImpl.java,使用它们替换由 Eclipse 创建的默认的 Activator.java。双击 DictionaryService 项目中的 META-INF/MANIFEST.MF。Eclipse 应该会显示清单文件的设计视图。单击 Dependencies 选项卡,清除 Required Plug-ins,然后在 Imported Packages 中添加 org.osgi.framework。您的 Eclipse 会如下面的 图 4 所示。

图 4. 创建一个服务包
服务包

Activator.java, DictionaryService.java and DictionaryServiceImpl.java 的源代码如 清单 1 所示。

清单 1. DictionaryService 包代码
package com.demo.cxfdosgi;

import java.util.Dictionary;
import java.util.Hashtable;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;

public class Activator implements BundleActivator {

    private ServiceRegistration registration;

    public void start(BundleContext bc) throws Exception {
	    Dictionary<String, String> props = new Hashtable<String, String>();
	    props.put("osgi.remote.interfaces", "*");
	    props.put("osgi.remote.configuration.type", "pojo");
	    props.put("osgi.remote.configuration.pojo.address", 
                  "http://localhost:9000/DictionaryService");    
	
	    registration = bc.registerService(DictionaryService.class.getName(), 
	                                      new DictionaryServiceImpl(), props);	
	}

    public void stop(BundleContext context) throws Exception {
        registration.unregister();
    }
}

package com.demo.cxfdosgi;

public interface DictionaryService {
	
	public String lookupWord(String word) throws Exception;

}

package com.demo.cxfdosgi;

public class DictionaryServiceImpl implements DictionaryService {

	public String lookupWord(String word) throws Exception {
		return word + " means...";
	}

}

我们的 Web 服务基本上就是一个 pojo(接口和实现)。Activator 类设置服务属性并注册服务。特别值得一提的是,DictionaryService 不必实现或扩展任何 cxf 类。我们甚至不必把 dosgi 单包设置为一个必需的插件。通过应用 cxf-dosgi,在执行 Run 配置时,它将被作为一个 cxf Web 服务进行部署。

部署服务包

下一步是部署和运行服务包。为此,我们需要创建一个 Run Configuration。右键单击 DictionaryService 项目,然后选择 Run As/Run Configurations。在名为 demo_dosgi 的 OSGi Framework 文件夹中创建一个新的配置。您的 Run Configuration 弹出对话框应该如 图 5 所示。

图 5. Run 配置
Run 配置

单击 ApplyRun。现在 Eclipse 会启动它内部的 OSGi 框架(Equinox),安装和启动工作空间的三个包。您的 Eclipse 环境现在应该如 图 6 所示。

图 6. 运行包
运行包

注意,Eclipse 不会自动显示 osgi> 提示。单击一下 Console 窗口会得到此提示。在这个提示中运行命令 ss(短暂状态),查看包的状态。要获得其他可用命令清单,在提示中运行 ?。上面的显示也可以确认服务端点 URL http://localhost:9000/DictionaryService 是否可用。还可以通过在浏览器中运行 URL http://localhost:9000/DictionaryService?wsdl 来确认这一点,现在这个浏览器应该显示 WSDL 内容。把这个 xml 文档作为 DictionaryService.wsdl 保存到一个临时文件夹中。在下一小节,我们将使用它生成一个客户机。


开发 Web 客户机以调用服务

要调用新部署的 Web 服务,现在让我们创建一个简单的 Web 应用程序客户机。在 Eclipse 中切换到 Java EE Perspective,创建一个叫作 DictionaryServiceClient 的新的 Dynamic Web Application 项目。把 DictionaryService.wsdl 复制到 WebContent 文件夹中。右键单击 DictionaryService.wsdl 文件,然后选择 Web Services/Generate Client。现在 Eclipse 应该已经在包名为 com.demo.cxfdosgi 的 src 文件夹中创建了一组客户机 Java 文件。现在我们创建一个 jsp 文件 dictionaryServiceClient.jsp,用来调用这个服务代理,如下面的 清单 2 所示:

清单 2. dictionaryServiceClient.jsp 代码
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1" 
import="com.demo.cxfdosgi.DictionaryServicePortTypeProxy"%>

<html>
<body>
<%
DictionaryServicePortTypeProxy proxy = new 
DictionaryServicePortTypeProxy();
out.println("?? DictionaryService response = 
"+proxy.lookupWord("whatisthis"));
%>
</body>
</html>

右键单击 DictionaryServiceClient 项目,然后导出一个 war 文件 DictionaryServiceClient.war。把这个文件复制到 C:/jakarta-tomcat-5.5.9/webapps 文件夹中。运行 C:/jakarta-tomcat-5.5.9/bin/startup.exe,然后访问 http://localhost:8082/DictionaryServiceClient/dictionaryServiceClient.jsp。浏览器将显示:

?? DictionaryService response = whatisthis means...

由于 Eclipse 和 Tomcat 在两个不同的 JVM 中运行,我们已经用一个分布式客户机测试了服务包。注意:把 Tomcat 的 HTTP 端口设置为 8082,这是因为 Eclipse 已经在使用 8080 部署 cxf Web 服务。


新版的服务包

把 Web 服务作为一个 OSGi 包进行部署的一个有趣功能就是可以同时部署一个 Web 服务的多个版本。现在让我们开发 DictionaryService 的下一个版本:DictionaryServiceV2。DictionaryServiceV2 的 lookupWord 方法能够返回两个字符串数组,一个表示单词的释义,另一个包含该词的同义词。由于我们修改了方法签名,显然已经破坏了现有客户机。通过把修改后的 Web 服务作为一个独立的包进行部署,我们使旧客户机能够继续运行,同时使新客户机能够使用服务的新版本。

在 Eclipse 中切换到 Plug-in Development Perspective,使用三个文件创建一个叫作 DictionaryServiceV2 的新的 Plug-in 项目。这三个文件分别是 Activator.java、DictionaryServiceV2.java 和 DictionaryServiceV2Impl.java,源代码如下所示:

清单 3. DictionaryServiceV2 包代码
package com.demo.cxfdosgi.v2;

import java.util.Dictionary;
import java.util.Hashtable;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;

public class Activator implements BundleActivator {

    private ServiceRegistration registration;

    public void start(BundleContext bc) throws Exception {
	    Dictionary<String, String> props = new Hashtable<String, String>();
	    props.put("osgi.remote.interfaces", "*");
	    props.put("osgi.remote.configuration.type", "pojo");
	    props.put("osgi.remote.configuration.pojo.address", 
                  "http://localhost:9000/DictionaryServiceV2");    
	
	    registration = bc.registerService(DictionaryServiceV2.class.getName(), 
	                                      new DictionaryServiceV2Impl(), props);	
	}
    
    public void stop(BundleContext context) throws Exception {
        registration.unregister();
    }
}

package com.demo.cxfdosgi.v2;

public interface DictionaryServiceV2 {
	
	/**
	 * @param word - String, whose meaning and synonyms are requested
	 * @return String[] - where String[0] has the word meaning and 
     *                          String[1] has synonyms
	 * @throws Exception
	 */
	public String[] lookupWord(String word) throws Exception;

}

package com.demo.cxfdosgi.v2;

public class DictionaryServiceV2Impl implements DictionaryServiceV2 {

	public String[] lookupWord(String word) throws Exception {
		// TODO Auto-generated method stub
		String[] result = new String[2];
		
		result[0] = word + " means...";
		result[1] = "Synonyms:...";
		
		return result;
	}

}

您的 Eclipse 窗口应该如 图 7 所示。

图 7. 创建 DictionaryServiceV2 包
DictionaryServiceV2 包

编辑 demo_dosgi Run 配置,确保配置中包含了 DictionaryServiceV2 包,如 图 8 所示。

图 8. 修改后的 Run 配置
Run 配置

单击 ApplyRun。现在 Eclipse 会重新启动 OSGi 框架,安装第四个包(DictionaryServiceV2),然后启动所有的四个包。现在您的 Eclipse 会如 图 9 所示。

图 9. 运行包
运行包

这将确认所有的四个包都已安装并且正在运行。日志消息也可以确定服务端点:

http://localhost:9000/DictionaryService

http://localhost:9000/DictionaryServiceV2

处于激活状态。在浏览器中访问第二个服务,如 http://localhost:9000/DictionaryServiceV2?wsdl,然后把显示的 wsdl 内容保存到文件 DictionaryServiceV2.wsdl 中。


调用新服务

要调用新版本的 Web 服务,我们需要生成一个新的客户机。创建一个新的项目 DictionaryServiceV2Client 作为一个 Dynamic web Application。使用与第一个版本相同的步骤复制 wsdl 并生成 Java 客户机。创建一个新的 jsp 文件 dictionaryServiceV2Client.jsp,如:

清单 4. dictionaryServiceV2Client.jsp 代码
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1" 
import="com.demo.cxfdosgi.v2.DictionaryServiceV2PortTypeProxy"%>

<html>
<body>
<%
DictionaryServiceV2PortTypeProxy proxy = new 
DictionaryServiceV2PortTypeProxy();
String[] resp = proxy.lookupWord("whatisthis");
out.println("?? DictionaryService response = "+resp[0]);
%>
<br/>
<%
out.println("?? DictionaryService response = "+resp[1]);
%>
</body>
</html>

把这个项目作为 DictionaryServiceV2Client.war 导出,然后把它安装到运行在 Eclipse 外部的 tomcat 服务器中。现在在浏览器中访问 http://localhost:8082/DictionaryServiceV2Client/dictionaryServiceV2Client.jsp 时应该显示为:

?? DictionaryService response = whatisthis means...  
?? DictionaryService response = Synonyms:...

现在我们已经成功地开发、部署和测试了一个 Web 服务的两个版本。


结束语

在本文中,我没有使用 pojo 服务包中的任何 cxf-dosgi API 类。然而,可以通过编辑 manifest.mf 文件(前面的 “导入包” 部分),导入指定包来使用 cxf-dosgi 类。注意,所有 J2SE 类本质上说都可用于一个包。但是,要从一个服务包中使用任何 JEE 服务(如发送邮件等),您需要通过编辑 manifest.mf 来导入具体的 JEE 包。Eclipse 提供了大部分可导入的 JEE 包。

我已经描述了把 Web 服务作为一个 OSGi 包进行开发和部署以及使用一个简单的 web 应用程序客户机调用服务的详细方法。我还讨论了 SOA 策略对于在一个整洁的 OSGi 容器环境中同时部署和支持一个服务的多个版本的好处。


下载

描述名字大小
用于本文的 Service 和 Client 包cxf-dosgi-dw-article.zip3,562KB

参考资料

学习

获得产品和技术

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=SOA and web services, Java technology
ArticleID=478123
ArticleTitle=以 OSGi 包的形式开发和部署 Web 服务
publish-date=03292010