Exposing a SOAP WebService under Thorntail

Part of five-cents demo

This topic is part of the ‘old-client’ system of the Five-cents demo :

Five cents demo general schema - backend - legacy client

GitHubThe source code is available on GitHub, under /backends/client-legacy and/framework/utils folder.

Project initialization

For this project, we want to build an old SOAP Client legacy system, and expose it with Thorntail for ease of use, and avoid a bigger monolithic solution here.

As for the REST resource, we will adopt a code-first approach, and use JAX-WS annotations to expose our service under SOAP format.

In order to generate a Thorntail skeleton, we can follow the same path as Exposing JEE JAX-RS resources under Thorntail with Swagger and Swagger-UI embedded but with JAX-WS fractions this time, alias webservices. We should end up with this dependencies :

<!-- Thorntail BOM -->
<dependency>
   <groupId>io.thorntail</groupId>
   <artifactId>webservices</artifactId>
</dependency>
<dependency>
   <groupId>io.thorntail</groupId>
   <artifactId>cdi</artifactId>
</dependency>

Enterprise service

As for the REST resource, you will find the use of an XML loader to get a few clients from scratch. This initialization takes place in the init method of com.fivecents.backends.clientlegacy.enterprise.ClientEnterpriseService :

@Named
@ApplicationScoped
public class ClientEnterpriseService {
   private List<Client> clients;

   @Inject
   private GenericXmlStoreLoader storeLoader;

   @PostConstruct
   private void init() {
      // Let's initialize our clients from xml resource.
      ClientStore clientStore = storeLoader.loadFromResource("clients.xml", ClientStore.class);
      clients = clientStore.getClients();
   }
(...)
}

I let you find out how the GenericXmlLoader is working, if you are interested.

The rest of the ClientEnterpriseService class contains CRUD methods for legacy clients (search clients, update, create, remove).

Jax-WS exposure

As for the Web Service exposure, well, nothing new under the sun, the code-first approach leads us to the writing of the com.fivecents.backends.clientlegacy.soap.ClientEndpoint class with @WebService or @WebMethod annotations :

/**
 * SOAP WebService endpoint for the client service.
 * 
 * @author Laurent CAILLETEAU
 */
@WebService(
   portName="clientPort",
   serviceName="clientService",
   targetNamespace = "http://clientlegacy.fivecents.com/")
public class ClientEndpoint{
   @Inject
   private ClientEnterpriseService clientEnterpriseService;

   @WebMethod(operationName = "findAllClients")
   @WebResult(name = "client")
   public List<Client> findAll() {
      return clientEnterpriseService.getAllClients();
   }

   @WebMethod
   @WebResult(name = "client")
   public Client create(@WebParam(name = "client") Client client) {
      return clientEnterpriseService.createClient(client);
   }

   @WebMethod
   @WebResult(name = "removalSucceeded")
   public boolean remove(@WebParam(name = "id") int clientId) {
      return clientEnterpriseService.removeClient(clientId);
   }

   @WebMethod
   @WebResult(name = "client")
   public Client search(@WebParam(name = "id") int clientId) {
      return clientEnterpriseService.searchForClient(clientId);
   }

   @WebMethod
   @WebResult(name = "updateSucceeded")
   public boolean update(@WebParam(name = "client") Client client) {
      return clientEnterpriseService.updateClient(client);
   }
}

We can now start Thorntail :

mvn clean install
java -jar target/client-legacy-thorntail.jar

The server should display :

2018-09-29 10:25:01,520 INFO [org.jboss.ws.cxf.metadata] (MSC service thread 1-6) JBWS024061: Adding service endpoint metadata: id=com.fivecents.backends.clientlegacy.soap.ClientEndpoint
  address=http://cailleteau-netbook:8080/clientService
  implementor=com.fivecents.backends.clientlegacy.soap.ClientEndpoint
  serviceName={http://clientlegacy.fivecents.com/}clientService
  portName={http://clientlegacy.fivecents.com/}clientPort
  annotationWsdlLocation=null
  wsdlLocationOverride=null
  mtomEnabled=false
(...)
2018-09-29 10:25:03,745 INFO [org.wildfly.extension.undertow] (ServerService Thread Pool -- 5) WFLYUT0021: Registered web context: '/' for server 'default-server'
2018-09-29 10:25:03,783 INFO [org.jboss.as.server] (main) WFLYSRV0010: Deployed "client-legacy.war" (runtime-name : "client-legacy.war")
2018-09-29 10:25:03,795 INFO [org.wildfly.swarm] (main) THORN99999: Thorntail is Ready

Our SOAP web service is exposed on http://localhost:8080/clientService, we can use a SOAPUi test to check responses, located in src/test/resources/client-legacy-soapui-project.xml :

SOAPUi - unqualified

Using qualified namespaces

As you can see on the SOAPUi response, the client XML tag and its children are using the default namespace, and not “http://clientlegacy.fivecents.com/&#8221; namespace (prefix ‘leg’). As there is no value for the default target namespace (no xmls=… in the XML response), its value is “”.

Actually, for highly contractual document as preconized by an SOA approach, I have always preferred using qualified tags instead of unqualified ones (who knows, you may have multiple Client types in your system? So distinguishing them in the SOA monolithic ESB with different namespaces isn’t a bad idea).

To get to that, we just need to add a package-info.java in com.fivecents.backends.clientlegacy.enterprise.beans to add a @XmlSchema annotation directly on the package itself, and specify QUALIFIED for element form default entry :

/**
 * JAXB annotations for the package. We prefer qualified XML form elements, 
 * but generated JAXB elements set it to unqualified. We change that here.
 *
 * @author Laurent CAILLETEAU
 */
@XmlSchema(
   namespace = "http://clientlegacy.fivecents.com/",
   xmlns = {
      @XmlNs(prefix="leg", namespaceURI="http://clientlegacy.fivecents.com/")
   },
   elementFormDefault = XmlNsForm.QUALIFIED)
package com.fivecents.backends.clientlegacy.enterprise.beans;

import javax.xml.bind.annotation.XmlSchema;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;

Let’s do the same for com.fivecents.backends.clientlegacy.soap package :

/**
 * JAXB annotations for the package. We prefer qualified XML form elements, 
 * but generated JAXB elements set it to unqualified. We change that here.
 *
 * @author Laurent CAILLETEAU
 */
@XmlSchema(
   namespace = "http://clientlegacy.fivecents.com/",
   xmlns = {
      @XmlNs(prefix="leg", namespaceURI="http://clientlegacy.fivecents.com/")
   },
   elementFormDefault = XmlNsForm.QUALIFIED)
package com.fivecents.backends.clientlegacy.soap;

import javax.xml.bind.annotation.XmlSchema;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;

Let’s restart Thorntail :

mvn clean install
java -jar target/client-legacy-thorntail.jar

And replay the SOAPUi test :

SOAPUI qualified

This time, the response contains only Qualified tags. We can check this in the generated XML schema, with the elementFormDefault value :

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
   xmlns:tns="http://clientlegacy.fivecents.com/" 
   xmlns:leg="http://clientlegacy.fivecents.com/" 
   elementFormDefault="qualified"
   targetNamespace="http://clientlegacy.fivecents.com/" 
   version="1.0">

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Blog at WordPress.com.

Up ↑

%d bloggers like this: