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

Exploring A Top-Down SOAP Service: Part VII

DZone's Guide to

Exploring A Top-Down SOAP Service: Part VII

In this part, we need to add the queueing operation. Here is a look at the operations we are exposing with our SOAP service.

· Web Dev Zone
Free Resource

Learn how to build modern digital experience apps with Crafter CMS. Download this eBook now. Brought to you in partnership with Crafter Software

Our Goal

I had wanted to complete our top-down SOAP project with this article, but it would get really long, so looks like one more after this one.

When we left off in Part VI, we had switched to using inheritance for our desired SOAP response type, and compared it to Part V, where we had used composition.

Next, we need to add the queueing operation. Here is a look at the operations we are exposing with our SOAP service.

The ShapeCalculatorService (I really might have called it Component) interface is from the child Shape Calculator project:

package com.eli.calc.shape.service;

import java.util.List;

import com.eli.calc.shape.domain.CalculationRequest;
import com.eli.calc.shape.domain.CalculationResult;
import com.eli.calc.shape.model.CalcType;
import com.eli.calc.shape.model.ShapeName;

public interface ShapeCalculatorService {

    void deleteAllPendingRequests();

    void deleteAllResults();

    /**
     * 
     * @param shapeName - must not be null;
     * @param calcType  - must not be null;
     * @param dimension - must be greater than / equal to zero;
     * 
     * Attempting to queue another request with the same
     * param values will have no effect.
     * 
     * An IllegalArgumentException will be thrown
     * if any of the params do not meet criteria
     */
    void queueCalculationRequest(
            ShapeName shapeName,
            CalcType calcType,
            double dimension
            );

    /**
     * @return - the list of pending requests
     * (not yet run)
     */
    List<CalculationRequest> getAllPendingRequests();

    /**
     * @return - the list of results
     */
    List<CalculationResult> getAllCalculatedResults();

    /**
     * Runs the calculations of all pending requests
     * in a multi-threaded manner.
     * 
     * Not all request queue attempts will make it
     * into the queue. Any previously queued, or
     * previously run request, will not be re-run.
     * 
     * Pending/Queued requests are thrown away once
     * they have been run.  Thus, an invocation of
     * this operation removes all pending requests
     * that existed at that instance in time.
     * 
     * @return - the number of Requests run
     */
    int runAllPendingRequestsStopOnError();
    int runAllPendingRequestsNoStopOnError();

}

We will look at two of the more interesting operations:

  • queueCalculationRequest() - this article
  • getAllPendingRequests() - the following article

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.

Not as Much Breakdown

I'm going to go as fast as possible, since we've previously covered XSDs, seperate schemas and namespaces and the like.  You can review that in Parts I through VI.

Input Parameters

Looking at the Shape Calculator interface, we see that the queue operation requires three parameters.  We could just mimic that in our new SOAP service wrapper operation.

However, it is possible that we might in the future add another parameter (say, for instance, if we add some sort of security), so...   let's create an overall input parameter object that will contain present and future inputs.

Furthermore, let's tie this new parameter object to the respective queue operation, rather than make it generic.

New Type: QueueRequestParms

This new type is composed of a dimension and two Enums.

Image title

Is There An Issue?

We want to create a new parameter type(QueueRequestParms) for the new (queueing) operation.  But this new type is to be composed of two Enums that are already defined in the child ShapeCalculator project.   But for us to get an auto-generated parameter type, we will need to again define those same Enums in the WSDL/XSDs.

The question, then, is,  are we going to have some sort of dual-maintenance issue later on?

Note that not only will the new parameter type be generated, but so will generated copies of those very same Enums in the very same package name.   (We  will just throw those away since we will be using those from the Shape Calculator)

Hmmm..... we shall see...

WSDL Changes

Let's do the easy stuff first.

We duplicate every section in the WSDL that currently has our single operation (runAllPendingRequestsNoStopOnError), and use it as a template to add the new queueCalculation operation.

The main difference between the first operation, and this new one is that the latter has a <wsdl:part....> in the input message, while both have <wsdl:part....> in the response message.

