Prototype
Prototype

Reputation: 21

How to generate WSDL based on another SOAP service?

I am developing a SOAP service based on Spring WebServices, which, according to a business task, must proxy a SOAP request through itself.

My problem is that I can't get a correctly generated WSDL at the output, which I could give to the customer, since in fact there is almost no description of elements in my main xsd due to the fact that I use a generated SOAP service, objects from which participate in @RequestPayload and @ResponsePayload. The only elements added by my service are setDataInQueue and message (currently not used, but will be needed later).

Due to the large number of links in the xsd file, I could not publish my question, as it was marked as spam, so I had to omit them.

Generated WSDL on 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>

Generated WSDL on 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>

As you can see, the final wsdls do not have any sections with operations and descriptions of requests/responses, according to which the customer could eventually generate a client to work with my proxy service.

I have two endpoints: I had to split the endpoint into separate classes and declarations since they have a different namespace.

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);
    }

}

These endpoints are configured according to one of the examples of the main guides from 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 (for generate @XMlRootElement annotation):

<?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>

Since I need to proxy requests from another SOAP service, I use WSDL-based code generation using wsimport:

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 ...
}

I have been developing a SOAP service quite recently and have studied a lot of guides when trying to solve the problem, but I have not been able to achieve results. I would also like to note that I get an error when trying to import my SOAP service into SoapUI:

Error loading [http://localhost:8080/ws/message.xsd]: org.apache.xmlbeans.XmlException: org.apache.xmlbeans.XmlException: error: Premature end of file

Upvotes: 1

Views: 148

Answers (0)

Related Questions