Over a million developers have joined DZone.

Exploring a Top-down SOAP Service: Part III

Come read Part III of Eli Corrales's series about exploring a top-down SOAP service. This time, we'll add a shape calculator, an informational response, and more!

· Web Dev Zone

Start coding today to experience the powerful engine that drives data application’s development, brought to you in partnership with Qlik.

Last time, we established a working baseline. We assured ourselves that we could make a change in the WSDL, generate the code, copy/merge the generated copy to our working copy, install/deploy, and test it. Let's keep going.

Latest Code

You can get the latest code here.

And for our dependency on our child (Shape Calculator project), here.

Add Shape Calculator

We want our external (API) runAllPendingRequestsNoStopOnError() to actually DO something, so we add the calculator and we call the very same operation on it:

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

@Autowired
private ShapeCalculatorService calculator;

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


            int numRun = calculator.runAllPendingRequestsNoStopOnError();

        } catch (java.lang.Exception ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex);
        }
    }

Build.Deploy.Run.Test

This time, when we try hitting our runAllPendingRequestsNoStopOnError() operation, we see a lot more output from the console. We see hibernate get into the act, so we are definitely doing what we want. However, there is no feedback at all. And that calculator method does return the number of calculations run, which might be useful, but we can't pass that back to our SOAP client the way we have the response set up.

One more test. Let's temporarily shut down MySQL...  (again please refer to the several previous articles on this)

Image title

Image title

Now let's have SOAPUI do its thing.

Notice that the Eclipse console has a long trace of exceptions because Hibernate (in the base Shape Calculator project) cannot access the data store.

But there was absolutely no indication of any problems in the SOAPUI response. Nada.

This is not good, and it doesn't meet our previous requirements.

Add Response To Operation In WSDL

So our goal is to have the operation we've been exercising to return some sort of response, rather than void.

We go back to edit and enhance the WSDL. Let's take a look at it. You'll notice that in the   <wsdl:message...> section, and in the  <wsdl:portType......> section, and in the  <wsdl:binding....> section, everything indicates that our single operation is one-way.  In. We send the runAllPendingRequestsNoStopOnError message, and that's it.

So we need to add a corresponding Out portion to every place there's an In.

Here is our first stab at the complete WSDL, with the new parts added.

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions 
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:tns="http://ws.service.shape.calc.eli.com/" 
    targetNamespace="http://ws.service.shape.calc.eli.com/" 
    >

        <wsdl:message name="runAllPendingRequestsNoStopOnError">
        </wsdl:message>

        <wsdl:message name="runAllPendingRequestsNoStopOnErrorResponse">
        </wsdl:message>

        <wsdl:portType name="ShapeCalculatorWebService">
            <wsdl:operation name="runAllPendingRequestsNoStopOnError">

                <wsdl:input name="runAllPendingRequestsNoStopOnError" message="tns:runAllPendingRequestsNoStopOnError">
                </wsdl:input>

                <wsdl:output name="runAllPendingRequestsNoStopOnErrorResponse" message="tns:runAllPendingRequestsNoStopOnErrorResponse">
                </wsdl:output>

            </wsdl:operation>
        </wsdl:portType>

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

            <wsdl:input name="runAllPendingRequestsNoStopOnError">
                <soap:body use="literal"/>
            </wsdl:input>

            <wsdl:output name="runAllPendingRequestsNoStopOnErrorResponse">
                <soap:body use="literal"/>
            </wsdl:output>

        </wsdl:operation>
    </wsdl:binding>

    <wsdl:service name="ShapeCalculatorWebService">
        <wsdl:port name="ShapeCalculatorWebService" binding="tns:ShapeCalculatorWebServiceSoapBinding">
            <soap:address location="http://localhost:8080/web-service-soap-bottom-up/services/ws/ShapeCalculatorWebService"/>
        </wsdl:port>
    </wsdl:service>

</wsdl:definitions>

Now we clean and generate-sources, adjust our code, etc., install, run it and test it.

Remember that any time you change the WSDL, you'll need to go re-do your SOAPUI project:

http://localhost:8080/ws-soap-top-down-p3/services/ShapeCalculatorWebService?wsdl 

