Apache CXF- Contract First Web Services

I wrote a post a few years ago detailing a step by step guide to building a contract first web service using Spring.  I recently started working with Apache CXF and thought it would be worth putting together a more up to date post, this time using CXF.
We'll create a fictitious Account Service that takes a single account number parameter and returns associated account information. Although the sample service will be very simple, the approach taken should provide you with a solid foundation upon which to build real world services.

Source Code

The full source code for this post is available on GitHub so feel free to have a look. You may find it useful to have the code locally as you work through this post.

Domain Model Definition

We'll begin by defining the service domain model as an XML Schema Definition (XSD). The schema will define the entities that our Account service will use. We'll begin by defining the core Account entity - the diagram below shows the XSD snippet that defines the Account as well as a visual representation taken from XML Spy.

Next we'll define the AccountDetailsRequest type that will be used as a parameter to the Account service.

Finally we'll define the AccountDetailsResponse type that will be used as the return type from the Account service.

The full schema definition is shown below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://com/blog/samples/webservices/accountservice" xmlns:account="http://webservices.samples.blog.com" targetNamespace="http://com/blog/samples/webservices/accountservice" elementFormDefault="qualified">
 <xsd:complexType name="Account">
  <xsd:sequence>
   <xsd:element name="AccountNumber" type="xsd:string"/>
   <xsd:element name="AccountName" type="xsd:string"/>
   <xsd:element name="AccountBalance" type="xsd:double"/>
   <xsd:element name="AccountStatus" type="EnumAccountStatus"/>
  </xsd:sequence>
 </xsd:complexType> 
 <xsd:simpleType name="EnumAccountStatus">
  <xsd:restriction base="xsd:string">
   <xsd:enumeration value="Active"/>
   <xsd:enumeration value="Inactive"/>
  </xsd:restriction>
 </xsd:simpleType>
 <xsd:element name="AccountDetailsRequest">
  <xsd:complexType>
   <xsd:sequence>
    <xsd:element name="accountNumber" type="xsd:string"/>
   </xsd:sequence>
  </xsd:complexType>
 </xsd:element>
 <xsd:element name="AccountDetailsResponse">
  <xsd:complexType>
   <xsd:sequence>
    <xsd:element name="AccountDetails" type="Account"/>
   </xsd:sequence>
  </xsd:complexType>
 </xsd:element>
</xsd:schema>

Service Contract Definition

Next we'll define the public facing service contract using a WSDL. A WSDL is an XML document that describes a SOAP Web Service and how clients can interact with it. WSDLs are a great way to describe service contracts because they are easy to interpret by developers and can be used by tooling to generate many useful artefacts, some of which we'll look at later.  The Account Service WSDL is defined as follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap12/"
 xmlns:tns="http://www.briansjavablog.com/Accounts/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="Accounts"
 targetNamespace="http://www.briansjavablog.com/Accounts/"
 xmlns:accounts="http://com/blog/samples/webservices/accountservice">
 <wsdl:types>
  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <xsd:import namespace="http://com/blog/samples/webservices/accountservice"
    schemaLocation="../schema/AccountsService.xsd">
   </xsd:import>
  </xsd:schema>
 </wsdl:types>
 <wsdl:message name="AccountDetailsRequest">
  <wsdl:part element="accounts:AccountDetailsRequest" name="parameters" />
 </wsdl:message>
 <wsdl:message name="AccountDetailsResponse">
  <wsdl:part element="accounts:AccountDetailsResponse" name="parameters" />
 </wsdl:message>
 <wsdl:portType name="Accounts">
  <wsdl:operation name="GetAccountDetails">
   <wsdl:input message="tns:AccountDetailsRequest" />
   <wsdl:output message="tns:AccountDetailsResponse" />
  </wsdl:operation>
 </wsdl:portType>
 <wsdl:binding name="AccountsServiceSoapBinding" type="tns:Accounts">
  <soap:binding style="document"
   transport="http://schemas.xmlsoap.org/soap/http" />
  <wsdl:operation name="GetAccountDetails">
   <soap:operation
    soapAction="http://www.briansjavablog.com/Accounts/GetAccountDetails" />
   <wsdl:input>
    <soap:body use="literal" />
   </wsdl:input>
   <wsdl:output>
    <soap:body use="literal" />
   </wsdl:output>
  </wsdl:operation>
 </wsdl:binding>
 <wsdl:service name="AccountsService">
  <wsdl:port binding="tns:AccountsServiceSoapBinding" name="AccountsPort">
   <soap:address
    location="http://localhost:8080/apache-cfx-demo/services/accounts" />
  </wsdl:port>
 </wsdl:service>
