We will go through creating and implementating web services in our openhub-example module.

WSDL and XSD implementation

Versioning

Use versions in format <major>.<minor>

Minor version is for compatible changes, major version indicates non-compatible changes.

Versioning should be explicit - it means to present version number in elements, URLs etc.:

If there is change in WSDL's version then change versions of XSDs as well.

See Best practices for more development tips.

hello-v1.0.wsdl:


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
                  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
                  xmlns:xs="http://www.w3.org/2001/XMLSchema"
                  xmlns:cc="http://openhubframework.org/ws/Common-v1"
                  xmlns:tns="http://openhubframework.org/ws/HelloService-v1"
                  targetNamespace="http://openhubframework.org/ws/HelloService-v1">

    <wsdl:types>
        <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified">
            <xs:import namespace="http://openhubframework.org/ws/HelloService-v1" schemaLocation="helloOperations-v1.0.xsd"/>
        </xs:schema>
    </wsdl:types>

    <wsdl:message name="traceHeaderMsg">
        <wsdl:part element="cc:traceHeader" name="traceHeader"/>
    </wsdl:message>

    <wsdl:message name="syncHelloRequestMsg">
        <wsdl:part element="tns:syncHelloRequest" name="syncHelloRequest">
        </wsdl:part>
    </wsdl:message>
    <wsdl:message name="syncHelloResponseMsg">
        <wsdl:part element="tns:syncHelloResponse" name="syncHelloResponse">
        </wsdl:part>
    </wsdl:message>
    <wsdl:message name="asyncHelloRequestMsg">
        <wsdl:part element="tns:asyncHelloRequest" name="asyncHelloRequest">
        </wsdl:part>
    </wsdl:message>
    <wsdl:message name="asyncHelloResponseMsg">
        <wsdl:part element="tns:asyncHelloResponse" name="asyncHelloResponse">
        </wsdl:part>
    </wsdl:message>


    <wsdl:portType name="hello-v1.0">
        <wsdl:operation name="syncHello">
            <wsdl:input message="tns:syncHelloRequestMsg" name="syncHelloRequest">
            </wsdl:input>
            <wsdl:output message="tns:syncHelloResponseMsg" name="syncHelloResponse">
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="asyncHello">
            <wsdl:input message="tns:asyncHelloRequestMsg" name="asyncHelloRequest">
            </wsdl:input>
            <wsdl:output message="tns:asyncHelloResponseMsg" name="asyncHelloResponse">
            </wsdl:output>
        </wsdl:operation>
    </wsdl:portType>


    <wsdl:binding name="helloBindingSoap11-v1.0" type="tns:hello-v1.0">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>

        <wsdl:operation name="syncHello">
            <soap:operation soapAction=""/>
            <wsdl:input name="syncHelloRequest">
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output name="syncHelloResponse">
                <soap:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="asyncHello">
            <soap:operation soapAction=""/>
            <wsdl:input name="asyncHelloRequest">
                <soap:header message="tns:traceHeaderMsg" part="traceHeader" use="literal"/>
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output name="asyncHelloResponse">
                <soap:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>


    <wsdl:service name="helloService-v1.0">
        <wsdl:port binding="tns:helloBindingSoap11-v1.0" name="helloSoap11-v1.0">
            <soap:address location="/ws/hello/v1"/>
        </wsdl:port>
    </wsdl:service>

</wsdl:definitions>

helloOperations-v1.0.xsd:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
           xmlns:cc="http://openhubframework.org/ws/Common-v1"
           xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="http://openhubframework.org/ws/HelloService-v1">

    <!--
        Note: all XSD/WSDL files are copied together at one place during XJC (Maven) code generation
    -->
    <xs:import namespace="http://openhubframework.org/ws/Common-v1" schemaLocation="commonTypes-v1.0.xsd"/>

    <!-- syncHello -->
    <xs:element name="syncHelloRequest">
        <xs:annotation>
            <xs:documentation>Synchronous calling of hello service</xs:documentation>
        </xs:annotation>

        <xs:complexType>
            <xs:sequence>
                <xs:element name="name" type="xs:string">
                    <xs:annotation>
                        <xs:documentation>Greeting's name</xs:documentation>
                    </xs:annotation>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="syncHelloResponse">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="greeting" type="xs:string"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>


    <!-- asyncHello -->
    <xs:element name="asyncHelloRequest">
        <xs:annotation>
            <xs:documentation>Asynchronous calling of hello service</xs:documentation>
        </xs:annotation>

        <xs:complexType>
            <xs:sequence>
                <xs:element name="name" type="xs:string">
                    <xs:annotation>
                        <xs:documentation>Greeting's name</xs:documentation>
                    </xs:annotation>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="asyncHelloResponse">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="confirmAsyncHello" type="cc:callbackResponse"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

</xs:schema>

Generating Java sources from WSDL/XSD

We use jaxws-maven-plugin plugin for generating Java classes from WSDL/XSD:


