使用MVC样式的Web体系结构:第2部分
--构建Web服务MVC体系结构
将任何 MVC 模式的实现转换为 Web
服务
Naveen Balani (
naveen_balani@syntelinc.com)
技术分析员,Syntel
India, Ltd.
2002 年 4 月
本文是一篇由两部分组成的系列文章的第二部分,着重描述如何使用任何模型-视图-控制器(Model-View-Controller,MVC)模式来实现
Web 服务体系结构。本文将着重描述如何使用 SOAP 和 UDDI API 来访问静态和动态 Web 服务,以及组成 Web
服务体系结构的组件的设计实现。
在本系列前一篇文章中,您已经构建了一个已有的 MVC 模式;现在您就可以在这个模式的基础上应用 Web
服务体系结构了。如下面的图 1 所示,需要实现的关键组件是静态服务导航文件、SOAP 客户机、UDDI 客户机、服务实用程序和与 MVC
模式的交互。

图 1. Web 服务体系结构的关键组件
为了提供实现,我设计了一个应用 Web 服务体系结构的 MVC 模式样本。我将提供一个建立在图 2
中描述的体系结构之上的实现。该体系结构是用静态服务映射导航文件来动态调用的。这个实现主要向您展示如何使用各种 API 来调用 Web
服务,以及如何将这种调用应用到已有的 MVC 框架中去。
图 2. Web 服务体系结构样本