</wsdl:definitions>
                                                                                    
The WSDL is composed of 5 main parts
  • Types - the <wsdl:types> section defines the domain model associated with the service. The types are defined using XSD and can be defined in the WSDL itself or imported from a separate XSD. On line 9 above we import the schema definition file we created earlier.
  • Message - the <wsdl:message> is used to define the request and response messages handled by the service. The nested <wsdl:part> section defines the domain type that will be used to form the messages.
  • PortType - the <wsdl:portType> defines the service operations that are exposed to clients, parameters required to invoke the operations and response types returned. 
  • Binding - the <wsdl:binding> section defines the protocol and data format. 
    • The binding type attribute refers to the portType defined earlier in the WSDL. 
    • The soap binding style can be either RPC or document. In this instance we've chosen document. 
    • The transport attribute indicates that the service will be exposed over HTTP. Other less common options include JMS and SMTP.
    • The operation element defines each operation that we exposed through the portType.
    • Binding - the <wsdl:binding> section defines the protocol and data format. 
  • Service - the <wsd;:service> defines the exposed service using the portType and binding we defined above.  

POM Configuration

Now that we've defined the WSDL its time to configure the POM to use the Apache CXF code generation plugin. The plugin invokes a WSDL2Java process which parses the WSDL and generates a service endpoint interface and associated domain objects. The plugin configuration is shown below. Note that I've omitted the rest of the POM definition for brevity. To see the full definition pull the sample code from github.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
          <plugins>
      <plugin>
  <groupId>org.apache.cxf</groupId>
  <artifactId>cxf-codegen-plugin</artifactId>
  <version>3.1.7</version>
  <executions>
     <execution>
        <id>generate-sources</id>
   <phase>generate-sources</phase>
   <configuration>
      <sourceRoot>src/generated/java</sourceRoot>
      <wsdlOptions>
         <wsdlOption>
            <wsdl>${basedir}/src/main/resources/wsdl/Accounts.wsdl</wsdl>
         </wsdlOption>
      </wsdlOptions>
   </configuration>
   <goals>
      <goal>wsdl2java</goal>
   </goals>
     </execution>
  </executions>
      </plugin>
          </plugins>
                                                                          
The WSDL path on line 14 tells CXF what WSDL to run WSDL2Java with. The sourceRoot configuration on line 11 is the fully qualified package name that the generated class will be copied to. Its a good idea to put these in src/generated/java so that its obvious to other developers that the package contains generated code.

Code Generation

We're now ready to generate the Service Endpoint Interface and domain objects from the WSDL. To run the code generation simply open a command window and run mvn generate-sources



Refresh the IDE workspace and you will see 2 new packages. The package names are based on the namespaces specified in the WSDL. The contents of both packages is described below.
  • com.blog.samples.webservices.accountservice - contains 4 service domain objects, Account, AccountDetailsRequest, AccountDetailsResponse and EnumAccountStatus. These 4 types represent the service request and response. ObjectFactory is a helper class for creating new instances of the domain types and package-info.java applies XML namespace metadata to all domain classes in the package.
  • com.briansjavablog.accounts - contains the Service Endpoint Interface Accounts.java. This interface is a Java representation of the service operation we defined in the WSDL. The contents of this class are covered in detail below. AccountsService.java is a web service client and can be used to invoke the service.

Service Endpoint Interface

The Service endpoint interface that was generated in the previous step is defined below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
/**
 * This class was generated by Apache CXF 3.1.7
 * 2016-09-29T07:57:42.236+01:00
 * Generated source version: 3.1.7
 * 
 */
