Over a million developers have joined DZone.

Exploring A Top-Down SOAP Service: Part VIII, Finale

What remains is to expose all the other calculator operations. We are only going to explore one of the remaining operations, and for the rest, you can view the sources.

· Web Dev Zone

Make the transition to Node.js if you are a Java, PHP, Rails or .NET developer with these resources to help jumpstart your Node.js knowledge plus pick up some development tips.  Brought to you in partnership with IBM.

Intro

We have taken quite a few articles to arrive at today — where we complete the first release of our top-down SOAP service.   What remains is to expose all the other calculator operations. Please go back to get some context. We are only going to explore one of the remaining operations, and for the rest, you can view the sources.

Let's Explore getPendingRequests()

This one is interesting because the calculator operation returns a List of Calculation Requests.

Immediately we see that we have a similar issue as we had in Part VII, that we have to reference pre-existing classes and packages, contained in our child Shape Calculator project.

The underlying calculator operation is named getAllPendingRequests() but we're going to simplify the external SOAP service operation name, to getPendingRequests().

Latest Code

You can go here for the latest code up to this point. It will be our starting point for this article.

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

New Type: PendingRequestsResponse

Image title

We know from the previous articles of this top-down project, that we decided to have all response types inherit from StatusResponse.  Our new type does that, and has nothing else to offer except a List of CalculationRequest.  Interestingly enough, the pre-existing CalculationRequest seems a lot like the new type we created last article (QueueRequestParms).  It means nothing.   In the future, QueueRequestParms will probably have more parameters.

WSDL Changes

Here is the WSDL, showing only the additions of the new operation with its new response type:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions 

    <!-- ....... more stuff here... -->

    <wsdl:message name="GetPendingRequests" />
    <wsdl:message name="GetPendingRequestsResponse">
        <wsdl:part name="response" element="resp:PendingRequestsResponse" />
    </wsdl:message>


    <wsdl:portType name="ShapeCalculatorWebService">

    <!-- ....... more stuff here... -->

        <wsdl:operation name="getPendingRequests">
            <wsdl:input message="tns:GetPendingRequests" />
            <wsdl:output message="tns:GetPendingRequestsResponse" />
        </wsdl:operation>

    </wsdl:portType>

    <wsdl:binding name="ShapeCalculatorWebServiceSoapBinding" type="tns:ShapeCalculatorWebService">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />

    <!-- ....... more stuff here... -->

        <wsdl:operation name="getPendingRequests">
            <wsdl:input>
                <soap:body use="literal" />
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>

    </wsdl:binding>


    <!-- ....... more stuff here... -->

</wsdl:definitions>

(The XSD imports weren't shown).

XSD Response Changes

We show only the latest changes, related to the new response type:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema 

    <!-- ....... more stuff here... -->

    xmlns:domain="http://domain.shape.calc.eli.com/"

    <!-- ....... more stuff here... -->
    >

    <xsd:import namespace="http://domain.shape.calc.eli.com/" schemaLocation="./ShapeCalcDomain.xsd" />

    <!-- ....... more stuff here... -->


    <xsd:element name="PendingRequestsResponse">
        <xsd:complexType>
            <xsd:complexContent>
                <xsd:extension base="resp:StatusResponse">
                    <xsd:sequence>
                        <xsd:element 
                            name="pendingRequests" 
                            minOccurs="0"
                            maxOccurs="unbounded"
                            nillable="true"
                            type="domain:CalculationRequest"/>
                    </xsd:sequence>
                </xsd:extension>
            </xsd:complexContent>
        </xsd:complexType>
    </xsd:element>

</xsd:schema>


New Domain XSD

Just as we had to do previously with pre-existing ShapeName and CalcType in the model sub-package, we now need to do for the pre-existing CalculationRequest in the domain sub-package.

Here is the new schema in its entirety:

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

    <xsd:import namespace="http://model.shape.calc.eli.com/" schemaLocation="./ShapeCalcModel.xsd" />

    <!-- 
    this type is here just to generate some required types for SOAP service.
    this type is already in-use(originates) in another (base) project
    -->
    <xsd:complexType name="CalculationRequest">
        <xsd:sequence>
            <xsd:element minOccurs="1" maxOccurs="1" name="shapeName" type="model:ShapeName" />
            <xsd:element minOccurs="1" maxOccurs="1" name="calcType" type="model:CalcType" />
            <xsd:element minOccurs="1" maxOccurs="1" name="dimension" type="xsd:double" />
        </xsd:sequence>
    </xsd:complexType>

</xsd:schema>

Generate Sources

Let's take a look...

Image title

If you have been following along with the previous articles, everything above is known, except for what is marked in red.   I also tried to delineate the top box as all the pre-existing (and duplicated) in our child calculator project, and the bottom box is what we are defining for our SOAP service.

Reconcile Code

We are going to copy PendingRequestsResponse as-is, over to its proper place in  src/main/java , and we will again copy the interface (ShapeCalculatorWebService), but comment out the  @XmlSeeAlso .    As  for the implementation (ShapeCalculatorWebServiceImpl), we'll merge it with our working copy by just taking the new operation (getPendingRequests()).

Here is the generated operation:

    /* (non-Javadoc)
     * @see com.eli.calc.shape.service.ws.ShapeCalculatorWebService#getPendingRequests()*
     */
    @Generated(value = "org.apache.cxf.tools.wsdlto.WSDLToJava", date = "2016-09-28T23:52:04.078-04:00")
    public com.eli.calc.shape.service.ws.resp.PendingRequestsResponse getPendingRequests() { 
        LOG.info("Executing operation getPendingRequests");
        try {
            com.eli.calc.shape.service.ws.resp.PendingRequestsResponse _return = null;
            return _return;
        } catch (java.lang.Exception ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex);
        }
    }