You might also want to view it in the browser to make sure it did in fact change.

So, this time, we have something more.

Image title

Let's try the error test by shutting down MySQL.

Image title

OK, now at least we know it will not fail silently.

Add Informational Response

What we would like to do, only for now as a test, is to be at least able to return the int value from the calculator.runAllPendingRequestsNoStopOnError() as part of the SOAP response.

So we add a response type to the response message:

<wsdl:message name="runAllPendingRequestsNoStopOnErrorResponse">
    <wsdl:part name="response" element="RunAllResponse" />
</wsdl:message>

However, that's not enough because the element value has to be in a namespace. Before we worry about that too much, let's try another generate-sources.

Re-generating gives us this:

[ERROR] Part <response> in Message 
<{http://ws.service.shape.calc.eli.com/}
runAllPendingRequestsNoStopOnErrorResponse> 
referenced Type 
<{http://ws.service.shape.calc.eli.com/}somename> 
cannot be found in the schemas

Add new WSDL section:  TypesWhat schemas?

We need a new section that will define the elements we declared in the   <wsdl:message.....> .  We declared an element of type RunAllResponse, so now we need to define that element.

    <wsdl:types>
        <schema>
            ...... something here defining element....
        </schema>
    </wsdl:types>

It really isn't my intention to teach too much about WSDLs but I hope you have noticed that all of the WSDL sections or blocks, exist in the wsdl:  namespace.   And that namespace was declared at heading of the    <wsdl:definitions......> .

Right now, the above code, has the   <schema>     without an explicit namespace.   If we try to generate-sources, we'll get:

[ERROR] Failed to execute goal org.apache.cxf:cxf-codegen-plugin:3.1.7:wsdl2java (generate-sources) on project ws-soap-top-down-p3: Execution generate-sources of goal org.apache.cxf:cxf-codegen-plugin:3.1.7:wsdl2java failed: org.apache.cxf.wsdl11.WSDLRuntimeException: Fail to create wsdl definition file:/C:/_dzone-article-sandbox/eclipse-workspace/ws-soap-top-down-p3/WebContent/wsdl/ShapeCalcWebService.wsdl: WSDLException (at /wsdl:definitions/wsdl:types/schema): faultCode=INVALID_WSDL: Encountered illegal extension element 'schema' in the context of a 'javax.wsdl.Types'. Extension elements must be in a namespace other than WSDL's. -> [Help 1]

Which then brings us to the question:  What Namespace?

Well..  we can put the namespace we need AND define it right in the    <schema...>  block, just like the namespace wsdl: was defined and used.

Here it the above  <wsdl:types......>  block re-done:

    <wsdl:types>
        <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
            ...... some definition here.....
        </xsd:schema>
    </wsdl:types>

Now let's take a stab at defining a simple type; our RunAllResponse, which we want to be an integer.

    <wsdl:types>
        <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
            <xsd:element name="RunAllResponse" type="xsd:int" />
        </xsd:schema>
    </wsdl:types>

What we see above is that a new custom element (RunAllResponse) is defined as an integer, and that integer type itself has already been defined by the XMLSchema, and due to our  xsd: namespace  declaration, it exists there, thus the  xsd:int  type.

Ok, we have defined our new element. And we had previously tried to declare its use in our outbound or response message.  Before we go any further, let's see our complete WSDL again, with what we've just added.

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions 
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:tns="http://ws.service.shape.calc.eli.com/" 
    targetNamespace="http://ws.service.shape.calc.eli.com/" 
    >

    <wsdl:types>
        <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
            <xsd:element name="RunAllResponse" type="xsd:int" />
        </xsd:schema>
    </wsdl:types>

        <wsdl:message name="runAllPendingRequestsNoStopOnError">
        </wsdl:message>

        <wsdl:message name="runAllPendingRequestsNoStopOnErrorResponse">
            <wsdl:part name="response" element="RunAllResponse" />
        </wsdl:message>

        <wsdl:portType name="ShapeCalculatorWebService">
            <wsdl:operation name="runAllPendingRequestsNoStopOnError">

                <wsdl:input name="runAllPendingRequestsNoStopOnError" message="tns:runAllPendingRequestsNoStopOnError">
                </wsdl:input>

                <wsdl:output name="runAllPendingRequestsNoStopOnErrorResponse" message="tns:runAllPendingRequestsNoStopOnErrorResponse">
                </wsdl:output>

            </wsdl:operation>
        </wsdl:portType>

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

            <wsdl:input name="runAllPendingRequestsNoStopOnError">
                <soap:body use="literal"/>
            </wsdl:input>

            <wsdl:output name="runAllPendingRequestsNoStopOnErrorResponse">
                <soap:body use="literal"/>
            </wsdl:output>

        </wsdl:operation>
    </wsdl:binding>

    <wsdl:service name="ShapeCalculatorWebService">
        <wsdl:port name="ShapeCalculatorWebService" binding="tns:ShapeCalculatorWebServiceSoapBinding">
            <soap:address location="http://localhost:8080/web-service-soap-bottom-up/services/ws/ShapeCalculatorWebService"/>
        </wsdl:port>
    </wsdl:service>

</wsdl:definitions>

Let's try another generate-sources.

And it works.   We have a clean console output.  Here is the next version of the auto-generated implementation, showing only the operation:

    /* (non-Javadoc)
     * @see com.eli.calc.shape.service.ws.ShapeCalculatorWebService#runAllPendingRequestsNoStopOnError()*
     */
    @Generated(value = "org.apache.cxf.tools.wsdlto.WSDLToJava", date = "2016-09-22T01:50:25.464-04:00")
    public int runAllPendingRequestsNoStopOnError() { 
        LOG.info("Executing operation runAllPendingRequestsNoStopOnError");
        try {
            int _return = 0;
            return _return;
        } catch (java.lang.Exception ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex);
        }
    }

Reconcile Sources (In-use vs Generated)

Since the above is the generated version, we're going to manually reconcile it with the one we took and used from our first success (that we moved to another package) in the previous article.

Image title

Really, the only change that matters is we need to change our working version to return an  int .  And we also need to change the interface class to return an int.

ShapeCalculatorWebService

Here is the updated, working copy of the interface:

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

import javax.annotation.Generated;
import javax.jws.Oneway;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;

/**
 * This class was generated by Apache CXF 3.1.7
 * 2016-09-21T12:04:15.336-04:00
 * Generated source version: 3.1.7
 * 
 */
@WebService(targetNamespace = "http://ws.service.shape.calc.eli.com/", name = "ShapeCalculatorWebService")
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
@Generated(value = "org.apache.cxf.tools.wsdlto.WSDLToJava", date = "2016-09-21T12:04:15.336-04:00", comments = "Apache CXF 3.1.7")
public interface ShapeCalculatorWebService {

    @WebMethod
    @Oneway
    @Generated(value = "org.apache.cxf.tools.wsdlto.WSDLToJava", date = "2016-09-21T12:04:15.336-04:00")
    public int runAllPendingRequestsNoStopOnError();
}

ShapeCalculatorWebServiceImpl

And here is the updated, working copy of the implementation:


/**
 * Please modify this class to meet your needs
 * This class is not complete
 */

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.Generated;
import javax.jws.Oneway;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;

import com.eli.calc.shape.service.ShapeCalculatorService;
import com.eli.calc.shape.service.ws.ShapeCalculatorWebService;

/**
 * This class was generated by Apache CXF 3.1.7
 * 2016-09-21T13:21:57.958-04:00
 * Generated source version: 3.1.7
 * 
 */

@javax.jws.WebService(
                      serviceName = "ShapeCalculatorWebService",
                      portName = "ShapeCalculatorWebService",
                      targetNamespace = "http://ws.service.shape.calc.eli.com/",
                      wsdlLocation = "file:/C:/_dzone-article-sandbox/eclipse-workspace/ws-soap-top-down-p2/WebContent/wsdl/ShapeCalcWebService.wsdl",
                      endpointInterface = "com.eli.calc.shape.service.ws.ShapeCalculatorWebService")

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

    @Generated(value = "org.apache.cxf.tools.wsdlto.WSDLToJava", date = "2016-09-21T13:21:57.958-04:00")
    private static final Logger LOG = LoggerFactory.getLogger(ShapeCalculatorWebServiceImpl.class);

    @Autowired
    private ShapeCalculatorService calculator;

    /* (non-Javadoc)
     * @see com.eli.calc.shape.service.ws.ShapeCalculatorWebService#runAllPendingRequestsNoStopOnError()*
     */
    @Generated(value = "org.apache.cxf.tools.wsdlto.WSDLToJava", date = "2016-09-21T13:21:57.958-04:00")
    public int runAllPendingRequestsNoStopOnError() { 
        LOG.debug("\n\n\nExecuting operation runAllPendingRequestsNoStopOnError\n\n\n");
        try {

            int numRun = calculator.runAllPendingRequestsNoStopOnError();

            return numRun;

        } catch (java.lang.Exception ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex);
        }
    }

}