Here is the WSDL showing only what we have added:


    ..... more code .....


    <wsdl:message name="QueueCalculation">
        <wsdl:part name="parameters" element="parms:QueueRequestParms" />
    </wsdl:message>
    <wsdl:message name="QueueCalculationResponse">
        <wsdl:part name="response" element="resp:QueueResponse" />
    </wsdl:message>

    ..... more code .....


    <wsdl:portType name="ShapeCalculatorWebService">

    ..... more code .....

        <wsdl:operation name="queueCalculation">
            <wsdl:input message="tns:QueueCalculation" />
            <wsdl:output message="tns:QueueCalculationResponse" />
        </wsdl:operation>

    </wsdl:portType>

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

    ..... more code .....

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

    </wsdl:binding>

    ..... more code .....

XSD Response Changes

The new queue operation declares a new type of response so we need to define that new type in our ShapeCalcRespTypes.xsd.

In this case, it is very simple:

    <xsd:element name="QueueResponse" type="resp:StatusResponse" />

XSD Parameter Changes

Moving along to the parameter type, here is the beginning of our WSDL as of now..

Image title

Notice we have the two operations with their input and output messages and their response types.

And the newer queue operation has the parameters type.   We really haven't done anything with the ShapeCalcParmTypes.xsd to this point, but we will now.

So here is the ShapeCalcParmTypes.xsd:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://parms.ws.service.shape.calc.eli.com/" 
    xmlns:tns="http://parms.ws.service.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" />

    <xsd:element name="QueueRequestParms">
        <xsd:complexType>
            <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:element>

</xsd:schema>

Notice that we are importing a new XSD, using another namespace, and we use that in defining our Parms type.

The namespace of the new , imported schema is the same as the package of the pre-existing Enums in our child Shape Calculator project.   ShapeName and CalcType originated there.

New Model XSD

With Complete Enums

And here what that one would look like if we were actually re-defining the Enums:

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

    <xsd:simpleType name="ShapeName">
        <xsd:restriction base="xsd:string">
            <xsd:enumeration value="CIRCLE"/>
            <xsd:enumeration value="SQUARE"/>
            <xsd:enumeration value="EQUILATERALTRIANGLE"/>
            <xsd:enumeration value="CUBE"/>
            <xsd:enumeration value="SPHERE"/>
            <xsd:enumeration value="TETRAHEDRON"/>
        </xsd:restriction>
    </xsd:simpleType>

    <xsd:simpleType name="CalcType">
        <xsd:restriction base="xsd:string">
            <xsd:enumeration value="CALC_AREA"/>
            <xsd:enumeration value="CALC_VOLUME"/>
        </xsd:restriction>
    </xsd:simpleType>

</xsd:schema>

With Empty Enums

And here is the same XSD, minus the Enum values:

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

    <xsd:simpleType name="ShapeName">
        <xsd:restriction base="xsd:string">
        </xsd:restriction>
    </xsd:simpleType>

    <xsd:simpleType name="CalcType">
        <xsd:restriction base="xsd:string">
        </xsd:restriction>
    </xsd:simpleType>

</xsd:schema>

Let's try our service using both versions of the above.

Generate Sources (Filled Enums version)

Image title

Reconcile Code (Filled Enums version)

Looking at above image, I show that we copy the parms and resp packages as-is from generated-sources to  src/main/java .

We copy the ShapeCalculatorWebService interface over, but we comment out this (as we have been since we began this project):

Image title

Why? Because I don't want to have to copy over and maintain the various ObjectFactory classes.

The generated ShapeCalculatorWebServiceImpl we merge with the one already in src/main/java, and we take the shell of the new method, leaving all the rest  in our working copy  as-is.

The remaining step is to complete custom-coding the new queueing operation.

Here is the new operation, queueCalculation(), with our custom code:

    /* (non-Javadoc)
     * @see com.eli.calc.shape.service.ws.ShapeCalculatorWebService#queueCalculation(com.eli.calc.shape.service.ws.parms.QueueCalculationParms parameters)*
     */
    @Generated(value = "org.apache.cxf.tools.wsdlto.WSDLToJava", date = "2016-09-26T19:03:45.979-04:00")
    public com.eli.calc.shape.service.ws.resp.StatusResponse queueCalculation(com.eli.calc.shape.service.ws.parms.QueueRequestParms parameters) { 

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

        com.eli.calc.shape.service.ws.resp.StatusResponse _return = new StatusResponse();

        try {
            calculator.queueCalculationRequest(
                                parameters.getShapeName(), 
                                parameters.getCalcType(), 
                                parameters.getDimension());
            _return.setStatus(StatusCode.SUCCESS);
            _return.setDescription("Queued Calculation: "
                    + parameters.getShapeName() + ", "
                    + parameters.getCalcType() + ", "
                    + parameters.getDimension());
            return _return;
        } catch (java.lang.Exception ex) {
            ex.printStackTrace();
            LOG.debug("\n\n\noperation queueCalculation threw an exception..\n\n\n");
            _return.setStatus(StatusCode.ERROR);
            _return.setClazz(ex.getClass().getName());
            _return.setDescription("Error attempting to Queue Calculation");
            _return.setErrMsg(ex.getMessage());
            if(ex.getCause()!=null) _return.setCauseMsg(ex.getCause().getMessage());
        }
        return _return;
    }

