从遗留的 jax-ws 客户端代码生成 wsdl

generate wsdl from legacy jax-ws client code

是否可以仅使用 JAX-WS 客户端代码生成 WSDL?

我有一个遗留的客户端代码,里面没有 wsdl,只有远程 wsdl uri,我需要为该客户端代码创建模拟服务,我需要 WSDL 来创建这个模拟。

有什么想法吗?

出于创建mock/stub服务提供者的目的,我认为如果可以的话最好下载原始的WSDL。从客户端生成的代码生成 WSDL 的风险是生成的 WSDL 肯定有可能 不完全 匹配原始代码,这违背了模拟或存根的目的。

但是,这里是我用来从生成的 JAX-WS 客户端生成 WSDL 的步骤。我确实必须手动编写一个实现 class.

原始 WSDL:http://wsf.cdyne.com/WeatherWS/Weather.asmx?WSDL

  1. 生成 JAX-WS 客户端: wsimport -extension -keep http://wsf.cdyne.com/WeatherWS/Weather.asmx?WSDL

这将创建示例 JAX-WS 客户端,其中包括生成的服务端点接口 (SEI):com.cdyne.ws.weatherws.WeatherSoap

  1. Oddly enough,我们需要一个实现class到运行wsgen来生成一个WSDL。我手动创建了一个实现 class,声明它实现了 SEI,然后从接口定义中复制了所有方法并给每个方法一个 return null; 实现。
package com.cdyne.ws.weatherws;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;

@WebService(name = "WeatherSoap", targetNamespace = "http://ws.cdyne.com/WeatherWS/")
public class WeatherSoapStubImpl implements WeatherSoap {

    /**
     * Gets Information for each WeatherID
     * 
     * @return
     *     returns com.cdyne.ws.weatherws.ArrayOfWeatherDescription
     */
    @WebMethod(operationName = "GetWeatherInformation", action = "http://ws.cdyne.com/WeatherWS/GetWeatherInformation")
    @WebResult(name = "GetWeatherInformationResult", targetNamespace = "http://ws.cdyne.com/WeatherWS/")
    @RequestWrapper(localName = "GetWeatherInformation", targetNamespace = "http://ws.cdyne.com/WeatherWS/", className = "com.cdyne.ws.weatherws.GetWeatherInformation")
    @ResponseWrapper(localName = "GetWeatherInformationResponse", targetNamespace = "http://ws.cdyne.com/WeatherWS/", className = "com.cdyne.ws.weatherws.GetWeatherInformationResponse")
    public ArrayOfWeatherDescription getWeatherInformation() {
        return null;
    }

    /**
     * Allows you to get your City Forecast Over the Next 7 Days, which is updated hourly. U.S. Only
     * 
     * @param zip
     * @return
     *     returns com.cdyne.ws.weatherws.ForecastReturn
     */
    @WebMethod(operationName = "GetCityForecastByZIP", action = "http://ws.cdyne.com/WeatherWS/GetCityForecastByZIP")
    @WebResult(name = "GetCityForecastByZIPResult", targetNamespace = "http://ws.cdyne.com/WeatherWS/")
    @RequestWrapper(localName = "GetCityForecastByZIP", targetNamespace = "http://ws.cdyne.com/WeatherWS/", className = "com.cdyne.ws.weatherws.GetCityForecastByZIP")
    @ResponseWrapper(localName = "GetCityForecastByZIPResponse", targetNamespace = "http://ws.cdyne.com/WeatherWS/", className = "com.cdyne.ws.weatherws.GetCityForecastByZIPResponse")
    public ForecastReturn getCityForecastByZIP(
        @WebParam(name = "ZIP", targetNamespace = "http://ws.cdyne.com/WeatherWS/")
        String zip) {
            return null;
        }