@WebService(targetNamespace = "http://www.briansjavablog.com/Accounts/", name = "Accounts")
@XmlSeeAlso({com.blog.samples.webservices.accountservice.ObjectFactory.class})
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface Accounts {

    @WebMethod(operationName = "GetAccountDetails", action = "http://www.briansjavablog.com/Accounts/GetAccountDetails")
    @WebResult(name = "AccountDetailsResponse", targetNamespace = "http://com/blog/samples/webservices/accountservice", partName = "parameters")
    public com.blog.samples.webservices.accountservice.AccountDetailsResponse getAccountDetails(
        @WebParam(partName = "parameters", name = "AccountDetailsRequest", targetNamespace = "http://com/blog/samples/webservices/accountservice")
        com.blog.samples.webservices.accountservice.AccountDetailsRequest parameters
    );
} 
  • @WebService - Marks the class as defining a Web Service interface from a WSDL. The namespace should match the namespace defined in the WSDL and the name should match the WSDL PortType.
  • @XmlSeeeAlso - Lets JAXB know what other classes need to be registered with the JAXB context for serialization and deserialization. 
  • @SoapBinding - Describes mapping from web service operations to SOAP protocol
  • @WebMethod - Maps a service operation to a Java method. The operation name references the operation defined in the WSDL and the target namespace uses the namespace associated with the WSDL operation.  
  • @WebResult - Maps a service operation response message to a Java return type. The name refers to the response message name defined in the WSDL. The target namespace uses the namespace associated with the WSDL message and the partName refers to wdl:part name in the WSDL.
  • @WebParam - Maps a service operation request message to a Java parameter type. The name refers to the request message name defined in the WSDL. The target namespace uses the namespace associated with the WSDL message and the partName refers to wdl:part name in the WSDL. 

Service Endpoint Implementation

Now that we've generated the Service Endpoint Interface, its time to create an implementation. The endpoint implementation class will be called by the CXF framework after it has deserialized the incoming SOAP body and figured out which endpoint method should handle the request. Our simple service exposes a single operation, but for services with multiple operations CXF uses the request payload to determine which endpoint method to call. Creating an endpoint implementation is straight forward and simply requires a class that implements the Service Endpoint Interface as shown below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@WebService(portName = "Accounts", serviceName = "Accounts", 
            endpointInterface = "com.briansjavablog.accounts.Accounts", 
            targetNamespace = "http://www.briansjavablog.com/Accounts/")
public class AccountServiceEndpoint implements Accounts {

    private AccountService accountService;

    @Autowired
    public AccountServiceEndpoint(AccountService accountService) {
        this.accountService = accountService;
    }

    @Override
    public AccountDetailsResponse getAccountDetails(AccountDetailsRequest parameters) {

        return accountService.getAccountDetails(parameters);
    }
}

The @WebService annotation marks this class as implementing a web service endpoint. The attributes portName, serviceName and targetNamespace should all match their equivalent in the WSDL definition. The enpointInterface refers to the generated Service Endpoint Interface class we looked at earlier. The AccountServiceEndpoint constructor takes a Spring injected AccountService as an argument. The getAccountDetails method is an implementation of the interface method defined on the Service Endpoint Interface class and uses the injected service to get the required account details.

Account Service 

The Account Service injected into the endpoint implementation above is trivial and simply returns hard coded values. I've split this out into a separate service class to demonstrate that its good practice to decouple endpoint processing logic from business logic. The only thing to note in this class is that we use the JAXB generated ObjectFactory to create an instance of the response type.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@Service
public class AccountServiceImpl implements AccountService {

    @Override
    public AccountDetailsResponse getAccountDetails(AccountDetailsRequest parameters) {

        ObjectFactory factory = new ObjectFactory();
        AccountDetailsResponse response = factory.createAccountDetailsResponse();
  
        Account account = factory.createAccount();
        account.setAccountNumber("12345");
        account.setAccountStatus(EnumAccountStatus.ACTIVE);
        account.setAccountName("Joe Bloggs");
        account.setAccountBalance(3400);
  
        response.setAccountDetails(account);  
        return response;
    }
}
                                                                                      

Spring XML Configuration 

The guys at CXF have made a considerable effort to ensure that CXF integrates nicely with the Spring framework. Of course CXF can be configured programatically without Spring, but if you're already using Spring for dependency injection (or anything else for that matter), it makes sense to use CXFs Spring support. This section describes how to configure the endpoint we created earlier so that it can be published to a Servlet container like Tomcat.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jaxws="http://cxf.apache.org/jaxws"
    xsi:schemaLocation="http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context.xsd
          http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
          http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

   <context:component-scan base-package="com.briansjavablog.accounts" />

   <jaxws:endpoint id="accountService"
                   implementor="com.briansjavablog.accounts.service.AccountServiceEndpoint"
                   address="/services/accounts">
    <jaxws:inInterceptors>
      <ref bean="loggingInInterceptor" />
    </jaxws:inInterceptors>
    <jaxws:outInterceptors>
      <ref bean="loggingOutInterceptor" />
    </jaxws:outInterceptors>
   </jaxws:endpoint>

   <bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor" />
   <bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor" />