Notice that the return type is a StatusResponse.   However, in the WSDL (go back up) it said QueueResponse.  But a QueueResponse IS a StatusResponse, so that's what is generated.

Notice the operation parameter type is QueueRequestParms (go back up and review the UML diagram for this class).....

Let's take a look at QueueRequestParms:



//... package definition.... imports...  doc...etc....

import com.eli.calc.shape.model.CalcType;
import com.eli.calc.shape.model.ShapeName;


@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "shapeName",
    "calcType",
    "dimension"
})
@XmlRootElement(name = "QueueRequestParms")
@Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-27T11:26:13-04:00", comments = "JAXB RI v2.2.11")
public class QueueRequestParms {

    @XmlElement(required = true)
    @XmlSchemaType(name = "string")
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-27T11:26:13-04:00", comments = "JAXB RI v2.2.11")
    protected ShapeName shapeName;
    @XmlElement(required = true)
    @XmlSchemaType(name = "string")
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-27T11:26:13-04:00", comments = "JAXB RI v2.2.11")
    protected CalcType calcType;
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-27T11:26:13-04:00", comments = "JAXB RI v2.2.11")
    protected double dimension;

  //......... getters and setters......

Notice that it has the correct class and package names that we want.  It is important to note that the com.eli.calc.shape.model package already exists in the child project.  Thus, we can not bring those generated classes over.  We just wanted to correctly generate our parameter class.

We'll drive this point home some more as we go.

In the above-generated sources, we also left out copying a package-info.java. We'll get back to that in a bit.

Build.Deploy.Start

As always, make sure MySQL is up.  Make sure you are using the latest WSDL definition in SOAPUI... and we conduct some tests.

The SOAP service now has two operations:

Image title

Side Issue: Missing package-info.java

We test the queueing one.... and it doesn't matter what (if any) values you choose in your request parameters, we are having a problem.

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <soap:Fault>
         <faultcode>soap:Client</faultcode>
         <faultstring>Unmarshalling Error: unexpected element (uri:"http://parms.ws.service.shape.calc.eli.com/", local:"shapeName"). Expected elements are &lt;{}calcType>,&lt;{}shapeName>,&lt;{}dimension></faultstring>
      </soap:Fault>
   </soap:Body>
</soap:Envelope>

There is something seriously wrong.  Somehow, CXF front end seems confused between what the WSDL says, and the Java classes.

Image title

Copy the package-info.java from generated-sources......parms (and only from parms) over to its same location in  src/main/java .

Again Build.Deploy.Start.Test

And this time we get an expected error since we ran the request with "?" in the parameters.

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <soap:Fault>
         <faultcode>soap:Client</faultcode>
         <faultstring>Unmarshalling Error: ?</faultstring>
      </soap:Fault>
   </soap:Body>
</soap:Envelope>

package-info.java:

@javax.xml.bind.annotation.XmlSchema(namespace = "http://parms.ws.service.shape.calc.eli.com/", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package com.eli.calc.shape.service.ws.parms;

I am just going to quote from some sources...


18
down voteaccepted

package-info.java is a way to apply java annotations at the package level. In this case, Jaxb is using package-level annotations to indicate the namespace and to specify namespace qualification for attributes

(from: What is JAXB generated package-info.java)

And you may find this article helpful: GENERATE NAMESPACE & SCHEMA INFORMATION USING JAXB MARSHALLING.

Continue With Tests

Image title

The above result is interesting, however, at least for now makes sense.   The "a" is definitely not a valid ShapeName, and CXF got involved, and by the time it reaches the interior of our operation, the parameter value is NULL, and the calculator complains.    So this is a good thing.

Let's try a correct value: CIRCLE

Image title

That looks good... and finally... the simulated-error test:

Image title

WSDL/XSD Enum Restriction Definition

In that way, we have set up our project. Here is the actual, working XSD for those Enums:

Image title

 I changed the XSD (see below) and re-built and re-deployed, and started up again... 

Image title

Notice that CIRCLE is no longer a valid value.  Only TETRAHEDRON.

Here is the generated class (that we won't be using):

@XmlType(name = "ShapeName", namespace = "http://model.shape.calc.eli.com/")
@XmlEnum
@Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-28T01:47:12-04:00", comments = "JAXB RI v2.2.11")
public enum ShapeName {

    TETRAHEDRON;

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

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

}

We run the test:

Image title

Comments

We conclude that, as things now stand, CXF is using the real, live ShapeName and CalcType, and the only thing the WSDL was used for (from a service viewpoint), with regard only to the Enums,  was to generate those types.  (Again, we didn't use the generated Enums).  We could do some further research,  but in fact, for now I am happy that it behaves this way - and this leads us back to the question Is There An Issue? For now, I would say, NO.

This, of course, is from our service viewpoint.  Later when we look at developing clients, we'll have to re-look at this.  (Obviously, SOAPUI doesn't care).

Had there been some sort of enforcement to match the WSDL/XSDs, then we would have had a dual-maintenance code issue, because these Enums are pre-defined in the child Shape Calculator project already, and now we also defined them again in this SOAP service schemas.

Anytime we would want to add a new shape (example:  HEXAGON), we have to go to two places.

So I'm happy.

Back To Empty Enums

Remember that I showed two versions of the new Model XSD, and we just covered the first version with the ShapeName and CalcType Enums fully defined.   Let's do a quick study using the second version: empty Enums.

Generate Sources (Empty Enums)

Image title

As we can see (or can't see)  there were no generated ShapeName and CalcType like with the first version.  Hmmm...

So let's take a look at the parameter type, since that's what our real goal was.

QueueRequestParms:


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

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.
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "shapeName",
    "calcType",
    "dimension"
})
@XmlRootElement(name = "QueueRequestParms")
@Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-28T02:29:53-04:00", comments = "JAXB RI v2.2.11")
public class QueueRequestParms {

    @XmlElement(required = true)
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-28T02:29:53-04:00", comments = "JAXB RI v2.2.11")
    protected String shapeName;
    @XmlElement(required = true)
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-28T02:29:53-04:00", comments = "JAXB RI v2.2.11")
    protected String calcType;
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2016-09-28T02:29:53-04:00", comments = "JAXB RI v2.2.11")
    protected double dimension;

    //  ..... getters and setters....
}

The ShapeName and CalcType were converted to just Strings.

We could do that — we could use this version, and then what we would do is change the service implementation  from this:

Dealing With Enums

    @Generated(value = "org.apache.cxf.tools.wsdlto.WSDLToJava", date = "2016-09-26T19:03:45.979-04:00")
    public com.eli.calc.shape.service.ws.resp.StatusResponse queueCalculation(com.eli.calc.shape.service.ws.parms.QueueRequestParms parameters) { 

        try {
            calculator.queueCalculationRequest(
                                parameters.getShapeName(), 
                                parameters.getCalcType(), 
                                parameters.getDimension());

to this:

Dealing With Strings

    @Generated(value = "org.apache.cxf.tools.wsdlto.WSDLToJava", date = "2016-09-26T19:03:45.979-04:00")
    public com.eli.calc.shape.service.ws.resp.StatusResponse queueCalculation(com.eli.calc.shape.service.ws.parms.QueueRequestParms parameters) { 

        try {
            calculator.queueCalculationRequest(
                                ShapeNme.valueOf(parameters.getShapeName()), 
                                CalcType.valueOf(parameters.getCalcType()), 
                                parameters.getDimension());

But I prefer keeping them as Enums for now, so what we can do (to alert others) is this:

Image title

Re-built, deployed, tested, etc....   it works great.

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.

Next ...

I apologize for the length of this article.... and for the number of parts.

We will cover one more operation of this top-down SOAP service project since it returns a list of objects.

Crafter is a modern CMS platform for building modern websites and content-rich digital experiences. Download this eBook now. Brought to you in partnership with Crafter Software.

Topics:
java ,j2ee ,soap ,wsdl ,xsd ,schema ,wsdl2java ,cxf ,web services ,namespaces

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

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

X

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

{{ parent.tldr }}

{{ parent.urlSource.name }}