<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>jaxws-maven-plugin</artifactId>
    <configuration>
        <sourceDestDir>${modules.output.directory}</sourceDestDir>
        <wsdlDirectory>${modules.import.directory}</wsdlDirectory>
        <bindingDirectory>${modules.import.directory}</bindingDirectory>
        <bindingFiles>
            <bindingFile>jaxb_global_bindings.xjb</bindingFile>
        </bindingFiles>
    </configuration>
    <executions>
        <execution>
            <id>WSDL-import-in-hello-model</id>
            <goals>
                <goal>wsimport</goal>
            </goals>
            <configuration>
                <staleFile>${project.build.directory}/jaxws/.in.hello.model</staleFile>
                <packageName>org.openhubframework.openhub.modules.in.hello.model</packageName>
                <wsdlFiles>
                    <wsdlFile>hello-v1.0.wsdl</wsdlFile>
                </wsdlFiles>
                <bindingFiles>
                    <bindingFile>jaxb_global_bindings.xjb</bindingFile>
                    <bindingFile>jaxb_common_bindings.xjb</bindingFile>
                </bindingFiles>
            </configuration>
        </execution>
    </executions>
</plugin>

Publishing WSDL/XSD

Web service configuration for openhub-example module is in org.openhubframework.openhub.modules.ExampleWebServiceConfig:

@Configuration
@Profile(ExampleProperties.EXAMPLE_PROFILE)
public class ExampleWebServiceConfig {

    static final ClassPathResource HELLO_OPERATIONS_XSD_RESOURCE = new ClassPathResource(
            "org/openhubframework/openhub/modules/in/hello/ws/v1_0/helloOperations-v1.0.xsd");

    @Bean(name = "helloOperations-v1.0")
    public XsdSchema helloOperations() {
        return new SimpleXsdSchema(HELLO_OPERATIONS_XSD_RESOURCE);
    }

    @Bean(name = "hello")
    public SimpleWsdl11Definition helloWsdl() {
        SimpleWsdl11Definition wsdl = new SimpleWsdl11Definition();
        wsdl.setWsdl(new ClassPathResource(
                "org/openhubframework/openhub/modules/in/hello/ws/v1_0/hello-v1.0.wsdl"));
        return wsdl;
    }
}

Implementation of WebServiceValidatingSources that registers XSD schemas for validation incoming/outgoing messages (traceHeader is mandatory for asynchronous requests only - set synchronnous requests to ignore requests)

@Component
@Profile(ExampleProperties.EXAMPLE_PROFILE)
public class ExampleWebServiceValidatingSources implements WebServiceValidatingSources {

    @Override
    public Resource[] getXsdSchemas() {
        return new Resource[] {ExampleWebServiceConfig.HELLO_OPERATIONS_XSD_RESOURCE};
    }

    @Override
    public String[] getIgnoreRequests() {
        return new String[] {"{http://openhubframework.org/ws/HelloService-v1}syncHelloRequest"};
    }
}

Route implementations

See How to write routes?


@CamelConfiguration(value = SyncHelloRoute.ROUTE_BEAN)
@Profile(ExampleProperties.EXAMPLE_PROFILE)
public class SyncHelloRoute extends AbstractBasicRoute {

    static final String ROUTE_BEAN = "syncHelloRouteBean";

    private static final String OPERATION_NAME = "syncHello";

    private static final String ROUTE_ID_SYNC_HELLO = getRouteId(ServiceEnum.HELLO, OPERATION_NAME);

    static final String HELLO_SERVICE_NS = "http://openhubframework.org/ws/HelloService-v1";

    @Override
    protected void doConfigure() throws Exception {
        from(getInWsUri(new QName(HELLO_SERVICE_NS, "syncHelloRequest")))
                .routeId(ROUTE_ID_SYNC_HELLO)

                .policy(RouteConstants.WS_AUTH_POLICY)

                .to("throttling:sync:" + OPERATION_NAME)

                .unmarshal(jaxb(SyncHelloRequest.class))

                .log(LoggingLevel.DEBUG, "Calling hello service with name: ${body.name}")

                .bean(this, "composeGreeting")

                .marshal(jaxb(SyncHelloResponse.class));
    }

    @Handler
    public SyncHelloResponse composeGreeting(@Body SyncHelloRequest req) {
        Assert.notNull(req, "req must not be null");

        String greeting = "Hello " + req.getName();

        SyncHelloResponse res = new SyncHelloResponse();
        res.setGreeting(greeting);
        return res;
    }
}

Unit test implementations

See How to write unit test?


@ActiveRoutes(classes = SyncHelloRoute.class)
public class SyncHelloRouteTest extends AbstractExampleModuleTest {

    @Produce(uri = TestWsUriBuilder.URI_WS_IN + "syncHelloRequest")
    private ProducerTemplate producer;

    @EndpointInject(uri = "mock:test")
    private MockEndpoint mock;

    /**
     * Checking successful calling.
     */
    @Test
    public void testSyncHelloRoute() throws Exception {
        String xml = "<syncHelloRequest xmlns=\"http://openhubframework.org/ws/HelloService-v1\">"
                + "    <name>Mr. Parker</name>"
                + "</syncHelloRequest>";

        String output = producer.requestBody((Object)xml, String.class);
        SyncHelloResponse res = Tools.unmarshalFromXml(output, SyncHelloResponse.class);

        assertThat(res, notNullValue());
        assertThat(res.getGreeting(), is("Hello Mr. Parker"));
    }
}