Web 服务客户端的服务定位器

作者:Sean Brydon

问题描述

许多应用程序都使用服务定位器模式目前,Web 服务在应用程序中越来越通用,为了支持 Web 服务,需要更新服务定位器代码。此外,如果应用程序目前尚未应用服务定位器模式,则应该考虑使用该模式。

企业应用程序需要一种查找引用的方法,这些引用提供对分布式组件和 Web 服务的访问。Java[TM] 2 Platform, Enterprise Edition (J2EE[TM]) 应用程序使用 Java 命名和目录接口 (JNDI) 查找 Enterprise Bean home 接口、Java Message Service (JMS) 组件、数据源、连接、连接工厂,现在也可以查找 Web 服务。重复的查找代码会使代码难以读取和维护。服务定位器模式将该代码集中到一个类中,并消除了冗余代码,否则冗余代码会分散在应用程序的各个部分。

此解决方案适用于以下情况:

解决方案

服务定位器模式已经应用了一段时间,许多 J2EE 应用程序都使用服务定位器类查找 J2EE 资源。现在,获得了可以查找的 Web 服务和引用的服务,我们需要考虑如何利用该解决方案来支持 Web 服务。

该解决方案包括以下内容:

检查服务定位器的模式

让我们看一下来自 Java Adventure Builder 的早期服务定位器示例,其中添加了 Web 服务支持。从下面的代码可以看出,有多种方法可以获取各种 J2EE 资源(如 EJBHome、JMS ConnectionFactory 或数据库之类的数据源)的引用。

public class ServiceLocator {

  private transient InitialContext ic;

  public ServiceLocator() throws ServiceLocatorException {
    try {
      ic = new InitialContext();
    } catch (Exception e) {
      throw new ServiceLocatorException(e);
    }
}

  /**
  * @return the EJB Home factory corresponding to the homeName
  */
  public EJBHome getRemoteHome(String jndiHomeName, Class className) throws ServiceLocatorException {
    try {
      Object objref = ic.lookup(jndiHomeName);
      return (EJBHome) PortableRemoteObject.narrow(objref, className);
    } catch (Exception e) {
      throw new ServiceLocatorException(e);
    }
  }

  /**
  * @return the factory for the factory to get JMS connections from
  */
  public ConnectionFactory getJMSConnectionFactory(String jmsConnFactoryName)
                                               throws ServiceLocatorException {
    try {
      return (ConnectionFactory) ic.lookup(jmsConnFactoryName);
    } catch (Exception e) {
    throw new ServiceLocatorException(e);
   }
  }

  /*
  * @return the DataSource corresponding to the name parameter
  */
  public DataSource getDataSource(String dataSourceName) throws ServiceLocatorException {
    try {
      return (DataSource)ic.lookup(dataSourceName);
    } catch (Exception e) {
    throw new ServiceLocatorException(e);
    }
  }
   ...//and other J2EE resources
}

代码示例 1:J2EE 资源的服务定位器,不支持 Web 服务

我们来看一下服务定位器模式中的一些主要成员:

服务定位器的 UML 图

在使用业务对象或服务的客户端上,首先需要查找并获取该服务的引用。使用服务定位器模式时,将遇到以下主要成员:

现在,我们已经对服务定位器有了一定的了解,让我们再来看一下如何添加 Web 服务支持。

将 Web 服务支持添加到服务定位器

现在,我们来看一下要支持 Web 服务,需要向服务定位器添加哪些方法。

在本示例中,假定存在一个正在运行某服务的部署的应用程序,该应用程序是一个 war 文件,包含了访问正在运行的服务的 Servlet。在 J2EE 中,对于像 Web 应用程序中要访问 Web 服务应用程序的 Servlet 组件,需要编写调用服务的代码,同时,在 web.xml 部署描述符中需要包含一个对服务的引用(请参见代码示例 2)。

<service-ref>
    <description>Client of Some Service</description>
    <service-ref-name>service/
SomeService</service-ref-name>
    <service-interface>
        com.sun.j2ee.blueprints.myclientapplication.
SomeService
    </service-interface>
    <wsdl-file>WEB-INF/wsdl/SomeService.wsdl</wsdl-file>
    <jaxrpc-mapping-file>WEB-INF/someservice-mapping.xml</jaxrpc-mapping-file>
    <service-qname xmlns:servicens="urn:
SomeService">servicens:SomeService</service-qname>
</service-ref>