Yes, we could have instead copied the latest auto-generated interface to our working ( src/main/java)  area, but that introduces something new:

 @XmlSeeAlso({com.eli.calc.shape.service.ws.types.ObjectFactory.class}) 

and I don't want to deal with that yet, as we don't need it.

Build.Deploy.Start.WSDL.Test

Make sure MySQL is running.

We stumbled onto an error during startup.  There are lots of traces, but it all boils down to this:

Caused by: java.io.FileNotFoundException: C:\_dzone-article-sandbox\eclipse-workspace\ws-soap-top-down-p2\WebContent\wsdl\ShapeCalcWebService.wsdl (The system cannot find the path specified)

I purposely left something unchanged so we would see this problem.   Remember that we are maintaining a working copy of our service implementation class, and we are just bringing over the changes we want from our auto-generated class.   (We may switch that around and start using the auto-generated class and add our working code to it).

Also, I have been creating new projects for every part of these articles, i.e., there is a ws-soap-top-down-p1, -p2, -p3, and so on.

The point is that the above error was caused by wsdlLocation.

Our project POM, as of now, has in it  <wsdl> , but not  <wsdlLocation> , and  it has well-known value:

    <wsdl>${basedir}/WebContent/wsdl/(your wsdl service name here).wsdl<wsdl> 

