Over a million developers have joined DZone.

The Java Web App Journey... SOAP Services (Part 3)

As we near the end of our web-enabled Java app's SOAP journey, see how to tackle a few nagging problems, link the service to our front-end, and get testing squared away.

· Java Zone

What every Java engineer should know about microservices: Reactive Microservices Architecture.  Brought to you in partnership with Lightbend.

We left off in part 2, where we had conducted an initial, successful test of the generated WSDL using SOAPUI. Now, it's time to continue continue...

There is a link to the latest code at the end of this article.

Code-WSDL-SOAPUI Cycle

We are going to be going back and forth between generating, viewing, and testing the WSDL (using SOAPUI), and making code and/or annotation changes.

Let's concentrate on the more complex (perhaps) method of the bunch:

queueCalculationRequest()

First, we need to make those XML parameter names meaningful.

For that, we could add @WebParam(name="paramName") to each parameter. This is done in the SEI (Service Endpoint Interface), which is our ShapeCalculatorWebService interface. However, we also need to remove the <!--Optional--> (see below):

Image title


For that, we use @XmlElement(name="paramName", required=true) instead of the @WebParam(...).

And so, our interface now looks like this:

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

import java.util.List;

import javax.jws.WebParam;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlElement;

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;
import com.eli.calc.shape.service.ws.types.CalculatedResultsResponse;
import com.eli.calc.shape.service.ws.types.PendingRequestsResponse;
import com.eli.calc.shape.service.ws.types.SuccessOrErrorResponse;

@WebService
public interface ShapeCalculatorWebService {

    SuccessOrErrorResponse deleteAllPendingRequests();

    SuccessOrErrorResponse deleteAllResults();

    SuccessOrErrorResponse queueCalculationRequest(
            @XmlElement(name="ShapeName", required=true) ShapeName shapeName, 
            @XmlElement(name="CalcType", required=true) CalcType calcType, 
            @XmlElement(name="Dimension", required=true) double dimension);

    PendingRequestsResponse getAllPendingRequests();
    //List<CalculationRequest> getAllPendingRequests();

    CalculatedResultsResponse getAllCalculatedResults();
    //List<CalculationResult> getAllCalculatedResults();

    SuccessOrErrorResponse runAllPendingRequestsStopOnError();
    SuccessOrErrorResponse runAllPendingRequestsNoStopOnError();

}


Also, let's add some debug logging so we can easily tell if we've reached our method body.

Here is our implementation now:

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

import javax.jws.WebService;

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

import com.eli.calc.shape.model.CalcType;
import com.eli.calc.shape.model.ShapeName;
import com.eli.calc.shape.service.ShapeCalculatorService;
import com.eli.calc.shape.service.ws.ShapeCalculatorWebService;
import com.eli.calc.shape.service.ws.types.CalculatedResultsResponse;
import com.eli.calc.shape.service.ws.types.PendingRequestsResponse;
import com.eli.calc.shape.service.ws.types.SuccessOrErrorResponse;

@WebService
public class ShapeCalculatorWebServiceImpl implements ShapeCalculatorWebService {

    private static final Logger logger = LoggerFactory.getLogger(ShapeCalculatorWebServiceImpl.class);

    @Override
    public SuccessOrErrorResponse deleteAllPendingRequests() {

        logger.debug("\n\n\nExecuting operation deleteAllPendingRequests...\n\n\n");

        return null;
    }

    @Override
    public SuccessOrErrorResponse deleteAllResults() {

        logger.debug("\n\n\nExecuting operation deleteAllResults...\n\n\n");

        return null;
    }

    @Override
    public SuccessOrErrorResponse queueCalculationRequest(
            ShapeName shapeName, 
            CalcType calcType, 
            double dimension) {

        logger.debug("\n\n\nExecuting operation queueCalculationRequest...\n\n\n");

        return null;
    }

    @Override
    public PendingRequestsResponse getAllPendingRequests() {

        logger.debug("\n\n\nExecuting operation getAllPendingRequests...\n\n\n");

        return null;
    }

    @Override
    public CalculatedResultsResponse getAllCalculatedResults() {

        logger.debug("\n\n\nExecuting operation getAllCalculatedResults...\n\n\n");

        return null;
    }

    @Override
    public SuccessOrErrorResponse runAllPendingRequestsStopOnError() {

        logger.debug("\n\n\nExecuting operation runAllPendingRequestsStopOnError...\n\n\n");

        return null;
    }

    @Override
    public SuccessOrErrorResponse runAllPendingRequestsNoStopOnError() {

        logger.debug("\n\n\nExecuting operation runAllPendingRequestsNoStopOnError ...\n\n\n");

        return null;
    }

}


