 | 级别: 初级 Naveen Balani (naveen_balani@syntelinc.com), 技术分析员, Syntel India, Ltd.
2002 年 4 月 01 日 本文是一篇由两部分组成的系列文章的第二部分,着重描述如何使用任何模型-视图-控制器(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 类是一个静态类,它负责解析静态导航服务文件。下面的清单展示了该文件的一部分:
<service type="CheapestAirFare" serviceCount="N"
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 类将进入的客户机请求路由到合适的请求处理程序代码。
- 通过加强来自请求处理程序的响应为表示层提供所需的模型数据。
- 以统一的方式处理异常情况。
让我们一步一步地研究服务控制器的逻辑。
- 从 JavaServer Page 获取服务类型。
serviceType = request.getParameter(serviceType);
|
- 调用对应的 formbean。
是一个包含
processClientRequest(HttpServletRequestrequest) 方法的 Java 接口类,所有的 formbean 都要继承它。
IFormBean IformBean = (IFormBean) class.forname(Navigator.getformBean(serviceType));
|
- 调用 formbean
processClientRequest(HttpServletRequestrequest) 方法。
IformBean.processClientRequest(HttpServletRequest request)
|
- 从
Navigator 类获取
servicesCount 。
int Count = Navigator.getServicesCount(serviceType)
|
- 创建一个临时存储空间(即
Vector storeResults )来存储所有从
收到的结果。
- 为每个服务计数调用请求处理程序。
IStaticRequestHandler 是一个包含
processRequest(Services service,IFormBean iformBean) 方法的 Java 接口类,所有
RequestHandler 都要继承它。
while(count > 0)
{
IStaticRequestHandler requestHandler = (IStaticRequestHandler)
class.forname(Navigator.getstaticRequestHandler(serviceType);
|
- 根据
Navigator 类中的计数和服务类型获取
Services 对象。
Services services = Navigator.getservices(serviceType,count);
|
- 调用
requestHandler processRequest(Services service) 方法并向其传送
services 对象。
String tempdata = requestHandler.processRequest(services,IformBean)
|
- 存储在
storeResults 向量中获得的结果。
storeResults.addElement(tempdata);
|
- 减少计数并终止
while 循环。现在您就得到所有由
storeResults 向量中的
requestHandler 调用的静态服务了。
- 请检查
Navigator 类是否启用了
DynamicLookup 。
boolean flag = Navigator.getdynamicLookUp(serviceType)
|
- 如果它已经被启用了,那么请从
Navigator 类查找动态请求处理程序类。
IDynamicRequestHandler 是包含
processRequest(String serviceType,IFormBean IformBean) 方法的 Java 接口类,所有动态请求处理程序都要继承这个类。
IDynamicRequestHandler dynamicrequestHandler = (IDynamicRequestHandler)
class.forname(Navigator.getdynamicRequestHandler(serviceType);
|
- 调用
processRequest(serviceType) 方法,并取回通过调用在 UDDI 注册中心查找到的 SOAP 服务获得的数据。
Vector dynamic = dynamicrequestHandler.processRequest(serviceType,IformBean)
|
- 将向量(静态和动态的)放在
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 客户机
下面是静态客户机的一些逻辑:
- 创建并初始化一个新的
org.apache.soap.rpc.Call 对象。
- 设置第一个服务的目标 URN、方法名和参数。
call.setTargetObjectURI(serviceUrn );
call.setMethodName(methodName );
|
- 植入将被传送到 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);
|
- 设置第一个服务的目标 URL。
Response resp =null;
URL url =new URL (serviceImplementation);
|
- 调用对应的服务。
Resp =call.invoke(serviceImplementation, serviceUrn);
Parameter result=resp.getReturnValue();
|
- 从 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 文档。
- 创建 UDDI 代理的实例。
UDDIProxy proxy =newUDDIProxy();
|
- 您需要一个位置向量来存储从 UDDI 获得的每个服务的位置。
Vector locationVecor = new Vector(1,1)
|
- 您还需要一个 WSDL 位置向量来存储从 UDDI 获得的每个服务的 WSDL 文档位置。
Vector wsdlVector = new Vector(1,1)
|
- 您需要一个结果向量来存储通过调用从 UDDI 获得的服务而获得的结果。
Vector servicesVector = new Vector(1,1)
|
- 设置查询并发布 API。
String inquiryAPI ="http:/ibm.com/services/uddi/publishap";
String publishAPI ="http:/ibm.com/services/uddi/publishapi";
proxy.setInquiryURL(inquiryAPI);
proxy.setPublishURL(publishAPI);
|
- 查找与
serviceType 关联的业务实体。
BusinessList businessList = proxy.find_business(serviceType,null,0);
BusinessInfos bis =businessList.getBusinessInfos();
Vector v2 =bis.getBusinessInfoVector();
|
- 查找与业务实体关联的商业服务。举例来说,查找所有提供
cheapestAirFare 服务的服务提供者。
for (int i2 =0;i2 < v2.size();i2++){
BusinessInfo businessInfo = (BusinessInfo)v2.elementAt(i2);
ServiceInfos serviceInfos = businessInfo.getServiceInfos();
Vector v3 =serviceInfos.getServiceInfoVector();
|
- 查找与每个服务关联的绑定细节。
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);
|
- 从绑定模板
AccessPoint 收集访问点。
accessPoint = bindingTemplate.getAccessPoint();
|
- 将访问点添加到位置向量中。
locationVecor.addElement(accessPoint.getText());
|
- 获取每个服务的 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())); }}}}
|
- 调用动态服务实用程序方法,从而提供对要调用的正确的服务实用程序类的映射。
for(int int i5 =0;i5 <locationVecor.size();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与我联系。
参考资料
关于作者
对本文的评价
|  |