实现服务管理器
服务管理器是与客户机进行交互的
servlet。它将创建服务控制器的实例,并将请求/响应参数委托给该实例。在初始化过程中,它将装入静态服务导航文件,并植入 Navigator
类。
实现 Navigator 类
Navigator
类是一个静态类,它负责解析静态导航服务文件。下面的清单展示了该文件的一部分:
formbean="packagename.className"
staticrequestHandler="packagename.className"
dynamicrequestHandler="packagename.className"
serviceimplemenation1="http://airservice.com/rrpcrouter"
serviceimplemenation1urn1="urn:fare"
methodName1
="getRate"
serviceimplemenationN= " " . serviceimplemenation1urnN=" "
methodNameN="getCheapestRate"
dynamicLookup="Yes">
Navigator 类将解析导航文件,然后根据 serviceCount 植入大小为 N
的一个向量,该向量为服务对象计数。对象服务类由三个变量和这些变量的访问器组成,这些变量是
serviceimplemenation、serviceimplemenationurn 和 methodName。Navigator 类还植入
formbean、包名称、serviceCount 和
dynamicLookup,并向同一个向量添加这些变量。最后,向量对象被放入一个散列表,其中服务类型作为键(cheapestAirFare),向量作为对象。
因此,Navigator 类有下面几种方法:
ParseNavigationFile(pathofNavigationFile):解析导航文件并植入到散列表。
get/set ServicesCount(serviceType):获取与 serviceType 关联的
servicesCount(已注册的静态服务数)。每个变量的 set 方法都是在解析过程中设置的。
get/set
formBean(serviceType):获取与 serviceType 关联的 formBean。
get/set
staticRequestHandler(serviceType):获取与静态服务的 serviceType 关联的 RequestHandler 类。
get/set dynamicRequestHandler(serviceType):获取与动态服务的 serviceType 关联的
RequestHandler 类。
get
services(serviceType,count):根据向量存储的元素从向量获取对应的服务对象。首先,根据 serviceType
从散列表获取对应的向量对象;然后,使用 count 作为向量索引获取对应的服务对象。
get/set
dynamicLookUp(serviceType):获取与 serviceType 关联的 dynamicLookUp 条目。
所有上述的
setter 方法都是在解析静态服务映射导航文件时植入的,而 getter 方法是由动态控制整个流程的服务控制器调用的。
实现 ServicesController
ServicesController
类充当应用程序控制器,它将进入的客户机请求路由到合适的请求处理程序和 formbean。这些处理程序动态或静态地与 SOAP 服务交互,并通过
JavaServer Pages 协调视图的生成。控制器子系统负责:
根据 Navigator 类调用合适的 formbean。
根据 Navigator
类将进入的客户机请求路由到合适的请求处理程序代码。
通过加强来自请求处理程序的响应为表示层提供所需的模型数据。
以统一的方式处理异常情况。
让我们一步一步地研究服务控制器的逻辑。
1.从 JavaServer Page 获取服务类型。
serviceType =
request.getParameter(serviceType);
2.调用对应的 formbean。 IFormBean
是一个包含
processClientRequest(HttpServletRequestrequest) 方法的 Java 接口类,所有的 formbean 都要继承它。
IFormBean IformBean = (IFormBean)
class.forname(Navigator.getformBean(serviceType));
3.调用 formbean
processClientRequest(HttpServletRequestrequest) 方法。
IformBean.processClientRequest(HttpServletRequest request)
4. 从 Navigator 类获取 servicesCount。
int Count = Navigator.getServicesCount(serviceType)
5.创建一个临时存储空间(即 Vector storeResults)来存储所有从 requestHandlers收到的结果。
6.为每个服务计数调用请求处理程序。IStaticRequestHandler 是一个包含 processRequest(Services
service,IFormBean iformBean) 方法的 Java 接口类,所有 RequestHandler 都要继承它。
while(count >
0)
{
IStaticRequestHandler requestHandler =
(IStaticRequestHandler)
class.forname(Navigator.getstaticRequestHandler(serviceType);
7.根据 Navigator 类中的计数和服务类型获取 Services
对象。
Services services = Navigator.getservices(serviceType,count);
8.调用 requestHandler
processRequest(Services service) 方法并向其传送 services 对象。
String tempdata =
requestHandler.processRequest(services,IformBean)
9.存储在 storeResults
向量中获得的结果。
storeResults.addElement(tempdata);
10.减少计数并终止 while 循环。现在您就得到所有由
storeResults 向量中的 requestHandler 调用的静态服务了。
11.请检查 Navigator 类是否启用了
DynamicLookup。
boolean flag =
Navigator.getdynamicLookUp(serviceType)
12.如果它已经被启用了,那么请从 Navigator 类查找动态请求处理程序类。IDynamicRequestHandler
是包含 processRequest(String serviceType,IFormBean IformBean) 方法的 Java
接口类,所有动态请求处理程序都要继承这个类。
IDynamicRequestHandler dynamicrequestHandler =
(IDynamicRequestHandler)
class.forname(Navigator.getdynamicRequestHandler(serviceType);
13.调用 processRequest(serviceType) 方法,并取回通过调用在 UDDI 注册中心查找到的 SOAP
服务获得的数据。
Vector dynamic =
dynamicrequestHandler.processRequest(serviceType,IformBean)
14.将向量(静态和动态的)放在 HttpRequest 属性中,然后根据 serviceType 调用合适的 JSP。对应的
JSP 页将从 HTTP requestattribute 获取数据,然后向用户显示这些数据。
getServletContext().getRequestDispatcher
("/servicetype.jsp").forward(request,response);
实现 FormBean
FormBean 从 HttpServlet
请求参数检索用户输入的查询。 如上面逻辑的步骤 2 所示,服务控制器引用 Navigator 类的 getformBean(serviceType)
方法来实例化对应的 formBean。在得到 formBean 实例之后,processClientRequest() 方法将被执行,它将植入来自 HTTP
请求参数的用户查询。 举例来说,对于 cheapestAirFare 服务,cheapestAirFare 类的
processClientRequest(HttpServletRequsr request) 方法可能是:
String country1 =
request.getParameter("Country1");
String country2 = request.getParameter("Country2");
静态
RequestHandler 的实现
如上面清单的步骤 8 所示,静态请求处理程序的执行方法将接受服务对象和 formBean
作为输入参数。每个请求处理程序的工作就是调用 SOAP 客户机,传送服务对象方法,然后取回结果,如下所示:
String result =
SOAPClient.process(services.getServiceImplemenation
(),services.getServiceImplemenationUrn(),services.getMethodName,IformBean);
SOAP
客户机的实现
每个 SOAP 客户机的工作就是向 SOAP 服务器发送查询,然后取回该查询的结果。图 3 展示了 SOAP
客户机是如何与 SOAP 服务器通讯的。 所有所需的参数,如 methodName、targetObject 和服务的位置,都是从 requestHandler
获得的(通过 Navigator 类)。

图 3. 与 SOAP 服务器通讯的 SOAP 客户机
下面是静态客户机的一些逻辑:
1.创建并初始化一个新的 org.apache.soap.rpc.Call 对象。
Call call =
new Call();
2.设置第一个服务的目标 URN、方法名和参数。
call.setTargetObjectURI(serviceUrn
);
call.setMethodName(methodName );
3.植入将被传送到 SOAP 服务器的参数。
Vector params
=new Vector();
Parameter country1Param = new Parameter(
"country1",String.class,country1,
,Constants.NS_URI_SOAP_ENC);
params.addElement(formBean.getCountry1());
Parameter
country2Param = new Parameter(
"country2",String.class,country2,Constants.NS_URI_SOAP_ENC);
params.addElement(formBean.getCountry2());
call.setParams(params);
4.设置第一个服务的目标 URL。
Response resp =null;
URL url =new
URL (serviceImplementation);
5.调用对应的服务。
Resp =call.invoke(serviceImplementation,
serviceUrn);
Parameter result=resp.getReturnValue();
6.从 SOAP 服务器获取结果对象。
String
farerate = (String ) result.getValue();
实现动态请求处理程序
每个动态请求处理程序的工作就是调用 UDDI 客户机的
processRequest(serviceType,IformBean) 方法,向其传送 serviceType 和
formBean,然后取回结果:
//Call The UDDI
Client
Vector results =
UDDIClient.processDyanmicRequest(serviceType,IformBean);
实现 UDDIClient
UDDIClient 与 UDDI
注册中心通讯,确定特定的 serviceType 是否可用。如图 4 所示,它使用 UDDI API 与 UDDI 注册中心进行交互。

图 4. 通过 UDDI API 与 UDDI 注册中心交互的
serviceType
下面的清单包含使用 UDDI API 在 UDDI 目录中搜索特定 serviceType 的逻辑。一旦您有了这个
serviceType,您就可以用它来获取服务的 location(acessPoints) 和与该服务关联的 WSDL 文档。
1.创建 UDDI 代理的实例。
UDDIProxy proxy
=newUDDIProxy();
2.您需要一个位置向量来存储从 UDDI 获得的每个服务的位置。
Vector locationVecor =
new Vector(1,1)
3.您还需要一个 WSDL 位置向量来存储从 UDDI 获得的每个服务的 WSDL 文档位置。
Vector
wsdlVector = new Vector(1,1)
4.您需要一个结果向量来存储通过调用从 UDDI 获得的服务而获得的结果。
Vector
servicesVector = new Vector(1,1)
5.设置查询并发布 API。
String inquiryAPI
="http:/ibm.com/services/uddi/publishap";
String publishAPI
="http:/ibm.com/services/uddi/publishapi";
proxy.setInquiryURL(inquiryAPI);
proxy.setPublishURL(publishAPI);
6.查找与 serviceType 关联的业务实体。
BusinessList businessList =
proxy.find_business(serviceType,null,0);
BusinessInfos bis
=businessList.getBusinessInfos();
Vector v2
=bis.getBusinessInfoVector();
7.查找与业务实体关联的商业服务。举例来说,查找所有提供 cheapestAirFare 服务的服务提供者。
for (int i2 = 0;i2 < v2.size();i2++){
BusinessInfo businessInfo =
(BusinessInfo)v2.elementAt(i2);
ServiceInfos serviceInfos
= businessInfo.getServiceInfos();
Vector v3
=serviceInfos.getServiceInfoVector();
8.查找与每个服务关联的绑定细节。
for (int i3 = 0;i3 <
v3.size();i3++){
ServiceInfo si
=(ServiceInfo)v3.elementAt(i3);
String skey
=si.getServiceKey();
BindingDetail bindingDetail =
proxy.find_binding(null,skey,null,0);
Vector v4
=bindingDetail.getBindingTemplateVector();
for (int i4 = 0;i4 <
v4.size();i4++){
BindingTemplate bindingTemplate =(BindingTemplate)v4.elementAt(i4);
9.从绑定模板 AccessPoint 收集访问点。
accessPoint =
bindingTemplate.getAccessPoint();
10.将访问点添加到位置向量中。
locationVecor.addElement(accessPoint.getText());
11.获取每个服务的 WSDL 文档。
TModelInstanceDetails tid
=bt.getTModelInstanceDetails();
Vector v5
=tid.getTModelInstanceInfoVector();
for (int i5 = 0;i5 <
v5.size();i5++){
TModelInstanceInfo tii =
TModelInstanceInfo)v5.elementAt(i5);
InstanceDetails
inst =tii.getInstanceDetails();
OverviewDoc od
=inst.getOverviewDoc();
OverviewURL ou
=od.getOverviewURL();
wsdlVector.addElement(ou.getText())); }}}}
12.调用动态服务实用程序方法,从而提供对要调用的正确的服务实用程序类的映射。
for(int int i5
=0;i5 {
IServiceUtility
serviceUtility = (IServiceUtility)
DynamicServiceReistry.locate(locationVecor.elmentat(i5),wsdlVector(i5));
String result=
serviceUtility.processDynamicRequest(formbean);
servicesVector.addElement(i5,result);
}
//Return servicesVector
实现
DynamicServiceRegistry
DynamicServiceRegistry 类是映射器类,它根据服务的位置和
WSDL 名提供要调用的正确的 serviceUtility。下面的清单向我们展示了代码是如何工作的。
public IServiceUtility locate(String location,String wsdl)
{
if(location.equals("http://location1/service/rpcrouter") &&
wsdl.equals("http://location/wsdl/cheapest-airface.wsdl"))
//Invoke Corresponding service utility
class
IServiceUtility serviceUtility =
(IServiceUtility)
class.forname("classname.packagename"+dynamicService1));
//Similarly for remaining services
else
// log all new services
found to provide implementation of newly services
obtained
// at a later
stage
{
Log.newService('New Service Found At' +location + "WSDL Document " +
wsdl);
}
实现 ServicesUtility
每个服务实用程序类都继承
IServiceUtility 类并实现 processDynamicRequest(formBean)。
获得的每个动态服务都将调用对应的服务实用程序类。因为每个服务实用程序类所需调用的 URN
名和方法名都将是唯一的,所以它们都有其自己的实现。对于已经被添加的新的动态服务来说,您可以使用诸如 WebSphere Application
Developer Studio 这样的工具,它可以根据获得的 WSDL 文件动态生成 SOAP 客户机;或者,您也可以通过引用 WSDL
文档(它将包含要调用的方法名和 Web 服务的 URN)编写 SOAP 客户机代码。在发现新添加的服务之后,您就可以更新
IDynamicServiceRegistry 类从而为新发现的服务添加条目,并对相应的服务实用程序类进行编码了。这样,serviceUtility
类将为每个服务的调用动态 SOAP 客户机;动态客户机执行与上面讨论的静态 SOAP 客户机相同的工作,然后向请求处理程序返回结果。
结束语
这样,该体系结构的样本实现就结束了。根据您的 MVC
模式,实现情况会有所不同,但决定如何同时访问静态和动态服务的逻辑是一样的。我希望这里描述的样本会对您构建自己的实现有所帮助。
如果收到您关于本文的来信,我将非常感兴趣。请随时通过 naveen_balani@syntelinc.com 与我联系。
参考资料
请单击本文顶部或底部的讨论参加关于本文的讨论论坛。
要了解更多关于 Web 服务的信息,请参阅Web services architectural overview。
developerWorks 的系列文章“Web
services architect”将为您建立 Web 服务技巧的坚实基础。
要了解更多关于
SOAP 的信息,请参阅 SOAP
规范。
您可能还会对 developerWorks 的文章“Myths
and misunderstandings surrounding SOAP”感兴趣。
UDDI.org
技术白皮书中也有很多关于 UDDI 的信息(PDF 格式)。
developerWorks 的一个系列文章中将描述 The role of private UDDI nodes in Web services,这里是该系列的第一部分。
请查看 Uniform
Resource Names (URN) 宪章。
MVC
模式用于分开接口和逻辑,以及分开方法和这两者。它原本来自 Smalltalk,但在其它很多地方都有用,Steve Burbeck 在这篇文章中说明了这一点。
在
MVC 上有更多信息。
如果您熟悉 Java Swing,那么您就已经熟悉 MVC 了。
MVC 中与
Observer 连接有关的常见问题引发了对另一种模式的开发 — 模型-视图-表示器(Model-View-Presenter,MVP)。
MVP
是连接 IBM 的主要面向对象语言环境的线程。
WebSphere 应用程序服务器(WebSphere Application Server)使用
MVP 和 MVC 两种模式。
Patterns Home Page 有关于软件模式和模式语言所有方面的信息。
Wiki 也有很多关于模式的信息。
关于模式的经典之作是 A Pattern
Language: Towns, Buildings, Construction,Christopher Alexander
等人所著。(牛津大学出版社,1977 年)。
关于作者
Naveen Balani 目前是 Syntel India,
Ltd. 的一名技术分析员。他现在的工作范围包括设计、开发和实现基于 J2EE 的产品。您可以通过 naveen_balani@syntelinc.com
与他联系。
浏览: 使用MVC样式的Web体系结构:第1部分
如果您希望与本文章的作者或其所在机构,进一步交流,请联系:畅享网 姜小姐
jill.jiang@amt.com.cn | 021-51096826-112 |
在线联系