and that is a very common suggestion in many online articles I've seen.

The above causes the following to be generated in the service implementation:

Image title

In my case, since my current working project is "-p3", it can't find the file.  What could we do?

  • change the "-p2" to "-p3"   (yuck)
  • use the auto-generated impl class and move our changes to it (yes, we could)
  • add a new tag <wsdlLocation> , and change from absolute path to  classpath:  (see below)
  • comment out that entire  wsdLocation="..blah..blah.."  line in the annotated code  (eh... yuck)

<wsdlLocation> Absolute vs Classpath

CXF wsdl2java plugin used the   <wsdl>.... </wsdl> to generate the above.   We can instead provide our own  <wsdlLocation>.... </wsdlLocation>.  This is a common online suggestion as well.

For this option, we add to our project POM:

<wsdlLocation>classpath:wsdl/ShapeCalcWebService.wsdl</wsdlLocation>

I added that immediately below the  <wsdl>.... </wsdl>

Generating sources again gives us this:

Image title

If I move that change over and re-install , start up server,  etc.. we get a clean start.

Let's view the working WSDL...

 http://localhost:8080/ws-soap-top-down-p3/services/ 

Image title

We click that link:

 http://localhost:8080/ws-soap-top-down-p3/services/ShapeCalculatorWebService?wsdl 

Image title

Time for our test in SOAPUI.   Let's create a fresh SOAP project with our WSDL URL.. and run the test.

Ok, looks better.  If you recall from our prior tests, the SOAP response used to be blank.  Now we are seeing a response.

Image title

Latest Code

You can get the latest code here.

What Is Next?

In the next article, we are going to explore more about the schema and separate out the schema information into a separate file.  We will create the types that we need to meet our previous requirements.

So, stay tuned!

Create data driven applications in Qlik’s free and easy to use coding environment, brought to you in partnership with Qlik.

Topics:
java 8 ,j2ee ,soap ,wsdl ,wsdl2java ,maven ,web services ,cxf

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 }}