    /**
     * Allows you to get your City's Weather, which is updated hourly. U.S. Only
     * 
     * @param zip
     * @return
     *     returns com.cdyne.ws.weatherws.WeatherReturn
     */
    @WebMethod(operationName = "GetCityWeatherByZIP", action = "http://ws.cdyne.com/WeatherWS/GetCityWeatherByZIP")
    @WebResult(name = "GetCityWeatherByZIPResult", targetNamespace = "http://ws.cdyne.com/WeatherWS/")
    @RequestWrapper(localName = "GetCityWeatherByZIP", targetNamespace = "http://ws.cdyne.com/WeatherWS/", className = "com.cdyne.ws.weatherws.GetCityWeatherByZIP")
    @ResponseWrapper(localName = "GetCityWeatherByZIPResponse", targetNamespace = "http://ws.cdyne.com/WeatherWS/", className = "com.cdyne.ws.weatherws.GetCityWeatherByZIPResponse")
    public WeatherReturn getCityWeatherByZIP(
        @WebParam(name = "ZIP", targetNamespace = "http://ws.cdyne.com/WeatherWS/")
        String zip) {
            return null;
        }
}
  1. 编译存根实现 class。 javac com/cdyne/ws/weatherws/WeatherSoapStubImpl.java

  2. 生成 WSDL。这是可以使用命令行开关来尝试尽可能接近原始文件的地方。 wsgen -keep -cp . com.cdyne.ws.weatherws.WeatherSoapStubImpl -wsdl:Xsoap1.2 -extension -inlineSchemas (在当前目录中创建 WSDL):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Generated by JAX-WS RI (http://jax-ws.java.net). RI's version is JAX-WS RI 2.2.9-b130926.1035 svn-revision#8c29a9a53251ff741fca1664a8221dc876b2eac8. -->
<definitions targetNamespace="http://ws.cdyne.com/WeatherWS/" name="WeatherSoapStubImplService" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:tns="http://ws.cdyne.com/WeatherWS/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata">
  <types>
    <xs:schema elementFormDefault="qualified" version="1.0" targetNamespace="http://ws.cdyne.com/WeatherWS/" xmlns:xs="http://www.w3.org/2001/XMLSchema">

      <xs:element name="GetCityForecastByZIP" type="tns:GetCityForecastByZIP"/>

      <xs:element name="GetCityForecastByZIPResponse" type="tns:GetCityForecastByZIPResponse"/>

      <xs:element name="GetCityWeatherByZIP" type="tns:GetCityWeatherByZIP"/>

      <xs:element name="GetCityWeatherByZIPResponse" type="tns:GetCityWeatherByZIPResponse"/>

      <xs:element name="GetWeatherInformation" type="tns:GetWeatherInformation"/>

      <xs:element name="GetWeatherInformationResponse" type="tns:GetWeatherInformationResponse"/>

      <xs:complexType name="GetWeatherInformation">
        <xs:sequence/>
      </xs:complexType>

      <xs:complexType name="GetWeatherInformationResponse">
        <xs:sequence>
          <xs:element name="GetWeatherInformationResult" type="tns:ArrayOfWeatherDescription" minOccurs="0"/>
        </xs:sequence>
      </xs:complexType>

      <xs:complexType name="ArrayOfWeatherDescription">
        <xs:sequence>
          <xs:element name="WeatherDescription" type="tns:WeatherDescription" minOccurs="0" maxOccurs="unbounded"/>
        </xs:sequence>
      </xs:complexType>

      <xs:complexType name="WeatherDescription">
        <xs:sequence>
          <xs:element name="WeatherID" type="xs:short"/>
          <xs:element name="Description" type="xs:string" minOccurs="0"/>
          <xs:element name="PictureURL" type="xs:string" minOccurs="0"/>
        </xs:sequence>
      </xs:complexType>

      <xs:complexType name="GetCityWeatherByZIP">
        <xs:sequence>
          <xs:element name="ZIP" type="xs:string" minOccurs="0"/>
        </xs:sequence>
      </xs:complexType>

      <xs:complexType name="GetCityWeatherByZIPResponse">
        <xs:sequence>
          <xs:element name="GetCityWeatherByZIPResult" type="tns:WeatherReturn" minOccurs="0"/>
        </xs:sequence>
      </xs:complexType>

      <xs:complexType name="WeatherReturn">
        <xs:sequence>
          <xs:element name="Success" type="xs:boolean"/>
          <xs:element name="ResponseText" type="xs:string" minOccurs="0"/>
          <xs:element name="State" type="xs:string" minOccurs="0"/>
          <xs:element name="City" type="xs:string" minOccurs="0"/>
          <xs:element name="WeatherStationCity" type="xs:string" minOccurs="0"/>
          <xs:element name="WeatherID" type="xs:short"/>
          <xs:element name="Description" type="xs:string" minOccurs="0"/>
          <xs:element name="Temperature" type="xs:string" minOccurs="0"/>
          <xs:element name="RelativeHumidity" type="xs:string" minOccurs="0"/>
          <xs:element name="Wind" type="xs:string" minOccurs="0"/>
          <xs:element name="Pressure" type="xs:string" minOccurs="0"/>
          <xs:element name="Visibility" type="xs:string" minOccurs="0"/>
          <xs:element name="WindChill" type="xs:string" minOccurs="0"/>
          <xs:element name="Remarks" type="xs:string" minOccurs="0"/>
        </xs:sequence>
      </xs:complexType>

      <xs:complexType name="GetCityForecastByZIP">
        <xs:sequence>
          <xs:element name="ZIP" type="xs:string" minOccurs="0"/>
        </xs:sequence>
      </xs:complexType>

      <xs:complexType name="GetCityForecastByZIPResponse">
        <xs:sequence>
          <xs:element name="GetCityForecastByZIPResult" type="tns:ForecastReturn" minOccurs="0"/>
        </xs:sequence>
      </xs:complexType>

      <xs:complexType name="ForecastReturn">
        <xs:sequence>
          <xs:element name="Success" type="xs:boolean"/>
          <xs:element name="ResponseText" type="xs:string" minOccurs="0"/>
          <xs:element name="State" type="xs:string" minOccurs="0"/>
          <xs:element name="City" type="xs:string" minOccurs="0"/>
          <xs:element name="WeatherStationCity" type="xs:string" minOccurs="0"/>
          <xs:element name="ForecastResult" type="tns:ArrayOfForecast" minOccurs="0"/>
        </xs:sequence>
      </xs:complexType>

      <xs:complexType name="ArrayOfForecast">
        <xs:sequence>
          <xs:element name="Forecast" type="tns:Forecast" nillable="true" minOccurs="0" maxOccurs="unbounded"/>
        </xs:sequence>
      </xs:complexType>

      <xs:complexType name="Forecast">
        <xs:sequence>
          <xs:element name="Date" type="xs:dateTime"/>
          <xs:element name="WeatherID" type="xs:short"/>
          <xs:element name="Desciption" type="xs:string" minOccurs="0"/>
          <xs:element name="Temperatures" type="tns:temp"/>
          <xs:element name="ProbabilityOfPrecipiation" type="tns:POP"/>
        </xs:sequence>
      </xs:complexType>

      <xs:complexType name="temp">
        <xs:sequence>
          <xs:element name="MorningLow" type="xs:string" minOccurs="0"/>
          <xs:element name="DaytimeHigh" type="xs:string" minOccurs="0"/>
        </xs:sequence>
      </xs:complexType>

      <xs:complexType name="POP">
        <xs:sequence>
          <xs:element name="Nighttime" type="xs:string" minOccurs="0"/>
          <xs:element name="Daytime" type="xs:string" minOccurs="0"/>
        </xs:sequence>
      </xs:complexType>
</xs:schema>
  </types>
  <message name="GetWeatherInformation">
    <part name="parameters" element="tns:GetWeatherInformation"/>
  </message>
  <message name="GetWeatherInformationResponse">
    <part name="parameters" element="tns:GetWeatherInformationResponse"/>
  </message>
  <message name="GetCityForecastByZIP">
    <part name="parameters" element="tns:GetCityForecastByZIP"/>
  </message>
  <message name="GetCityForecastByZIPResponse">
    <part name="parameters" element="tns:GetCityForecastByZIPResponse"/>
  </message>
  <message name="GetCityWeatherByZIP">
    <part name="parameters" element="tns:GetCityWeatherByZIP"/>
  </message>
  <message name="GetCityWeatherByZIPResponse">
    <part name="parameters" element="tns:GetCityWeatherByZIPResponse"/>
  </message>
  <portType name="WeatherSoap">
    <operation name="GetWeatherInformation">
      <input wsam:Action="http://ws.cdyne.com/WeatherWS/GetWeatherInformation" message="tns:GetWeatherInformation"/>
      <output wsam:Action="http://ws.cdyne.com/WeatherWS/WeatherSoap/GetWeatherInformationResponse" message="tns:GetWeatherInformationResponse"/>
    </operation>
    <operation name="GetCityForecastByZIP">
      <input wsam:Action="http://ws.cdyne.com/WeatherWS/GetCityForecastByZIP" message="tns:GetCityForecastByZIP"/>
      <output wsam:Action="http://ws.cdyne.com/WeatherWS/WeatherSoap/GetCityForecastByZIPResponse" message="tns:GetCityForecastByZIPResponse"/>
    </operation>
    <operation name="GetCityWeatherByZIP">
      <input wsam:Action="http://ws.cdyne.com/WeatherWS/GetCityWeatherByZIP" message="tns:GetCityWeatherByZIP"/>
      <output wsam:Action="http://ws.cdyne.com/WeatherWS/WeatherSoap/GetCityWeatherByZIPResponse" message="tns:GetCityWeatherByZIPResponse"/>
    </operation>
  </portType>
  <binding name="WeatherSoapPortBinding" type="tns:WeatherSoap">
    <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
    <operation name="GetWeatherInformation">
      <soap12:operation soapAction="http://ws.cdyne.com/WeatherWS/GetWeatherInformation"/>
      <input>
        <soap12:body use="literal"/>
      </input>
      <output>
        <soap12:body use="literal"/>
      </output>
    </operation>
    <operation name="GetCityForecastByZIP">
      <soap12:operation soapAction="http://ws.cdyne.com/WeatherWS/GetCityForecastByZIP"/>
      <input>
        <soap12:body use="literal"/>
      </input>
      <output>
        <soap12:body use="literal"/>
      </output>
    </operation>
    <operation name="GetCityWeatherByZIP">
      <soap12:operation soapAction="http://ws.cdyne.com/WeatherWS/GetCityWeatherByZIP"/>
      <input>
        <soap12:body use="literal"/>
      </input>
      <output>
        <soap12:body use="literal"/>
      </output>
    </operation>
  </binding>
  <service name="WeatherSoapStubImplService">
    <port name="WeatherSoapPort" binding="tns:WeatherSoapPortBinding">
      <soap12:address location="REPLACE_WITH_ACTUAL_URL"/>
    </port>
  </service>
</definitions>
  1. 使用 SOAPUI
  2. 等工具创建存根或模拟服务

通过这种方法和简单的服务接口,我能够通过客户端成功地将 SOAP 消息发送到在使用生成的 WSDL 创建的 SOAPUI 中创建的模拟服务提供者。