Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Exploring A Top-Down SOAP Service: Part V

DZone's Guide to

Exploring A Top-Down SOAP Service: Part V

In Part V, we want to create a new response type for it, that will meet the requirements set out at the start when we were approached to create a service for the Shape Calculator (make-believe story).

· Web Dev Zone
Free Resource

Get deep insight into Node.js applications with real-time metrics, CPU profiling, and heap snapshots with N|Solid from NodeSource. Learn more.

Last Time...

In Part IV, we explored and expanded on WSDLs, XSDs, multiple namespaces.  All of that was in preparation for today's steps.

At the moment, our top-down SOAP service has a single operation.  We want to create a new response type for it, that will meet the requirements set out at the start when we were approached to create a service for the Shape Calculator (make-believe story).

Our Eventual XSD Goals: Create Needed Packages and Classes

Our goal is to create this package and new classes via the XSD:

Image title

The above will be our web service response (output) types.  We will also (later) introduce a request (input) type.

Begin With New Type For RunAllPending.....() Operation

We want our response message to return a RunPendingRequestsResponse, and we want that response to also contain a StatusResponse - in order to meet our requirements.

So first, we update the output message in our WSDL:

    <wsdl:message name="RunAllPendingRequestsNoStopOnErrorResponse">
        <wsdl:part name="response" element="resp:RunPendingRequestsResponse" />
    </wsdl:message>

Then we have to make some changes in the external XSD. (next section)

New Response Type Depends On Other Types

Let's take a look at two ways to define our new type.  Composition, and Inheritance.

Composition

Our first version is to use composition.

Image title

Define New Types in XSD

In the XSD, we need to add all the above.  Here it is.

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://resp.ws.service.shape.calc.eli.com/" 
    xmlns:resp="http://resp.ws.service.shape.calc.eli.com/"
    elementFormDefault="qualified"
    >

    <xsd:simpleType name="StatusCode">
        <xsd:restriction base="xsd:string">
            <xsd:enumeration value="SUCCESS"/>
            <xsd:enumeration value="ERROR"/>
        </xsd:restriction>
    </xsd:simpleType>

    <xsd:annotation>
        <xsd:documentation>The class of the Exception </xsd:documentation>
        <xsd:documentation>The Exception error message</xsd:documentation>
        <xsd:documentation>The cause message if there is one</xsd:documentation>
    </xsd:annotation>
    <xsd:complexType name="StatusResponse">
        <xsd:sequence>
            <xsd:element minOccurs="1" maxOccurs="1" name="status" type="resp:StatusCode" />
            <xsd:element minOccurs="0" maxOccurs="1" name="description" type="xsd:string" />
            <xsd:element minOccurs="0" maxOccurs="1" name="clazz" type="xsd:string"/>
            <xsd:element minOccurs="0" maxOccurs="1" name="errMsg" type="xsd:string"/>
            <xsd:element minOccurs="0" maxOccurs="1" name="causeMsg" type="xsd:string"/>
        </xsd:sequence>
    </xsd:complexType>


    <xsd:element name="RunPendingRequestsResponse">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element name="statusResponse" type="resp:StatusResponse" />
                <xsd:element name="numRun" type="xsd:int" />
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>

</xsd:schema>

Generate Code

We run generate-sources and take a look at the results.

Image title

And the code is.....

ShapeCalculatorWebServiceImpl

@Generated(value = "org.apache.cxf.tools.wsdlto.WSDLToJava", date = "2016-09-25T00:50:08.566-04:00", comments = "Apache CXF 3.1.7")
public class ShapeCalculatorWebServiceImpl implements ShapeCalculatorWebService {

    ......   code here ....

    @Generated(value = "org.apache.cxf.tools.wsdlto.WSDLToJava", date = "2016-09-25T00:50:08.566-04:00")
    public com.eli.calc.shape.service.ws.resp.RunPendingRequestsResponse runAllPendingRequestsNoStopOnError() { 
        LOG.info("Executing operation runAllPendingRequestsNoStopOnError");
        try {
            com.eli.calc.shape.service.ws.resp.RunPendingRequestsResponse _return = null;
            return _return;
        } catch (java.lang.Exception ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex);
        }
    }

Remember that the above is the generated implementation, and we will have to reconcile it with our working copy, but real difference here is just the return type of the operation.

RunPendingRequestsResponse


package com.eli.calc.shape.service.ws.resp;

import javax.annotation.Generated;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for anonymous complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * &lt;complexType&gt;
 *   &lt;complexContent&gt;
 *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&gt;
 *       &lt;sequence&gt;
 *         &lt;element name="statusResponse" type="{http://resp.ws.service.shape.calc.eli.com/}StatusResponse"/&gt;
 *         &lt;element name="numRun" type="{http://www.w3.org/2001/XMLSchema}int"/&gt;
 *       &lt;/sequence&gt;
 *     &lt;/restriction&gt;
 *   &lt;/complexContent&gt;
 * &lt;/complexType&gt;
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "statusResponse",
    "numRun"
})
@XmlRootElement(name = "RunPendingRequestsResponse")
@Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-25T12:50:08-04:00", comments = "JAXB RI v2.2.11")
public class RunPendingRequestsResponse {

