UML软件工程组织

 

 

为SOAP/HTTP Web服务编写可靠客户机
 
作者:Shailesh K. Mishra 来源:IBM
 
本文内容包括:
了解如何通过使用 SOAP 消息处理程序来读取和缓存 SOAP 主体,从而为 SOAP/HTTP Web 服务编写可靠的客户机。这种方法在出现服务器故障或网络服务故障等情况下保存准备 Web 服务调用所使用的数据。客户机从 Web 服务获得响应后,可以将此数据从缓存丢弃,否则将使用相同的数据再次调用 Web 服务。

引言

在通常的 Web 服务调用场景中,Web 服务客户机准备调用,并随后调用 Web 服务。如果出现临时系统错误、网络故障或服务不可用,准备调用过程中使用的数据将丢失。可以采用多种方式来保存此数据。一种方法是使用 SOAP 消息处理程序(以下简称处理程序);不过处理程序最常用于进行 SOAP Header 处理。SOAP Header 用于承载请求的上下文数据,例如安全性和事务性之类的服务质量(Quality of Service,QoS)请求。在这些情况下,可以使用处理程序来读取 SOAP 主体。本文将说明如何使用处理程序来缓存主体、如何在出现故障时使用此缓存以及如何编写可靠的 Web 服务客户机。

编写 Web 服务消息处理程序

开发消息处理程序的主要目的是保存准备调用 Web 服务时使用的数据。清单 1 显示了一个消息处理程序,用于在发送请求时读取请求主体。

清单 1. 消息处理程序代码
 
package com.ibm.reliablewsclient.ws;

import java.util.logging.Logger;

import javax.xml.namespace.QName;
import javax.xml.rpc.JAXRPCException;
import javax.xml.rpc.handler.GenericHandler;
import javax.xml.rpc.handler.HandlerInfo;
import javax.xml.rpc.handler.MessageContext;
import javax.xml.rpc.handler.soap.SOAPMessageContext;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPMessage;

/*
 * Created on Aug 3, 2006 @author Shailesh K Mishra (shailekm@in.ibm.com)
 *  
 */

public class ClientHandler extends GenericHandler {
   private Logger logger;
   public static SOAPBody body_of_request=null;

/**
 *  
 */
   public ClientHandler() {
      super();

      // TODO Auto-generated constructor stub
   }

/*
 * (non-Javadoc)
 * 
 * @see javax.xml.rpc.handler.Handler#init(javax.xml.rpc.handler.HandlerInfo)
 */
   public void init(HandlerInfo arg0) {
      // set up logger
      logger = Logger.getLogger("com.ibm.reliablewsclient.ws");
      super.init(arg0);
   }

/*
 * (non-Javadoc)
 * 
 * @see javax.xml.rpc.handler.Handler#getHeaders()
 */
   public QName[] getHeaders() {
      // TODO Auto-generated method stub
      return null;
   }

/*
 * (non-Javadoc)
 * 
 * @see javax.xml.rpc.handler.Handler#handleRequest(javax.xml.rpc.handler.MessageContext)
 */
   public boolean handleRequest(MessageContext arg0) {
      try {
         logger.info("Begin procession ClientHandler.handleRequest");
         //generate SOAP body
         SOAPMessage message = ((SOAPMessageContext) arg0).getMessage();
         SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();
         SOAPBody body = envelope.getBody();
         body_of_request=body;
         logger.info("Request Body : " + body.toString());
         logger.info("Completed procesing for ClientHandler.handleRequest");

      } catch (Throwable ex) {
         throw new JAXRPCException("Error in handleRequest", ex);
      }
      return true;
   }

/*
 * (non-Javadoc)
 * 
 * @see javax.xml.rpc.handler.Handler#handleResponse(javax.xml.rpc.handler.MessageContext)
 */
   public boolean handleResponse(MessageContext arg0) {

      return true;
   }

}
            