And here is our finished version (inside the real class):

    /* (non-Javadoc)
     * @see com.eli.calc.shape.service.ws.ShapeCalculatorWebService#getPendingRequests()*
     */
    @Generated(value = "org.apache.cxf.tools.wsdlto.WSDLToJava", date = "2016-09-28T23:52:04.078-04:00")
    public PendingRequestsResponse getPendingRequests() { 

        LOG.debug("\n\n\nExecuting operation getPendingRequests\n\n\n");

        PendingRequestsResponse _return = new PendingRequestsResponse();

        try {
            List<CalculationRequest> requests = calculator.getAllPendingRequests();

            LOG.debug("\n\n\noperation getPendingRequests returning "+ ((null!=requests)?requests.size():0) +"\n\n\n");
            _return.setStatus(StatusCode.SUCCESS);
            _return.setDescription("Got Pending Requests: " + ((null!=requests)?requests.size():0));

            if (null!=requests) {
                for (CalculationRequest req : requests) {
                    _return.getPendingRequests().add(req);
                }
            }

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

        return _return;
    }

Let's take a look at our new response type:

PendingRequestsResponse


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

import java.util.ArrayList;
import java.util.List;
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;
import com.eli.calc.shape.domain.CalculationRequest;


/**
 * <p>Java class for anonymous complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "pendingRequests"
})
@XmlRootElement(name = "PendingRequestsResponse")
@Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-28T11:52:03-04:00", comments = "JAXB RI v2.2.11")
public class PendingRequestsResponse
    extends StatusResponse
{

    @XmlElement(nillable = true)
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-28T11:52:03-04:00", comments = "JAXB RI v2.2.11")
    protected List<CalculationRequest> pendingRequests;

    /**
     * Gets the value of the pendingRequests property.
     * 
     * <p>
     * This accessor method returns a reference to the live list,
     * not a snapshot. Therefore any modification you make to the
     * returned list will be present inside the JAXB object.
     * This is why there is not a <CODE>set</CODE> method for the pendingRequests property.
     * 
     * <p>
     * For example, to add a new item, do as follows:
     * <pre>
     *    getPendingRequests().add(newItem);
     * </pre>
     * 
     * 
     * <p>
     * Objects of the following type(s) are allowed in the list
     * {@link CalculationRequest }
     * 
     * 
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-28T11:52:03-04:00", comments = "JAXB RI v2.2.11")
    public List<CalculationRequest> getPendingRequests() {
        if (pendingRequests == null) {
            pendingRequests = new ArrayList<CalculationRequest>();
        }
        return this.pendingRequests;
    }

}

I removed very little of it to display.... read the auto-generated javadoc.  This is what forced us to do that for loop in the implementation, instead of just doing a setXXXX().  It is what it is.

Comments About Reconciling

Remember that we are not going to actually use any of the generated packages/classes that are already part of the Shape Calculator child project, as using them would cause a conflict.  The ones we created were for the purpose of generating our needed parameter and response types.

This was easy for us since we had easy access to the pre-existing code, and there was not a lot of work to do...  under other circumstances, perhaps another approach would be to add a mapping layer between the generated classes and the ones required by the calculator component.

Build.Deploy.Start

Reminder - MySQL up.  Also, get the lastest live WSDL...

In SOAPUI we see....

Image title

Test

Execute the operation and we get ...

Image title

If you don't see any when you try it, just run the queueCalculation SOAP request a few times and you should see a list when trying getPendingRequests.

Looks good, right? Maybe, maybe not.

Mix Up of Wrong SOAP Request / Response

Try running the runAllPendingRequestsNoStopOnError SOAP request....

Before I made the request, I wanted to make sure the correct operation was being requested, since by default in SOAPUI, all requests are named Request 1.   So I renamed them:

Image title

Then I executed "run" SOAP request.

Here's what I got:

Image title

What?????

Back to WSDL

We are missing something.  It has to do with   <soap:operation soapAction="....  ./> .

If you do some searching for how-tos  on WSDL-to-Java, you will get a lot of articles showing the     <wsdl:binding ...  section, for every  <wsdl:operation...   block inside of it, the examples will invariably have the following line inserted:

 <soap:operation soapAction="" style="document"/> 

Here is an example of ours, if we follow the online help:

        <wsdl:operation name="runAllPendingRequestsNoStopOnError">

          <soap:operation soapAction="" style="document"/>

            <wsdl:input>
                <soap:body use="literal" />
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>

And that is our problem.

Here is our changed WSDL, showing the entire  <wsdl:binding ....> ... </wsdl:binding>  section only:

    <wsdl:binding name="ShapeCalculatorWebServiceSoapBinding" type="tns:ShapeCalculatorWebService">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />

        <wsdl:operation name="runAllPendingRequestsNoStopOnError">
            <soap:operation soapAction="runAllPendingRequestsNoStopOnError" style="document"/>
            <wsdl:input>
                <soap:body use="literal" />
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>

        <wsdl:operation name="queueCalculation">
            <soap:operation soapAction="queueCalculation" style="document"/>
            <wsdl:input>
                <soap:body use="literal" />
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>

        <wsdl:operation name="getPendingRequests">
            <soap:operation soapAction="getPendingRequests" style="document"/>
            <wsdl:input>
                <soap:body use="literal" />
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>

    </wsdl:binding>

Prior to this point, we didn't even have this line.

 <soap:operation soapAction="" style="document"/>

anywhere in our WSDL.  We hadn't needed it.  I had stated from the start, that as a good learning tool, we add something only when it is truly required. That way we can hopefully gain at least a little understanding, by observing errors.

Generate Code

After doing so, the one important change occurs in the ShapeCalculatorWebService interface.

For each operation, prior to the above change, what we saw was an  @WebMethod  annotation, along with other annotations.  After the above change, that has changed to: @WebMethod("some method") .

Build.Deploy.Start.Test

And this time when we run that request, we get:

Image title

Comments Regarding soapAction

Why is <soap:operation soapAction="" /> so popular as an example, then?  It must work, right?  It does.   One way not to have to specify a soapAction (it is the default) is to make sure every operation is an In-Out type.  In our case, that it have a inbound message have a <wsdl:part......  I didn't want to deal with that.

We Are Done

What remains is to add the remaining calculator operations ,  and there's nothing new there so we won't discuss. Here are all the operations, shown in SOAPUI:

Image title

Latest Code

You can go here for the latest code up to this point. It will be our starting point for this article.

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

Final Thoughts

We have now explored exposing a pre-existing component/service  (the Shape Calculator) via a web application, then via a SOAP service using a bottom-up, code-first approach, and with this article, we concluded our top-down, WSDL-first approach.

Without any regard for what issues any clients would face, or having to later add in security, at the moment I have to say that doing the bottom-up approach seemed easier, quicker, simpler, I was more in control, with no throw-away code.  We didn't have to take an "artificial" steps.

What Is Next?

There's more... 

So, in our make-believe story, you completed what your boss asked for - to re-do the SOAP service using a top-down technique, and you delivered this first release to the department asking for it.

They are happy with the results so far, but they haven't yet really tested it with their processes.  You have thought that you really should re-do the web application (see previous articles) to utilize this just-completed SOAP service, so you can quickly, easily troubleshoot or run the calculator in case they think there's an issue, rather than having to fire up SOAPUI everytime.   Plus, you wonder if you may have to add authentication in the future, or maybe the web application becomes some sort of admin portal to managing the calculator component.....

In the meantime, your boss is now taking this Shape Calculator thing a lot more seriously, and he's looking ahead.

He just came by your cubicle and suggested you start work on also exposing the calculator via a REST service.

And that's where we go to next.

See you in the next article!

Learn why developers are gravitating towards Node and its ability to retain and leverage the skills of JavaScript developers and the ability to deliver projects faster than other languages can.  Brought to you in partnership with IBM.

Topics:
web dev ,soap ,wsdl ,xdl

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

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

{{ parent.tldr }}

{{ parent.urlSource.name }}