IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  SOA and Web services  >

使用 MVC 样式的 Web 体系结构,第 2 部分: 构建 Web 服务 MVC 体系结构

将任何 MVC 模式的实现转换为 Web 服务

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


级别: 初级

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 服务体系结构的关键组件

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


图 2. Web 服务体系结构样本
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 的一个向量,该向量为服务对象计数。对象服务类由三个变量和这些变量的访问器组成,这些变量是 serviceimplemenationserviceimplemenationurnmethodNameNavigator 类还植入 formbean、包名称、 serviceCountdynamicLookup ,并向同一个向量添加这些变量。最后,向量对象被放入一个散列表,其中服务类型作为键( 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

FormBeanHttpServlet 请求参数检索用户输入的查询。 如上面逻辑的步骤 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 服务器通讯的。 所有所需的参数,如 methodNametargetObject 和服务的位置,都是从 requestHandler 获得的(通过 Navigator 类)。


图 3. 与 SOAP 服务器通讯的 SOAP 客户机
与 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) 方法,向其传送 serviceTypeformBean ,然后取回结果:

//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

下面的清单包含使用 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 <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与我联系。



参考资料



关于作者

Naveen Balani 目前是 Syntel India, Ltd. 的一名技术分析员。他现在的工作范围包括设计、开发和实现基于 J2EE 的产品。您可以通过 naveen_balani@syntelinc.com与他联系。




对本文的评价










回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款