</beans>
  • Line 11 - Enable component scanning so that beans annotated as @Component and @Service are registered as Spring components. 
  • Lines 13 to 15 - Define endpoint configuration by specifying endpoint implementation class for implementor and the address of the exposed endpoint. Note that the address is relative to the application root.
  •  Lines 16 to 21 - Register endpoint interceptors for logging incoming SOAP request payload and outgoing SOAP response payload.
  • Line 24 - Create interceptor for logging inbound SOAP payloads. This interceptor is provided by CXF out of the box and is executed before the JAXB deserialization and endpoint implementation class is called. 
  • Line 24 - Create interceptor for logging outbound SOAP payloads. This interceptor is provided by CXF out of the box and is executed after endpoint service method is executed and the JAXB serialization and endpoint implementation class is called. 

Web.xml 

The final piece of configuration we need to run our service in a Servlet container is the web.xml. If you've done any kind of Java web development the web.xml definition below will look familiar.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
    <context-param>
 <param-name>contextConfigLocation</param-name>
 <param-value>WEB-INF/beans.xml</param-value>
    </context-param>

    <listener>
 <listener-class>
     org.springframework.web.context.ContextLoaderListener
 </listener-class>
    </listener>

    <servlet>
 <servlet-name>CXFServlet</servlet-name>
 <display-name>CXF Servlet</display-name>
 <servlet-class>
     org.apache.cxf.transport.servlet.CXFServlet
 </servlet-class>
 <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
 <servlet-name>CXFServlet</servlet-name>
 <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>
  • Line 8 to 11 - Provides a path to the Spring beans.xml configuration file. This file is used by the ContextLoaderListener described below.
  • Lines 13 to 17 - ContextLoaderListener creates the Spring application context on container startup. It uses the bean definitions defined in contextConfigLocation.
  • Lines 19 to 26 - Configures CXF provided Servlet to process incoming HTTP requests. 
  • Lines 28 to 31 -  Servlet mapping configures CXF Servlet to process all requests received at the application root.

Integration Test 

The next step is to add an integration test. Most projects now a days use some sort of continuous integration so its important to have integration test coverage that is run on every code push. We're going to create a simple happy path integration test that will deploy the service to a specified endpoint using Jetty, make a HTTP call to the endpoint and then perform some assertions on the response. This is a simple but effective end to end test that we can run locally or on a continuous integration environment, without having to worry about deploying to a standalone Servlet container.
We'll begin by looking at the Spring configuration that describes the dependencies we need to run our test.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
 xmlns:jaxws="http://cxf.apache.org/jaxws"
 xsi:schemaLocation="
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

 
 <context:component-scan base-package="com.briansjavablog.accounts" />

 <jaxws:server id="accountService"
        address="http://localhost:8080/apache-cxf-demo/services/accounts">
     <jaxws:serviceBean>
  <bean class="com.briansjavablog.accounts.service.AccountServiceEndpoint" />
     </jaxws:serviceBean>
     <jaxws:inInterceptors>
  <ref bean="loggingInInterceptor" />
     </jaxws:inInterceptors>
     <jaxws:outInterceptors>
                <ref bean="loggingOutInterceptor" />
     </jaxws:outInterceptors>
 </jaxws:server>

 <jaxws:client id="testAccountServiceClient"
        address="http://localhost:8080/apache-cxf-demo/services/accounts"
        serviceClass="com.briansjavablog.accounts.Accounts">
     <jaxws:inInterceptors>
         <ref bean="loggingInInterceptor" />
     </jaxws:inInterceptors>
     <jaxws:outInterceptors>
                <ref bean="loggingOutInterceptor" />
     </jaxws:outInterceptors>
 </jaxws:client>

 <bean id="abstractLoggingInterceptor" abstract="true">
     <property name="prettyLogging" value="true" />
 </bean>
 <bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor" parent="abstractLoggingInterceptor" />
 <bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor" parent="abstractLoggingInterceptor" />