清单 1 中的处理程序代码将读取消息主体,并将其赋值给静态变量。此静态 body_of_request 字段用于缓存在准备 Web 服务调用时使用的数据。这是非常简单的缓存技术,但不能扩展。

编写可靠的托管 Web 服务客户机

托管客户机 是在托管环境中运行的客户机,即它通过应用服务器进行工作。我将说明如何通过使用 Servlet 作为客户机来编写可靠的托管客户机。首先,您需要编写 Web 服务,并随后为该 Web 服务生成存根。要生成存根,请在开发环境(我使用的是 IBM Rational ® Application Developer™)中右键单击 Web 服务的 WSDL 文件,然后单击生成客户机选项。生成存根后,就可以编写 Servlet 了。清单 2 显示了 Servlet 的 doGet 方法。

清单 2. Servlet 的 doGet 方法
 
            try {
         DemoWSProxy proxy=new DemoWSProxy();
         String str=proxy.generateId("John",2834742,"IBM Bangalore");
         throw new RemoteException("To demostrate");
      } catch (RemoteException e) {
         try {
            
            SOAPConnectionFactory fact;

            fact = SOAPConnectionFactory.newInstance();
            SOAPConnection con = fact.createConnection();
            javax.xml.soap.SOAPFactory sf = SOAPFactory.newInstance();

            MessageFactory mfact = MessageFactory.newInstance();
            SOAPMessage smsg = mfact.createMessage();

            SOAPPart prt = smsg.getSOAPPart();
            SOAPEnvelope env = prt.getEnvelope();
               env.addChildElement(ClientHandler.body_of_request);
               
            //Set the WebService end point URL
            URL endpoint = new URL("http://localhost:9080/
                           ReliableWSClientProject/services/DemoWS");

            //Send the message
            SOAPMessage response = con.call(smsg, endpoint);
            System.out.println(response.getSOAPBody().toString());
            response.writeTo(arg1.getOutputStream());

            System.out.println();
            //Close the connection
            con.close();
         }  catch (UnsupportedOperationException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
         } catch (MalformedURLException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
         } catch (SOAPException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
         } 
      }

调用 doGet 方法时,它将实例化 WSProxy 类(在存根生成期间创建),并调用 Web 服务的 generateId 方法。出于演示目的,我引发了一个 RemoteException 异常来表明使用的是缓存数据。调用 Web 服务时,消息处理程序将读取请求的主体,并将整个主体保存在 body_of_request 静态变量中。现在,当执行到 throw RemoteException 处时,将引发远程异常,流将进入 catch 块。在此 catch 块中,将使用所保存的请求主体来准备 SOAP 请求,以便再次调用 Web 服务。

使用 Servlet 配置消息处理程序

要在 Servlet 调用 Web 服务时调用此处理程序,需要执行以下步骤来配置处理程序:

  1. 打开 web.xml 文件,并转到 Handlers 选项卡,如图 1 中所示。

    图 1. Handlers 选项卡


     
  2. 单击 Add 并使用所需的值填写空白字段,如图 2 中所示。

    图 2. 填写消息处理程序详细信息


     
  3. 单击 Finish 并保存 web.xml 文件。

这样就完成了消息处理程序的配置。

编写可靠的非托管 Web 服务客户机

非托管 客户机是在非托管环境中运行的客户机,例如独立 Java™ 客户机。对于此类客户机,您需要以编程方式配置处理程序。清单 3 显示了可靠的非托管客户机代码。

清单 3. 非托管客户机
 
public class MyWSInvoker {

   /**
    *  
    */
   public MyWSInvoker() {
      super();
      // TODO Auto-generated constructor stub
   }

   public static void main(String[] args) {

      try {
         ArrayList handlerList = new ArrayList();
         //Instantiate HandlerInfo class by passing your MessageHandler
         // class and put this
         //HandlerInfo class into an arraylist
         handlerList.add(new HandlerInfo(ClientHandler.class, null, null));

         ServiceFactory fact = ServiceFactory.newInstance();
         Service service = fact.createService(new QName(
               "http://ws.reliablewsclient.ibm.com", "DemoWSService"));
         HandlerRegistry handlerRegistry = service.getHandlerRegistry();
         //QName passed in setHandlerChain method should be QName of
         // PortType
         handlerRegistry.setHandlerChain(new QName(
               "http://ws.reliablewsclient.ibm.com", "DemoWS"),
               handlerList);
         Call call = service.createCall();
         call.setPortTypeName(new QName(
               "http://ws.reliablewsclient.ibm.com", "DemoWS"));
         call.setOperationName(new QName(
               "http://ws.reliablewsclient.ibm.com", "generateId"));
         call.setTargetEndpointAddress("http://localhost:9080/
                                       ReliableWSClientProject/services/DemoWS");
         call.setReturnType(new QName("http://www.w3.org/2001/XMLSchema","string"));
         
         
         Object obj = call.invoke(new Object[] { "Jerry",new Integer(1234), "BIM" });
         if (obj instanceof String) {
            System.out.println((String) obj);
         }
         throw new RemoteException("my remote");
      } catch (RemoteException e) {
         // TODO Auto-generated catch block
         //e.printStackTrace();
         try {
            Thread.sleep(10000);
            SOAPConnectionFactory fact;

            fact = SOAPConnectionFactory.newInstance();
            SOAPConnection con = fact.createConnection();
            javax.xml.soap.SOAPFactory sf = SOAPFactory.newInstance();

            MessageFactory mfact = MessageFactory.newInstance();
            SOAPMessage smsg = mfact.createMessage();

            SOAPPart prt = smsg.getSOAPPart();
            SOAPEnvelope env = prt.getEnvelope();
               env.addChildElement(ClientHandler.body_of_request);
               
            //Set the WebService end point URL
            URL endpoint = new URL(
                  "http://localhost:9080/ReliableWSClientProject/services/DemoWS");

            //Send the message
            SOAPMessage response = con.call(smsg, endpoint);
            System.out.println(response.getSOAPBody().toString());
            response.writeTo(System.out);

            System.out.println();
            //Close the connection
            con.close();
         } catch (InterruptedException e1) {
            //TODO Auto-generated catch block
            e1.printStackTrace();
         } catch (UnsupportedOperationException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
         } catch (MalformedURLException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
         } catch (SOAPException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
         } catch (IOException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
         }

      } catch (ServiceException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }

   }
}

要在这种情况下配置消息处理程序,请通过按以下方式传入消息处理程序类名称来实例化 HandlerInfo

new HandlerInfo(ClientHandler.class, null, null);

然后将此 HandlerInfo 类添加到数组列表中:

ArrayList handlerList = new ArrayList();
          handlerList.add(new HandlerInfo(ClientHandler.class, null, null));
             

现在获取 HandlerRegistry

HandlerRegistry handlerRegistry = service.getHandlerRegistry();

HandlerRegistry 注册处理程序

handlerRegistry.setHandlerChain(
   new QName("http://ws.reliablewsclient.ibm.com", "DemoWS"),handlerList);

这样就完成了消息处理程序的配置。 当将请求发送到 Web 服务时,此处理程序将读取 SOAP 主体,并将其分配给 body_of_request 静态字段。

清单 3 引发了一个 RemoteExpection,以说明使用了缓存的主体信息。当执行到 catch 块时,将使用缓存的主体准备 SOAP 请求,并随后调用 Web 服务。

结束语

在本文中,您了解了使用消息处理程序编写可靠的托管和非托管 Web 服务客户机的简单步骤。使用静态字段缓存请求主体的方法并不完善,还是一项有待发展的技术,但是您可以利用一些好的机制来进行缓存。

下载

描述 名字 大小 下载方法
Source code for samples Sample.zip 33KB HTTP
Runtime description runtime.txt 1KB HTTP

参考资料

学习 讨论
 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号