如何基于其他SOAP服务生成WSDL?

How to generate WSDL based on another SOAP service?

提问人:Prototype 提问时间:11/3/2023 最后编辑:Prototype 更新时间:11/3/2023 访问量:51

问:

我正在开发一个基于 的 SOAP 服务,根据业务任务,它必须通过自身代理请求。Spring WebServicesSOAP

我的问题是我无法在输出中获得正确生成的 WSDL,我可以将其提供给客户,因为实际上由于我使用生成的 SOAP 服务,我的主 xsd 中几乎没有元素的描述,对象参与@RequestPayload和@ResponsePayload。我的服务添加的唯一元素是 和(目前未使用,但稍后将需要)。setDataInQueuemessage

由于 xsd 文件中的链接数量众多,我无法发布我的问题,因为它被标记为垃圾邮件,所以我不得不省略它们。

在以下位置生成 WSDL:localhost:8080/ws/proxy.wsdl

<wsdl:definitions ...>
    <wsdl:types>
        <xs:schema ...>
            <xs:import schemaLocation="message.xsd"/>
            <xs:import namespace="http://service.example.com/" schemaLocation="setDataInQueue.xsd"/>
        </xs:schema>
    </wsdl:types>
    <wsdl:portType name="ProxyPort"></wsdl:portType>
    <wsdl:binding name="ProxyPortSoap11" type="tns:ProxyPort">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    </wsdl:binding>
    <wsdl:service name="ProxyService">
        <wsdl:port binding="tns:ProxyPortSoap11" name="ProxyPortSoap11">
            <soap:address location="http://localhost:8080/ws"/>
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

在以下位置生成 WSDL:localhost:8080/ws/ehdproxy.wsdl

<wsdl:definitions ... >
    <wsdl:types>
        <xs:schema ...>
            <xs:import schemaLocation="message.xsd"/>
            <xs:import namespace="service.exampe.com/" schemaLocation="setDataInQueue.xsd"/>
        </xs:schema>
    </wsdl:types>
    <wsdl:portType name="EhdProxyPort"></wsdl:portType>
    <wsdl:binding name="EhdProxyPortSoap11" type="tns:EhdProxyPort">
        <soap:binding style="document" transport="schemas.xmlsoap.org/soap/http"/>
    </wsdl:binding>
    <wsdl:service name="EhdProxyService">
        <wsdl:port binding="tns:EhdProxyPortSoap11" name="EhdProxyPortSoap11">
            <soap:address location="localhost:8080/ws"/>
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

正如你所看到的,最终的 wsdl 没有任何包含请求/响应操作和描述的部分,根据这些部分,客户最终可以生成一个客户端来使用我的代理服务。

我有两个端点: 我不得不将端点拆分为单独的类和声明,因为它们具有不同的命名空间。

ProxyEndPoint:

@Endpoint
@RequiredArgsConstructor
public class ProxyEndPoint {

    public static final String BUS_NAMESPACE_URI = "http...service.example.com/";

    private final ConverterService converterService;
    private final EhdService ehdService;

    @ResponsePayload
    @PayloadRoot(namespace = BUS_NAMESPACE_URI, localPart = "setDataInQueue")
    public SetDataInQueueResponse handleSetDataInQueue(@RequestPayload SetDataInQueue request) {
        final var converted = this.converterService.convert(request);
        return this.ehdService.callSetDataInQueue(converted);
    }

EhdProxyEndPoint:

@Endpoint
@RequiredArgsConstructor
public class EhdProxyEndPoint {

    public static final String EHD_NAMESPACE_URI = "http...contractor.com/";

    private final EhdService ehdService;

