Red Hat Training

A Red Hat training course is available for Red Hat Fuse

28.2. 实施一个消费者

概述

要在从 WSDL 合同开始实施消费者,您必须使用以下存根:

  • 服务类
  • SEI

使用这些存根代码可实例化服务代理,以向远程服务发出请求。它还实现了消费者的业务逻辑。

生成的服务类

例 28.2 “生成的服务类概述” 显示生成的服务类 ServiceName_Service的典型概述[2],扩展 javax.xml.ws.Service 基础类。

例 28.2. 生成的服务类概述

@WebServiceClient(name="..." targetNamespace="..."
                  wsdlLocation="...")
public class ServiceName extends javax.xml.ws.Service
{
  ...
  public ServiceName(URL wsdlLocation, QName serviceName) { }

  public ServiceName() { }

  // Available only if you specify '-fe cxf' option in wsdl2java
  public ServiceName(Bus bus) { }

  @WebEndpoint(name="...")
  public SEI getPortName() { }
  .
  .
  .
}

例 28.2 “生成的服务类概述” 中的 ServiceName 类定义以下方法:

  • ServiceName(URL wsdlLocation, QName serviceName) - 根据 wsdl:service 元素中的数据构建服务对象,使用在 wsdlLocation 处获取的 QName ServiceName 服务。
  • ServiceName() - 默认构造器。它根据服务名称和在生成 stub 代码时提供的 WSDL 合同构建服务对象(例如,在运行 wsdl2java 工具时)。使用这种构造器会规定 WSDL 合同在指定位置仍可用。
  • ServiceName(Bus 总线) - (特定于CXF) 是一个额外构造器,允许您指定用于配置该服务的总线实例。这在多线程应用程序上下文中非常有用,其中多个总线实例可以与不同的线程相关联。此构造器提供了一种简单方式,可确保您指定的总线是与此服务一起使用的总线。只有在调用 wsdl2java 工具时指定 -fe cxf 选项时才可用。
  • getPortName() - 返回由 wsdl:port 元素定义的端点的代理,其 name 属性等于 PortName。为 ServiceName 服务定义的每个 wsdl:port 元素生成 getter 方法。包含多个端点定义的 wsdl:service 元素会生成带有多个 getPortName() 方法生成的服务类。

服务端点接口

对于原始 WSDL 合同中定义的每个接口,您可以生成对应的 SEI。服务端点接口是 wsdl:portType 元素的 Java 映射。原始 wsdl:portType 元素中定义的每个操作都映射到 SEI 中的对应方法。操作的参数映射如下: .输入参数映射到方法参数。

  1. 第一个输出参数映射到一个返回值。
  2. 如果有多个输出参数,第二个和后续输出参数映射到方法参数(此外,必须使用 Holder 类型)传递这些参数的值。

例如: 例 28.3 “Greeter Service Endpoint 接口” 显示 Greeter SEI,它是从 例 26.1 “helloworld WSDL 合同” 中定义的 wsdl:portType 元素生成的。为了简单起见,例 28.3 “Greeter Service Endpoint 接口” 会省略标准的 JAXB 和 JAX-WS 注解。

例 28.3. Greeter Service Endpoint 接口

package org.apache.hello_world_soap_http;
  ...
public interface Greeter
{
  public String sayHi();
  public String greetMe(String requestType);
  public void greetMeOneWay(String requestType);
  public void pingMe() throws PingMeFault;
}

消费者主要功能

例 28.4 “消费者实施代码” 显示实施 HelloWorld consumer 的代码。用户连接到 SOAPService 服务上的 SoapPort 端口,然后继续调用 Greeter 端口类型支持的各个操作。

例 28.4. 消费者实施代码

package demo.hw.client;

import java.io.File;
import java.net.URL;
import javax.xml.namespace.QName;
import org.apache.hello_world_soap_http.Greeter;
import org.apache.hello_world_soap_http.PingMeFault;
import org.apache.hello_world_soap_http.SOAPService;

public final class Client {

  private static final QName SERVICE_NAME =
  new QName("http://apache.org/hello_world_soap_http",
            "SOAPService");

  private Client()
  {
  }

  public static void main(String args[]) throws Exception
  {
 if (args.length == 0)
    {
      System.out.println("please specify wsdl");
      System.exit(1);
    }

 URL wsdlURL;
    File wsdlFile = new File(args[0]);
    if (wsdlFile.exists())
    {
      wsdlURL = wsdlFile.toURL();
    }
    else
    {
      wsdlURL = new URL(args[0]);
    }

    System.out.println(wsdlURL);
 SOAPService ss = new SOAPService(wsdlURL,SERVICE_NAME);
 Greeter port = ss.getSoapPort();
    String resp;

 System.out.println("Invoking sayHi...");
    resp = port.sayHi();
    System.out.println("Server responded with: " + resp);
    System.out.println();

    System.out.println("Invoking greetMe...");
    resp = port.greetMe(System.getProperty("user.name"));
    System.out.println("Server responded with: " + resp);
    System.out.println();

    System.out.println("Invoking greetMeOneWay...");
    port.greetMeOneWay(System.getProperty("user.name"));
    System.out.println("No response from server as method is OneWay");
    System.out.println();

 try {
      System.out.println("Invoking pingMe, expecting exception...");
      port.pingMe();
    } catch (PingMeFault ex) {
      System.out.println("Expected exception: PingMeFault has occurred.");
      System.out.println(ex.toString());
    }
    System.exit(0);
  }
}

来自 例 28.4 “消费者实施代码”Client.main () 方法进行如下:

如果 Apache CXF 运行时类位于您的类路径上,运行时会隐式初始化。不需要调用特殊的函数来初始化 Apache CXF。

使用者需要一个字符串参数,为 HelloWorld 提供 WSDL 合同的位置。WSDL 合同的位置存储在 wsdlURL 中。

您可以使用需要 WSDL 合同和服务名称的构造器创建服务对象。 调用适当的 getPortName() 方法来获取所需端口的实例。在这种情况下,SOAPService 服务只支持 SoapPort 端口,该端口实施 Greeter 服务端点接口。

使用者调用 Greeter 服务端点接口支持的不同方法。

对于 pingMe () 方法,示例代码演示了如何捕获 PingMeFault 错误异常。

使用 -fe cxf 选项生成的客户端代理

如果您在 wsdl2java 中指定 -fe cxf 选项(因此选择 cxf frontend)来生成客户端代理,则生成的客户端代理代码与 Java 7 更好地集成。在这种情况下,当您调用 getServiceNamePort () 方法时,您会返回一个类型为 SEI 的子接口并实现以下额外接口:

  • java.lang.AutoCloseable
  • javax.xml.ws.BindingProvider (JAX-WS 2.0)
  • org.apache.cxf.endpoint.Client

要了解如何简化使用客户端代理的过程,请考虑以下 Java 代码示例,使用标准的 JAX-WS 代理对象编写:

// Programming with standard JAX-WS proxy object
//
(ServiceNamePortType port = service.getServiceNamePort();
((BindingProvider)port).getRequestContext()
        .put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, address);
port.serviceMethod(...);
((Closeable)port).close();

将前面的代码与以下等效代码示例进行比较,并使用 cxf frontend 生成的代码编写:

// Programming with proxy generated using '-fe cxf' option
//
try (ServiceNamePortTypeProxy port = service.getServiceNamePort()) {
    port.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, address);
    port.serviceMethod(...);
}


[2] 如果 wsdl:service 元素的 name 属性以 _Service 结尾,则不使用 _Service。