</beans>
  • Line 12 - Configures component scanning so that beans annotated with @Component and @Service are registered with the bean factory.
  • Lines 14 to 15 - jaxws:server defines a server configuration that will launch Jetty to host the web service for the duration of the test. The address attribute specifies the local URL where the service will be deployed.
  • Lines 16 to 18 - Defines the endpoint class that will service incoming requests. This is the service endpoint implementation class we defined earlier.
  • Lines 19 to 24 - Logging interceptors are registered with the server to log inbound SOAP requests and outbound SOAP responses.
  •  Lines 27 to 29 - jaxws:client defines a web service client that we'll use to call the service. This client will be injected into our test class. The address attribute is the target URL of the service and the serviceClass is the Service Endpoint Interface that was generated earlier.
  • Lines 30 to 35 - Logging interceptors are registered with Client to log outbound SOAP requests and inbound SOAP responses.
  • Lines 38 to 42 - Defines inbound and outbound logging interceptors that are used by both the client and the server, The abstract interceptor is used to define a common property (prettyLogging) that is used by both logging interceptors.     
Finally we'll look at the test class definition. This class uses the test configuration above to create a web service client, a test server and deploy the application to the server.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration( {"classpath:beans-config-test.xml"} )
public class AccountServiceEndpointTest {

    @Autowired
    private Accounts accountsService;
    private AccountDetailsRequest accountDetailsRequest;
 
    @Before
    public void setUp() throws Exception {
  
        ObjectFactory objectFactory = new ObjectFactory();
 accountDetailsRequest = objectFactory.createAccountDetailsRequest();
 accountDetailsRequest.setAccountNumber("12345");
    }

    @Test
    public void testGetAccountDetails() throws Exception {
        AccountDetailsResponse response = accountsService.getAccountDetails(accountDetailsRequest);
        assertTrue(response.getAccountDetails()!= null);
        assertTrue(response.getAccountDetails().getAccountNumber().equals("12345"));
        assertTrue(response.getAccountDetails().getAccountName().equals("Joe Bloggs"));
        assertTrue(response.getAccountDetails().getAccountBalance() == 3400);
        assertTrue(response.getAccountDetails().getAccountStatus().equals(EnumAccountStatus.ACTIVE));
    }

}

  • Lines 1 & 2 - Define this as test that relies on Spring managed dependencies. The test application context is loaded form the beans-config-test.xml file we defined earlier. 
  • Lines 5 & 6 - Spring injects an instance of the Accounts Endpoint Service Interface that was generated from the WSDL. The injected instance is a client side proxy to the remote service and is constructed by Spring using the jaxws:client definition we defined in beans-config-test.xml. This is essentially a bridge out to the remote service deployed on Jetty.
  • Lines 9 to 15 - Setup method is called before the test method and is used to create a request object.
  • Lines 17 to 25 - Test method uses injected AccountService client side proxy to call the remote service deployed  on Jetty and performs a number of assertions on the deserialized response.

Running the Test

Running the test is straight forward. Simply fire up a command window and run the mvn test command. You should see the SOAP request and response payloads logged by both the client and the server as follows.

Running on Tomcat 

The service can be deployed to Tomcat manually or you can use the Tomcat maven plugin by running mvn tomcat:run-war from the command line. When the service is deployed we can test it on port 8080 using a SOAP client. We'll use the Eclipse Web Services Explorer to run a quick manual test to ensure the service is up and running.
  • In Eclipse launch the Web Services Explorer, enter the path to the WSDL as shown below and click Go.

  • Click the GetAccountDetails operation

  • Populate the account number parameter and click Go to call the service. In the bottom panel click the Source link to see the raw SOAP request and response payloads.

Wrapping Up

In this post we took a fairly detailed look at building and testing a contract first web service using Apache CXF. The service itself is very simple but the approach used should provide you with a solid grounding for building more complex services. The full source code is available on Github. As always, feel free to post comments, questions or suggestions below. Feedback is always welcome.

Comments

  1. Hi, Brian. This is a great tutorial. Keep up the good work :) ... Just a quick question: Do I have to explicitly write the wsdl as illustrated in the example or deos it get generated for us?

    ReplyDelete
    Replies
    1. Thanks Sydney, glad you like it. We took a contract first approach to developing this service, so yes, you have to write the WSDL yourself. An alternative approach is to write a Java service and then reverse engineer a WSDL from it. This is a contract last approach and is less desirable than contract first.

      Delete

Post a Comment

Popular posts from this blog

Spring Boot & Amazon Web Services (EC2, RDS & S3)

Spring Web Services Tutorial

Health Checks, Metrics & More with Spring Boot Actuator

Spring JMS Tutorial with ActiveMQ

Axis2 Web Service Client Tutorial

An Introduction to Wiremock

Spring Batch Tutorial

Externalising Spring Configuration

Spring Quartz Tutorial