级别: 初级 陈 云峰, IT 架构师, IBM GDC
2009 年 10 月 19 日 本文的目的在于通过使用 Axis2, Apache ActiveMQ 和简洁 POJO 代码作为例子对运行于 Axis2 的 Web 服务的开发与测试进行简单的说明,帮助开发者扫除目前为 Axis2 开发基于 JMS 的 Web 服务方面可能存在一些困难。
前言
Axis2 是一个轻便灵活的 SOAP 实现,常用于开发基于 HTTP 的 Web 服务,但是在许多情况下开发基于 JMS 的 Web 服务是一种更具吸引力的选择,例如需要事务支持,性能要求较高,高可靠性等。但是目前来说,开发基于 JMS 的 Web 服务可能存在一定的配置上的问题,使得开发不能顺利进行,Apache 文档也有描述上的不足,作者希望借本文来帮助开发者扫除这方面的困惑。本文将以 Apache ActiveMQ 为例来对 Axis2 的开发与测试进行简单的说明。同时通过以简洁 POJO 代码作为例子,希望使读者体会到以自底向上的方式开发基于 JMS 的 Web 服务的方便之处。希望读者可以对照本文和站点上其他 介绍 Axis 的文章 进行比较,了解其中的不同之处。
本文假定读者对基本的 Web 服务及 JMS 和 Ant 方面的基本知识具有一定的了解以便理解文章内容。另外,对基于 HTTP 的 Web 服务若有相当的了解也可起到加深理解的作用。
在本文中,作者从 Axis2 的架构出发,顺带说明基于 JMS 的 SOAP 的工作原理,然后将注意力集中在实现并测试一个基于 JMS 传输的 Web 服务(AccountService)上。AccountService 接收一些简单的请求,例如开户申请,查询余额等,然后给出回应。
文章的内容结构如下,读者可以跳跃到相关的部分阅读自己关心的内容,或直接下载附属的代码进行练习:
-
Axis2 架构的简单介绍
-
基于 JMS 的 SOAP 的工作原理
-
准备开发环境
-
开发 Web 服务 Java 类
-
产生 WSDL 和服务部署描述符
-
打包 Web 服务
-
创建客户端访问程序
-
运行 Web 服务及测试客户端
另外,本文假定读者是在 Windows 平台上进行相关的开发及调试的。
对 Axis2 架构的简单介绍
Axis2 是使用模块化的架构构建的,整体异常灵活并易于扩展。Axis2 核心包含了 Axis2 XML 对象模型 (AXIOM),它是一个基于 StAX 拉式处理的 XML 处理模型。
其他一些重要的模块包括:
-
信息模型 – 系统管理的各种信息处理状态的层次结构 ,
-
部署模型 – 用于部署 Web 服务及配置传输协议 ,
-
传输协议 – 支持常用传输协议并支持定制协议 ,
-
客户机编程接口 – 客户端访问 Web 服务所用 API.
-
代码生成 – 包括用于产生 WSDL 和 Java 代码的命令行工具和 Ant 任务定义,可以用于自底向上以及自顶向下的开发模式 ,
-
数据绑定 –扩展了基本的 XML 信息 (XML Info-set) 处理方式,支持集成使用 ADB(Axis Data Binding)和 JAXDB(Java API for XML Data Binding) 等。.
图 1. Axis2 架构
基于 JMS 的 SOAP 的工作原理
SOAP 是一个对于底层传输中立的消息协议,即 SOAP 消息可以传输于任何 Web 服务引擎 (Web Service Engine 或者 SOAP Engine) 支持的传输方式。例如,Axis2 服务引擎支持 HTTP,JMS, SMTP 等。本文把通过 JMS 发送 SOAP 消息以访问 Web 服务的过程称作基于 JMS 的 SOAP 传输(SOAP over JMS)。由于 JMS 很好的支持异步消息传输并能将消息持久化到数据库中,使得基于 JMS 的服务能应用在更多的场景之中。
图二以 Active MQ 和 Axis2 为例说明了基于 JMS 的 SOAP 的工作原理。在物理上,图中的 Axis2 引擎和 JMS 服务器 (JMS Provider) 可以都位于相同的机器,也可以位于不同的机器。
从服务端来看,Web 服务引擎使用特定传输协议支持 ( 此处 JMS) 和接收器 (Receiver) 获得到 JMS 服务器的连接,并接收消息。而 JMS 目标 (Destination) 可以是点对点的消息队列 (queue),或者是消息主题 (topic)。
通过客户端 API 来访问 Web 服务的普通应用程序或者其他 Web 服务必须提供包含了 JMS 目标的服务端点 URL(Endpoint Reference URL),正如通过 HTTP 访问 Web 服务必须提供 Web 服务的 URL 一样。所不同的是,JMS 服务器常常是运行于 Web 服务引擎的进程之外的,而 HTTP 协议支持是运行于 Web 服务引擎同一进程。
Axis2 支持同步 (Synchronous) 和异步 (Asynchronous) 的单向 (IN only) 和双向(IN-OUT)Web 服务访问,加上应答和请求 (request/response) 可能使用不同的传输协议,由此组合出来的消息交换过程 (message exchanging) 包含了很多应用场景。当请求和应答的消息交换模式变得复杂的时候,客户端可能需要对客户端 API 制定相应的寻址信息 (WS-Addressing)。
和其他许多 Web 服务引擎一样,对于 Axis2 而言,如果你在进行同步 JMS 双向调用,服务器会使用匿名的 JMS 目标 (temporary queue) 来存放应答消息内容并告知客户端发送对象 (sender)。当你发出一个异步请求并使用回调(Callback)对象来处理返回结果的时候,其机制也大致类似。但是如果你发出一个单向的异步请求而服务的操作必须返回一个应答的时候,你就必须已在 JMS 服务器中设定好一个应答的 JMS 目标供服务使用。
在实际的应用中,本文作者经历过一些例子,使用持久的 JMS 目标来存放服务的返回结果,然后用客户端 Web 服务或实现了 JMS MessageListener 的 Java 程序来处理返回结果,这种设计可以使得整个系统变得健壮、性能更好。
图 2. 基于 JMS 的 SOAP 工作原理
本文将介绍的例子是一个账户服务(AccountService),服务包含了开户,存款,和查询,客户端可以对它进行不同方式的调用。
在进入服务的开发之前,读者首先需要下载一些软件进行开发环境的准备。
准备开发环境
与本文相关的代码在 eclipse 及任何基于 eclipse 的 Java IDE 中都能编辑及进行测试。您需要预先准备 Java 1.5 环境并下载安装 eclipse 以便导入代码,并协同使用 Apache Ant 进行调试。
您需要从 Apache 的网站上下载 Axis2 1.4 标准版 (standard distribution) 及 Active MQ 5.2,然后解包安装到您方便的地方,笔者建议为安装目录取较浅较为短小的目录,如 d:\apps\Axis2 和 d:\apps\ActiveMQ。然后在系统中建立两个环境变量,AXIS2_HOME 和 ACTIVEMQ_HOME。
上述软件可通过点击相关链接进行进行下载使用,读者需要遵守的是相应开源软件的使用条款。
在例子当中我们需要为 Axis2 运行环境准备 Active MQ 的 JMS 相关的包,所以需要把 ACTIVEMQ_HOME/ lib 下相关包拷贝到 AXIS2_HOME/lib 下。或者如下面图 3 所示修改 AXIS2_HOME/bin/axis2Server.bat 文件的相应位置,使得服务器启动时自动加载 Active MQ 的相关 jar 包。
图 3. axis2server.bat
然后读者需要启动 Active MQ 并登陆到控制台中创建相关的 JMS 队列。两个队列的名字分别是 AccountService 和 AccountServiceResponse。参见下图。
图 4. Active MQ Web 控制台 (http://localhost:8161/admin)
最后是从 Axis2.xml 文件中恢复出与 JMS 相关的部分,由于 Axis2 的文件中默认使用了 Active MQ 的设置,所以,不需要改变其内容。
图 5. Axis2.xml
现在我们已经完成了开发所需要的设置。本文相关代码的结构图 5 所示。为了运行本文的例子,读者可能需要修改 Ant 脚本中的相关名称或路径,以适应您的本地配置。
图 6. 相关项目文件在 eclipse 里的结构
下面需要介绍的是 Java 的开发,WSDL 的生成以及服务部署和测试等。
Web 服务的 Java 类
这里的 Web 服务 Java 类是一个不实现任何接口的 POJO。 这是一个假想的 Account 服务类,它包含了三个操作,开户 (openAccount),存款 (deposit),和查询余额 (getBalance)。
现实当中的 Account 服务,可以设计成包含更多操作,例如转账等,这些留给读者去练习。我们的三个操作中的前两个即 openAccount 和 deposit 接收使用 RPC 风格的消息接收器,接收 Java Bean 或普通 Java 类型作为参数。第三个即 getBalance 使用 AXIOM XML 接收器,即接收 XML 元素作为参数 (Raw XML)。我在这里混合使用它们只是为了测试 Axis2 在实现和部署 Web 服务方面的灵活性。现实当中读者使用更多可能就是接收 Java bean 作为参数的 POJO。
参看清单 1,方法 openAccount 接受申请者的 ID 作为参数,这是一个字符串。方法 deposit 接受账号和数字作为参数。方法 getBalance 接受 OMElement 作为参数,OMElement 会包装账号 ID 等信息。
清单 1. AccountService 的部分代码
public String openAccount(String ownerId) throws AccountException {
try {
if (!checkOwnerId(ownerId)) {
log.error(Messages.M01);
throw new AccountException(Messages.M01);
}
if (!checkAccountByOwner(ownerId)) {
log.error(Messages.M02);
throw new AccountException(Messages.M02);
}
// We are going to create a new account.
// ID
String accountId = getId();
log.info("Creating account with ID =" + accountId);
// Key
AccountKey accKey = new AccountKey();
accKey.setAccountId(accountId);
// Account
Account accT = new Account();
accT.setAccountId(accountId);
accT.setOwnerId(ownerId);
// Store
saveAccount(accKey, accT);
// Response
return accountId;
} catch (Exception e) {
throw new AccountException(e);
}
}
|
生成 WSDL 和部署描述符
生成 WSDL 可以使用 Axis2 命令行工具 Java2WSDL 或 Ant 任务 Java2WSDLTask ,或者在 Ant 中调用命令行工具。本文作者使用倾向于 Ant 任务来产生 Account 服务的 WSDL,这样使得自动的部署和测试变得容易。
清单 2. 生成 WSDL 的 Ant 脚本
<target name="gen.account.wsdl" depends="compile">
<taskdef name="java2wsdl" classname="org.apache.ws.java2wsdl.Java2WSDLTask"
classpathref="axis2.classpath"/>
<java2wsdl className="com.ibm.workshop.axis2.services.AccountService"
outputLocation="${basedir}/resources/META-INF/"
targetNamespace="http://www.ibm.com/services/account"
targetNamespacePrefix="acnt"
schemaTargetNamespace="http://www.ibm.com/services/account/xsd"
schemaTargetNamespacePrefix="acntxsd">
<classpath refid="workshop.class.path"/>
</java2wsdl>
</target>
|
部署描述符“services.xml” 较为简单,可以从样板代码种拷贝过来作一些改动。在列表 3 中,我们为三个操作分别设置了接收器,它是一个服务器端模块,用于接收和解析 XML 消息并调用我们的 Account 服务实例。在文件的最后还包含了 JMS 目标的一些设置,这些设置和读者的 Axis2.xml 文件中的 JMS 设置相关联,它们是不可缺少的。
清单 3. 部署描述符的部分内容 (services.xml)
<serviceGroup>
<service name="AccountService" scope="application">
<transports>
<transport>jms</transport>
</transports>
<description>
Account Service
</description>
<operation name="openAccount">
<messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-out"
class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
</operation>
<operation name="deposit">
<messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-out"
class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
</operation>
<operation name="getBalance">
<messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-out"
class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/>
</operation>
<parameter name="ServiceClass"
>com.ibm.workshop.axis2.services.AccountService</parameter>
<parameter name="transport.jms.ConnectionFactory" locked="true"
>myQueueConnectionFactory</parameter>
<parameter name="transport.jms.Destination" locked="true"
>AccountService</parameter>
<parameter name="transport.jms.ReplyDestination"
locked="true">AccountServiceResponse</parameter>
</service>
</serviceGroup>
|

 |

|
对服务相关的 Java 类和各种 XML 文件进行打包
接下来我们将要把所有 Java 服务以及支持性文件打包到一个 Axis2 存档文件 (aar) 中,文件名为 accountServiceGroup.aar。如果读者打算实现并部署其他与 Account 有关的服务,也可以将他们打包到这个文件里。
部署描述符文件位于根目录下面的 META-INF 文件夹里面。虽然 Axis2 可以无需 WSDL 就可以运行 Web 服务,但是在命名空间上会出现问题,即系统会根据类结构来构造出一个默认的命名空间,但那通常是不符合要求的,导致客户端感到困惑,所以将 WSDL 一起打包是一个好的实践。WSDL 的位置也在 META-INF 下面。
清单 4. 用于打包服务的 Ant 脚本
<target name="gen.serviceGroup" depends="gen.account.wsdl">
<!--aar them up -->
<mkdir dir="${dist.dir}"/>
<copy toDir="${build.dir}/classes" failonerror="false" overwrite="true">
<fileset dir="${basedir}/resources">
<include name="**/*.xml"/>
<include name="**/*.wsdl"/>
</fileset>
</copy>
<jar destfile="${dist.dir}/accountServiceGroup.aar">
<fileset excludes="**/Test.class" dir="${build.dir}/classes"/>
</jar>
</target>
|
创建 Account Service 客户端
在 Axis2 中可以使用命令行工具(WSDL2Java)及 Ant 任务生成多种风格的服务访问绑定代码如 ADB, JAXB 等。简单起见,测试代码中的客户端采用 RPC 的方式来对服务进行访问。
AccountClient 是一个独立的 Java 应用程序,其中我使用了不同方法对异步和同步方式的单向和双向调用对服务进行了访问。读者可以针对更多的场景进行自己的练习。
对在 Axis2 中使用 JMS,需要比使用 HTTP 更多注意一些地方,例如协议的指定。需要初始化一个 JMS 发送器 (JMSSender) 实例供客户端 API 使用。
清单 5. Account client 部分代码
public void openAccountRPCCall(String ownerID) {
try {
RPCServiceClient serviceClient = new RPCServiceClient();
Options options = serviceClient.getOptions();
options.setTo(targetEPR);
TransportOutDescription tout = new TransportOutDescription(
Constants.TRANSPORT_JMS);
JMSSender jmsSender = new JMSSender();
tout.setSender(jmsSender);
options.setTransportOut(tout);
// Setting the operation
QName opOpenAccount = new QName(nameSpace, OP_OPEN_ACCOUNT);
Object[] opOpenAccountArgs = new Object[] { ownerID };
OMElement result = serviceClient.invokeBlocking(opOpenAccount,
opOpenAccountArgs);
log.info(">> Opening account Using RPC Call....");
String response = result.getFirstElement().getText().toString();
log.info(">> Returned Account Id: " + response);
} catch (Exception e) {
e.printStackTrace();
}
}
public static final String AccountServiceJMSEndpointURL = "jms:/AccountService?"
+ "transport.jms.ConnectionFactoryJNDIName=QueueConnectionFactory&"
+ "java.naming.factory.initial=
org.apache.activemq.jndi.ActiveMQInitialContextFactory&"
+ "java.naming.provider.url=tcp://localhost:61616&"
+ "java.naming.security.principal=system&"
+ "java.naming.security.credentials=manager";
|

 |

|
运行 Account 服务和测试客户端
在 Axis2 1.4 标准版中,本文的例子,采取较为简单的部署方式,直接将所开发的 Account 服务打包成 aar 文件放到 Axis2 根目录下的 services(AXIS2_HOME/repository/services)目录中。然后使用命令 axis2Server 启动 Axis2,并且在 eclipse 中启动测试客户端,将看到类似图 7 的两个控制台所示。
图 7. Eclipse 控制台 和 Axis2 控制台
在本文中我们配置了 Active MQ、Axis2 及 eclipse 开发环境,开发了一个使用 JMS 作为传输手段的 Web 服务并对其进行测试。读者可以通过本文体会到为 Axis2 开发基于 JMS 的 Web 服务的简单方便之处。
结束语
Axis2 支持多种传输协议,如 HTTP, SMTP,JMS 等,并提供扩展点 (extension point) 使得开发人员能够实现自己的传输机制。随着企业 Web 服务越来越多的应用需求,基于 JMS 的 Web 服务及其开发技能也越来越重要。希望这篇文章能帮助开发者们开始开发 JMS Web 服务。
下载 | 描述 | 名字 | 大小 | 下载方法 |
|---|
| 本文代码示例 | Axis2Jms.zip | 162 KB | HTTP |
|---|
参考资料 学习
获得产品和技术
-
下载 IBM 产品评估版
,并开始使用来自 DB2、Lotus、Rational、Tivoli 和 WebSphere® 的应用程序开发工具和中间件产品。
关于作者  | |  | 陈云峰,是位于上海的 IBM 全球服务中心的一名 IT 架构师。主要从事 J2EE, SOA/Web Services 应用开发 , 并对语义 Web,业务规则,和网格计算等有兴趣。2000 年毕业于成都电子科技大学。 |
对本文的评价
|