    @XmlElement(required = true)
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-25T12:50:08-04:00", comments = "JAXB RI v2.2.11")
    protected StatusResponse statusResponse;
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-25T12:50:08-04:00", comments = "JAXB RI v2.2.11")
    protected int numRun;

    /**
     * Gets the value of the statusResponse property.
     * 
     * @return
     *     possible object is
     *     {@link StatusResponse }
     *     
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-25T12:50:08-04:00", comments = "JAXB RI v2.2.11")
    public StatusResponse getStatusResponse() {
        return statusResponse;
    }

    /**
     * Sets the value of the statusResponse property.
     * 
     * @param value
     *     allowed object is
     *     {@link StatusResponse }
     *     
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-25T12:50:08-04:00", comments = "JAXB RI v2.2.11")
    public void setStatusResponse(StatusResponse value) {
        this.statusResponse = value;
    }

    /**
     * Gets the value of the numRun property.
     * 
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-25T12:50:08-04:00", comments = "JAXB RI v2.2.11")
    public int getNumRun() {
        return numRun;
    }

    /**
     * Sets the value of the numRun property.
     * 
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-25T12:50:08-04:00", comments = "JAXB RI v2.2.11")
    public void setNumRun(int value) {
        this.numRun = value;
    }

}

StatusResponse


package com.eli.calc.shape.service.ws.resp;

import javax.annotation.Generated;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for StatusResponse complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * &lt;complexType name="StatusResponse"&gt;
 *   &lt;complexContent&gt;
 *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&gt;
 *       &lt;sequence&gt;
 *         &lt;element name="status" type="{http://resp.ws.service.shape.calc.eli.com/}StatusCode"/&gt;
 *         &lt;element name="description" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/&gt;
 *         &lt;element name="clazz" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/&gt;
 *         &lt;element name="errMsg" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/&gt;
 *         &lt;element name="causeMsg" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/&gt;
 *       &lt;/sequence&gt;
 *     &lt;/restriction&gt;
 *   &lt;/complexContent&gt;
 * &lt;/complexType&gt;
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "StatusResponse", propOrder = {
    "status",
    "description",
    "clazz",
    "errMsg",
    "causeMsg"
})
@Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-25T12:50:08-04:00", comments = "JAXB RI v2.2.11")
public class StatusResponse {

    @XmlElement(required = true)
    @XmlSchemaType(name = "string")
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-25T12:50:08-04:00", comments = "JAXB RI v2.2.11")
    protected StatusCode status;
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-25T12:50:08-04:00", comments = "JAXB RI v2.2.11")
    protected String description;
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-25T12:50:08-04:00", comments = "JAXB RI v2.2.11")
    protected String clazz;
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-25T12:50:08-04:00", comments = "JAXB RI v2.2.11")
    protected String errMsg;
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-25T12:50:08-04:00", comments = "JAXB RI v2.2.11")
    protected String causeMsg;

    /**
     * Gets the value of the status property.
     * 
     * @return
     *     possible object is
     *     {@link StatusCode }
     *     
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-25T12:50:08-04:00", comments = "JAXB RI v2.2.11")
    public StatusCode getStatus() {
        return status;
    }

    /**
     * Sets the value of the status property.
     * 
     * @param value
     *     allowed object is
     *     {@link StatusCode }
     *     
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-25T12:50:08-04:00", comments = "JAXB RI v2.2.11")
    public void setStatus(StatusCode value) {
        this.status = value;
    }

    /**
     * Gets the value of the description property.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-25T12:50:08-04:00", comments = "JAXB RI v2.2.11")
    public String getDescription() {
        return description;
    }

    /**
     * Sets the value of the description property.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-25T12:50:08-04:00", comments = "JAXB RI v2.2.11")
    public void setDescription(String value) {
        this.description = value;
    }

    /**
     * Gets the value of the clazz property.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-25T12:50:08-04:00", comments = "JAXB RI v2.2.11")
    public String getClazz() {
        return clazz;
    }

    /**
     * Sets the value of the clazz property.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-25T12:50:08-04:00", comments = "JAXB RI v2.2.11")
    public void setClazz(String value) {
        this.clazz = value;
    }

    /**
     * Gets the value of the errMsg property.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-25T12:50:08-04:00", comments = "JAXB RI v2.2.11")
    public String getErrMsg() {
        return errMsg;
    }

    /**
     * Sets the value of the errMsg property.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-25T12:50:08-04:00", comments = "JAXB RI v2.2.11")
    public void setErrMsg(String value) {
        this.errMsg = value;
    }

    /**
     * Gets the value of the causeMsg property.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-25T12:50:08-04:00", comments = "JAXB RI v2.2.11")
    public String getCauseMsg() {
        return causeMsg;
    }

    /**
     * Sets the value of the causeMsg property.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-25T12:50:08-04:00", comments = "JAXB RI v2.2.11")
    public void setCauseMsg(String value) {
        this.causeMsg = value;
    }

}

StatusCode


package com.eli.calc.shape.service.ws.resp;

import javax.annotation.Generated;
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for StatusCode.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * <p>
 * <pre>
 * &lt;simpleType name="StatusCode"&gt;
 *   &lt;restriction base="{http://www.w3.org/2001/XMLSchema}string"&gt;
 *     &lt;enumeration value="SUCCESS"/&gt;
 *     &lt;enumeration value="ERROR"/&gt;
 *   &lt;/restriction&gt;
 * &lt;/simpleType&gt;
 * </pre>
 * 
 */
@XmlType(name = "StatusCode")
@XmlEnum
@Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-25T12:50:08-04:00", comments = "JAXB RI v2.2.11")
public enum StatusCode {