    @ResponsePayload
    @PayloadRoot(namespace = EHD_NAMESPACE_URI, localPart = "setDataInResult")
    public SetDataInResultResponse handleSetDataInResult(@RequestPayload SetDataInResult request) {
        return this.ehdService.callSetDataInResult(request);
    }

}

这些端点是根据 Spring WebServices 中主要指南的示例之一配置的:

WebServiceConfig:

@EnableWs
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter {

    @Bean
    public ServletRegistrationBean<MessageDispatcherServlet> messageDispatcherServlet(ApplicationContext context) {
        MessageDispatcherServlet servlet = new MessageDispatcherServlet();
        servlet.setApplicationContext(context);
        servlet.setTransformWsdlLocations(true);
        return new ServletRegistrationBean<>(servlet, "/ws/*");
    }

    @Bean(name = "proxy")
    public DefaultWsdl11Definition proxySchemaDefinition(XsdSchema proxySchema) {
        DefaultWsdl11Definition definition = new DefaultWsdl11Definition();
        definition.setServiceName("ProxyService");
        definition.setPortTypeName("ProxyPort");
        definition.setLocationUri("/ws");
        definition.setTargetNamespace(ProxyEndPoint.BUS_NAMESPACE_URI);
        definition.setSchema(proxySchema);
        return definition;
    }

    @Bean(name = "ehdproxy")
    public DefaultWsdl11Definition ehdProxySchemaDefinition(XsdSchema proxySchema) {
        DefaultWsdl11Definition definition = new DefaultWsdl11Definition();
        definition.setServiceName("EhdProxyService");
        definition.setPortTypeName("EhdProxyPort");
        definition.setLocationUri("/ws");
        definition.setTargetNamespace(EhdProxyEndPoint.EHD_NAMESPACE_URI);
        definition.setSchema(proxySchema);
        return definition;
    }

    @Bean
    public XsdSchema proxySchema() {
        return new SimpleXsdSchema(new ClassPathResource("/xsd/proxy.xsd"));
    }

}

resources/xsd/proxy.xsd:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
        ...
        jaxb:version="3.0"
        elementFormDefault="qualified"
        attributeFormDefault="unqualified"
>

    <xs:import schemaLocation="message.xsd"/>
    <xs:import namespace="service.example.com" schemaLocation="setDataInQueue.xsd"/>

</xs:schema>

resources/xsd/setDataInQueue.xsd:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0"
           ...
>

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

    <xs:complexType name="setDataInQueue">
        <xs:sequence>
            <xs:element name="FileName" type="xs:string"/>
            <xs:element name="File" type="xs:base64Binary"/>
            <xs:element name="Signature" type="xs:base64Binary"/>
        </xs:sequence>
    </xs:complexType>

</xs:schema>

/resources/xsd/message.xsd

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0">
<xs:element name="category" type="categoryEntity" />
<xs:element name="message" type="messageEntity" />
<xs:complexType name="messageEntity">
    <xs:sequence>
        <xs:element name="id" type="xs:string" minOccurs="0" />
        <xs:element name="catalog" type="catalogEntity" minOccurs="0" maxOccurs="unbounded" />
    </xs:sequence>
</xs:complexType>
<xs:complexType name="catalogEntity">
    <xs:sequence>
        <xs:element name="item" type="itemEntity" minOccurs="0" maxOccurs="unbounded" />
    </xs:sequence>
    <xs:attribute name="name" type="xs:string" />
    <xs:attribute name="parent_catalog_id" type="xs:int" />
</xs:complexType>
<xs:complexType name="itemEntity">
    <xs:sequence>
        <xs:element name="geodata" type="xs:string" minOccurs="0" />
        <xs:element name="categories" minOccurs="0">
            <xs:complexType>
                <xs:sequence>
                    <xs:element ref="category" minOccurs="0" maxOccurs="unbounded" />
                </xs:sequence>
            </xs:complexType>
        </xs:element>
        <xs:element name="data" minOccurs="0">
            <xs:complexType>
                <xs:sequence>
                    <xs:element name="attribute" type="attributeEntity" minOccurs="0" maxOccurs="unbounded" />
                </xs:sequence>
            </xs:complexType>
        </xs:element>
    </xs:sequence>
    <xs:attribute name="action" type="xs:string" />
    <xs:attribute name="isManualGeo" type="xs:boolean" />
</xs:complexType>
<xs:complexType name="categoryEntity">
    <xs:simpleContent>
        <xs:extension base="xs:int">
            <xs:attribute name="nameHier" type="xs:string" />
        </xs:extension>
    </xs:simpleContent>
</xs:complexType>
<xs:complexType name="attributeEntity">
    <xs:sequence>
        <xs:element name="values" minOccurs="0">
            <xs:complexType>
                <xs:sequence>
                    <xs:element name="value" type="valueEntity" minOccurs="0" maxOccurs="unbounded" />
                </xs:sequence>
            </xs:complexType>
        </xs:element>
    </xs:sequence>
    <xs:attribute name="field_id" type="xs:int" />
    <xs:attribute name="type" type="xs:string" />
    <xs:attribute name="pk" type="xs:string" />
    <xs:attribute name="isManual" type="xs:boolean" />
</xs:complexType>
<xs:complexType name="valueEntity">
    <xs:simpleContent>
        <xs:extension base="xs:string">
            <xs:attribute name="occurrence" type="xs:int" />
        </xs:extension>
    </xs:simpleContent>
</xs:complexType>
</xs:schema>

resources/jxb/bindings.xjb(用于生成注释):@XMlRootElement

<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings version="3.0"
               ...
               jaxb:extensionBindingPrefixes="xjc">

    <jaxb:globalBindings generateElementProperty="false" >
        <xjc:simple/>
        <xjc:serializable uid="-1"/>
    </jaxb:globalBindings>

</jaxb:bindings>

由于我需要代理来自另一个 SOAP 服务的请求,因此我使用 wsimport 使用基于 WSDL 的代码生成:

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.1.5'
    id 'io.spring.dependency-management' version '1.1.3'
}

group = 'com.group'
version = '0.0.1-SNAPSHOT'

java {
    sourceCompatibility = '21'
}

ext.jaxwsSourceDir = "${buildDir}/generated/sources/jaxws"

configurations {
    jaxb
    jaxws
    compileOnly {
        extendsFrom annotationProcessor
    }
}

sourceSets {
    main {
        java {
            srcDir 'src/main/java'
            srcDir 'build/generated/sources/jaxb'
            srcDir jaxwsSourceDir
        }
    }
}

task genJaxb {
    ext.sourcesDir = "${buildDir}/generated/sources/jaxb"
    ext.schema = "src/main/resources/xsd/proxy.xsd"
    ext.binding = "src/main/resources/jxb/bindings.xjb"

    outputs.dir sourcesDir

    doLast() {
        project.ant {
            taskdef name: "xjc", classname: "com.sun.tools.xjc.XJCTask",
                    classpath: configurations.jaxb.asPath
            mkdir(dir: sourcesDir)

            xjc(destdir: sourcesDir, schema: schema, binding: ext.binding) {
                arg(value: "-wsdl")
                produces(dir: sourcesDir, includes: "**/*.java")
            }
        }
    }
}

task wsimport {
    System.setProperty('javax.xml.accessExternalSchema', 'all')
    description = 'Generate classes from wsdl using wsimport'

    doLast {
        project.mkdir(jaxwsSourceDir)
        ant {
            taskdef(name: 'wsimport',
                    classname: 'com.sun.tools.ws.ant.WsImport',
                    classpath: configurations.jaxws.asPath
            )
            wsimport(
                    keep: true,
                    destdir: jaxwsSourceDir,
                    extension: "true",
                    verbose: true,
                    wsdl: "%WSDL LINK PLACEHOLDER%", // <- WSDL contractor inserted here
                    binding: "${projectDir}/src/main/resources/jxb/bindings.xjb",
                    xnocompile: true,
                    package: "contractor") {
                xjcarg(value: "-XautoNameResolution")
            }
        }
    }
}

compileJava.dependsOn genJaxb, wsimport

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web-services'

    // support xml/xsd/wsdl
    implementation 'wsdl4j:wsdl4j'
    implementation 'com.sun.xml.ws:rt:4.0.2'

    jaxb('org.glassfish.jaxb:jaxb-xjc:4.0.4')
    jaxb('org.glassfish.jaxb:jaxb-core:4.0.4')
    jaxb('org.glassfish.jaxb:jaxb-runtime:4.0.4')

    jaxws 'com.sun.xml.ws:jaxws-tools:4.0.2',
            'jakarta.xml.ws:jakarta.xml.ws-api:3.0.0',
            'jakarta.xml.bind:jakarta.xml.bind-api:4.0.1',
            'jakarta.activation:jakarta.activation-api:2.1.2',
            'com.sun.xml.ws:jaxws-rt:4.0.2'
   
    // dev / test utils ...
}

我最近一直在开发SOAP服务,并且在尝试解决问题时研究了很多指南,但是我无法取得成果。 我还想指出,在尝试将我的SOAP服务导入SoapUI时出现错误:

加载 [http://localhost:8080/ws/message.xsd] 时出错:org.apache.xmlbeans.XmlException:org.apache.xmlbeans.XmlException:错误:文件过早结束

java wsdl soap-client spring-ws

评论

1赞 Prototype 11/15/2023
有人可以帮忙吗?

答: 暂无答案