After another Maven install, deployment, and startup, we grab the newly generated WSDL: 

http://localhost:8080/web-service-soap-bottom-up-p1/

services/ShapeCalculatorWebService?wsdl 

If we try a request now, or with nothing in it (example: <ShapeName></ShapeName>), or spaces, we get <soap:fault>, soap:client. Plop it into a new SOAPUI project.

Image title

If we type in something more appropriate, we get to see the console output displaying our logging statement for that operation, and we see this:

Image title

One thing to notice is that there was no enforcement of parameter types being passed in. We will come back to that later. Let's keep moving forward.

We are ready to link our web service front-end project and make actual use of the Shape Calculator project. I provided links to that source in the previous articles. I will do that again at the end of this one.

So for this operation, let's add the Calculator. We will @Autowired it.


@WebService
public class ShapeCalculatorWebServiceImpl implements ShapeCalculatorWebService {

    private static final Logger logger = LoggerFactory.getLogger(ShapeCalculatorWebServiceImpl.class);

    @Autowired
    private ShapeCalculatorService calculator;


    //...more code...

    @Override
    public SuccessOrErrorResponse queueCalculationRequest(
            ShapeName shapeName, 
            CalcType calcType, 
            double dimension) {

        logger.debug("\n\n\nExecuting operation queueCalculationRequest...\n\n\n");

        calculator.queueCalculationRequest(shapeName,calcType,dimension);

        return null;
    }

    //...more code...
}

Remember, @Autowired only works if the enclosing class is managed by Spring.

However, you may not want to annotate the class itself (@Service, @Component, etc).

If you read the previous article, I mentioned a possible issue with how we (following the many given examples on the web) might have introduced an error in the WebServiceContext configuration class.  Take a look. We create an Endpoint by calling "new ShapeCalculatorWebServiceImpl()".

That means Spring is not managing the ShapeCalculatorWebServiceImpl, and thus no @Autowired in that class will occur.

We probably could add the @Service annotation to it, and perhaps autowire the implementation inside the WebServiceContext, and then make a reference to it when creating the endpoint.

I chose not to do go that route.  So here are the changes to WebServiceContext:

@Configuration
@ComponentScan(basePackages="com.eli.calc.shape") //this finds base and persist config
//@Import({ShapeCalcBaseContext.class,PersistContext.class})
//@ImportResource({"classpath:META-INF/cxf/cxf.xml"})//required - or use @Bean(Bus.DEF...blah)
public class WebServiceContext {

  ....  code ....

    @Bean
    public ShapeCalculatorWebService shapeCalculatorWebServiceImpl() {
        return new ShapeCalculatorWebServiceImpl();
    }

  ....  code ....

    @Bean
    public Endpoint endpoint() {
        logger.debug("\n\n\n\nELI: WebServiceContext endpoint \n\n\n\n");
        EndpointImpl endpoint = new EndpointImpl(springBus(), shapeCalculatorWebServiceImpl());

        .... code ...

    }

  .... code ....
}


Of note in the above code, you can choose one of either: 

  • @Import(...) the specific configuration classes from our Shape Calculator component project.

  • Or, you might decide to instead do a @ComponentScan(...).

Build. Deploy. Start. Test.

First, we try it with the same parameters as before:

Image title

The above is a bit curious. Recall that I had said there seemed to be no checks, even though the WSDL knows about the valid values for ShapeName and CalcType. The previous test seemed to pass. Something is happening, however, because an improved console output reveals the params have been nulled by the time we enter the  implementation's queueCalculationRequest():

    @Override
    public SuccessOrErrorResponse queueCalculationRequest(ShapeName shapeName, CalcType calcType, double dimension) {

        logger.debug("\n\n\nExecuting operation queueCalculationRequest:"
                    +shapeName+","+calcType+","+dimension+" ...\n\n\n");

        calculator.queueCalculationRequest(shapeName,calcType,dimension);

        return null;
}

The above shows this output:

Executing operation queueCalculationRequest:null,null,1.0

Let's try known good values: "CIRCLE", "CALC_AREA", and 1.

Image title


Latest Code

I have been promising updated code since we started these past three articles on Exploring Web Services.

You can get the web service project's code here.

And you can the the underlying Shape Calculator project's code here.

What's Next

Let's finish off the method we've been expanding.

  • At the moment, we have been returning NULL.

  • We have been returning a SOAP Fault for our app errors.

  • And we can tackle one more of the service opertations, one that returns a list.

Microservices for Java, explained. Revitalize your legacy systems (and your career) with Reactive Microservices Architecture, a free O'Reilly book. Brought to you in partnership with Lightbend.

Topics:
soapui ,web services ,spring ,java

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