    SUCCESS,
    ERROR;

    public String value() {
        return name();
    }

    public static StatusCode fromValue(String v) {
        return valueOf(v);
    }

}

Reconcile

Since the only important generated change to the service implementation is the return type, we'll just manually change it.  We'll create an instance of the return type in our working copy a bit.

For the interface, we do a code compare / merge....  I copied everything over from the generated file to the working copy in  src/main/java , except for  @XmlSeeAlso(...) .

As for the newly-generated types, we can copy them as-is to  src/main/java .   I mentioned a bit about this in a previous article - we could include generated-sources path as part of our build, but we're going to have to do something about retaining our real, working code (like invocations of the calculator).  I'm sure there are much better ways, but managing the code is not really a focus of this article, so the above is good enough.

Another reason I don't want to include the generated-sources path as part of our build is because we're going to be dealing with Enums from our Shape Calculator (that will also be in the XSD) and we can't have duplicated code.  (more on this later).

Image title

The real changes to our implementation are as a result of wanting to return either a success or error upon completion of the inner calculator operation.

runAllPendingRequestsNoStopOnError()

    @Generated(value = "org.apache.cxf.tools.wsdlto.WSDLToJava", date = "2016-09-21T13:21:57.958-04:00")
    public RunPendingRequestsResponse runAllPendingRequestsNoStopOnError() { 
        LOG.debug("\n\n\nExecuting operation runAllPendingRequestsNoStopOnError\n\n\n");
        RunPendingRequestsResponse _return = new RunPendingRequestsResponse();
        StatusResponse status = new StatusResponse();

        try {

            int numRun = calculator.runAllPendingRequestsNoStopOnError();

            LOG.debug("\n\n\noperation runAllPendingRequestsNoStopOnError returning "+numRun+"\n\n\n");
            status.setStatus(StatusCode.SUCCESS);
            status.setDescription("Ran Any Pending Requests: " + numRun);
            _return.setStatusResponse(status);
            _return.setNumRun(numRun);

        } catch (java.lang.Exception ex) {
            ex.printStackTrace();
            LOG.debug("\n\n\noperation runAllPendingRequestsNoStopOnError threw an exception..\n\n\n");
            status.setStatus(StatusCode.ERROR);
            status.setClazz(ex.getClass().getName());
            status.setDescription("Error attempting to Run Pending Requests");
            status.setErrMsg(ex.getMessage());
            if(ex.getCause()!=null) status.setCauseMsg(ex.getCause().getMessage());
            _return.setStatusResponse(status);
        }
        return _return;
    }

Notice that only one line of code has to do with the business of doing the desired operation.  All the rest is just to return something back.

When we were building our first SOAP service (bottom-up) (see the list of articles), we had total control of our Java response types, and we got creative with smarter constructors, and that allowed us have less code in our implementation.

For this top-down service project, I don't want to have to touch the generated types, otherwise we begin defeating of even doing WSDL-to-Java.

That then means we are limited to using the available setters.

If you are interested in trying to automate reconciliation and retaining code-changes and working with generated classes, here is a discussion about that

We, however, are going to decree what we are doing here to be good-enough because another department is awaiting a first-release (POF) and this is not a huge project with many services and operations, etc.

Build.Deploy.Start

And as always, we make sure MySQL is running.

We got a clean start in the console. Pulling up the WSDL in a browser shows we have new XSDs...

WSDL

Image title

Test

We again take the WSDL URL and in a new SOAPUI project, we try our operation.

Image title

And we shut down MySQL to simulate an error condition and we go again...

Image title

Looks good.

Latest Code

As promised in Part IV, here is the latest code up to this point.

And here is the code for the child (Shape Calculator) project that this web service exposes.

Next Time....

We explored creating our required response type, using Composition.  Let's do this again, but this time use Inheritance.  We can then compare the two.

See you next time!

Node.js application metrics sent directly to any statsd-compliant system. Get N|Solid

Topics:
java ,soap ,wsdl ,xsd ,schema ,web dev

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}