代码示例 2:web.xml 部署描述符中的服务引用

如果部署了客户端应用程序,并为该服务引用绑定了 JNDI 名称,则应用程序只需访问此命名服务即可获取对该服务的访问。在本示例中,假定服务引用在命名目录中绑定为 "java:comp/env/service/SomeService"表 1 左侧的代码显示了未使用服务定位器类的代码。现在,需要重构此代码才能使用服务定位器,如表右侧所示。

未使用服务定位器 重构以使用服务定位器
String s= "java:comp/env/service/SomeService";

try {        
  Context ic = new InitialContext();
  Service myService =(Service)ic.lookup(s);
  SomeServiceSEI port =(SomeServiceSEI)

      myService
.getPort(SomeServiceSEI.class);

  //now just call the web service method
  port.someMethod(someParameter);

} catch(Exception exe){
           ...
}
String s= "java:comp/env/service/SomeService";

try {        
  ServiceLocator sLoc = new ServiceLocator();
 
SomeServiceSEI port = (SomeServiceSEI)
   
sLoc.getServicePort(s,SomeServiceSEI.class);
 
  //now just call the web service method
  port.someMethod(someParameter);

} catch(ServiceLocatorException sle){
   ...
} catch(Exception exe){
   ...
}

表 1:重构以使用服务定位器模式

正如您看到的那样,为 Web 服务更新服务定位器只需进行简单的更改。尽管只需很少的代码更改,但这却非常有用,因为在应用程序中的许多地方都会重复使用该代码。在一段时间内,如果在多个地方出现重复代码,则这些代码会出现某些不一致、杂乱的情况,维护起来很困难。因此使用服务定位器可在一定程度上统一代码。

此外,还需要更新服务定位器类以支持查找 Web 服务。代码示例 3 显示了支持查找 Web 服务引用的服务定位器。

/**
 * Implements Service Locator pattern for Web services
 */
public class ServiceLocator {
    private transient InitialContext ic;
   
    public ServiceLocator() throws ServiceLocatorException  {
        try {
            ic = new InitialContext();
        } catch (Exception e) {
            throw new ServiceLocatorException(e);
        }
    }

    public Remote getServicePort(String jndiHomeName, Class className) throws ServiceLocatorException {
        try {
            Service service = (Service) ic.lookup(jndiHomeName);
            return service.getPort(className);
        } catch (Exception e) {
            throw new ServiceLocatorException(e);
        }
    }
}

代码示例 3:支持 J2EE 组件充当 Web 服务客户端的服务定位器

代码如上所示。该模式非常易于使用。它确实已将额外的类添加到了应用程序中,这可以在一定程度上统一您的代码。

策略:将 Web 服务支持添加到实现缓存策略的服务定位器中

有些应用程序设计了使用缓存策略的服务定位器。这是为了避免创建不必要的 JNDI 初始上下文和进行不必要的服务对象查找。下面的代码示例 4 显示了应用缓存策略的服务定位器查找 Web 服务的信息。

public class ServiceLocator {
   
    private InitialContext ic;
    //used to hold references to resources
    private Map cache = Collections.synchronizedMap(new HashMap());
   
    private static ServiceLocator instance = new ServiceLocator();
   
    public static ServiceLocator getInstance() {
        return instance;
    }
   
    private ServiceLocator() throws ServiceLocatorException {
        try {
            ic = new InitialContext();
        } catch (Exception e) {
            throw new ServiceLocatorException(e);
        }
    }

    public Remote getServicePort(String jndiHomeName, Class className) throws ServiceLocatorException {
        Remote servicePort = (Remote)
cache.get(jndiHomeName);
       
if (servicePort == null) {
          try {
            Service service = (Service) ic.lookup(jndiHomeName);
            servicePort = service.getPort(className);
            cache.put(jndiHomeName, servicePort);
          } catch (Exception e) {
            throw new ServiceLocatorException(e);
          }
        }
        return
servicePort;
    }
}

代码示例 4:应用缓存策略的服务定位器查找 Web 服务引用

可以从上述代码示例中看出,需要查找 Web 服务的应用程序可以使用服务定位器模式,这与查找其他 J2EE 资源的方式基本相同。

参考资料

有关本主题的详细信息,请参阅以下资料:


© Sun Microsystems 2005。Java BluePrints Solutions Catalog 中的所有内容受版权保护,未经 Sun Microsystems 的明确书面许可,不得在其他产品中发布。