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

Web App: REST Services - Part I

DZone's Guide to

Web App: REST Services - Part I

This article begins a phase of our make-believe story. We are following the progress of some in-house software that we have been developing and enhancing.

· Integration Zone
Free Resource

Share, secure, distribute, control, and monetize your APIs with the platform built with performance, time-to-value, and growth in mind. Free 90 day trial 3Scale by Red Hat

Introduction

This article begins yet another phase of our make-believe story. We are following the progress of some in-house software that we have been developing and enhancing, from when it became a component or service used by a command-line application, to when it was part of web application, to more recently exposed as a SOAP service (using a bottom-up, code-first approach), to what we just delivered: the same SOAP service, but developed WSDL-first, top-down. (For a complete list of the articles in this series, see here.)

Different people and departments have shown interest in your work and progress and have been the driving force behind all these projects.

You just delivered your most recent release of the SOAP service and are thinking that you really should re-do that web application and make it consume the SOAP service (instead of being tightly coupled to the component). You're thinking that the re-done web app could be useful as a test and to monitor how the SOAP service is being used by the other departments.

However, you have to put those plans momentarily on hold. Your boss has also taken your work more seriously recently, and he is now thinking ahead, and he has asked that you produce REST version of the service as well.

This is where we begin with this first article.

Choosing Our Starting Point

As of now, we have four different options that we could choose from to begin.

  1. The Shape Calculator project itself.

  2. The Bottom-Up SOAP Service project.

  3. The Top-Down SOAP Service project.

  4. A brand-new REST project that then ties in with the Shape Calculator project.

Using the Shape Calculator project directly might be tempting because we could probably knock this out quickly. However, we already know that some of its public operations do not return much information.

We want the REST service responses to be as good as our current (either) SOAP projects. All of the responses should return some clear status (SUCCESS/ERROR) and any additional data.

Also, we have previously decreed that we're not touching the Shape Calculator.

I really am leaning toward a completely new project. However, we can steal a lot of the code from the Bottom-Up SOAP project. I like the service interface (SEI), the implementation, and associated parameter and response classes.

Not as Much Detail

If you think that I omitted some steps or didn't explain something, either it is because it was already discussed in some of the previous articles in this series or there's already a lot of good information on the web available.

Especially keep in mind that I will not be explaining any of the implementation code that we will be copying over (see below). All of that was previously detailed as we developed the Shape Calculator, and later the two SOAP projects. We'll go into more detail as we explore only REST-related changes.

New Project

So, as always, I begin with a Dynamic Web Project in Eclipse (Neon) and I convert to Maven. You might choose different steps. In using the project-creation wizard, this time, I chose to change the content folder from WebContent to  src/main/webapp since this is a standard Maven folder.

Copy Initial Classes

Here is our starting point:

The POM doesn't have much new in it other than a <properties>...</properties> section detailing the various versions of the dependencies (CXF, Spring).

I copied the SEI and the Implementation from here and removed any annotations since we want to deal only with REST-related changes for now in order to keep things simple and clear as to what we are doing.


We need to copy over the response and parameter classes as well, thus clearing the above errors.


Our SEI looks better.


2. Add Child Project Dependency

We are still missing some types. ShapeName and CalcType happen to be classes in the child Shape Calculator project (get sources from here), so we need to add a dependency to the POM:

    <dependencies>

        <dependency>
            <groupId>shape-calc-jpa-hibernate</groupId>
            <artifactId>shape-calc-jpa-hibernate</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

    </dependencies>

We do a Maven update, and the problem clears.

Minor Changes

One of the things I do like about the top-down SOAP service is what we named some of the types. I am going to change SuccessOrErrorResponse to StatusResponse and RespCode to StatusCode.

Take a look at the Implementation. It is essentially a wrapper for the Shape Calculator, adding some better responses.

Parameter Changes

Another improvement we can borrow from the top-down SOAP service project is how we used one parameter type rather than discrete parts. So, let's improve our current SEI and implementation to match that improvement.

We change from this...

    StatusResponse queueCalculationRequest(
            ShapeName shapeName, 
            CalcType calcType, 
            double dimension);

...to this:

    StatusResponse queueCalculation(QueueRequestParms parameters);

You'll see an error; we have to copy from the response and parameter classes again, this time, the com.eli.calc.shape.service.ws.parms package. Then, I want to rename QueueRequestParms to QueueCalculationParms.

Since we changed the SEI, we have to change the implementation to match.

Here is the updated implementation, showing that one operation:

    @Override
    public StatusResponse queueCalculation(QueueCalculationParms parameters) {

        logger.debug("\n\n\nExecuting operation queueCalculationRequest:"
                +parameters.getShapeName()+","
                +parameters.getCalcType()+","+
                parameters.getDimension()+" ...\n\n\n");

        StatusResponse response = null;
        try {

            calculator.queueCalculationRequest(
                parameters.getShapeName(), parameters.getCalcType(), parameters.getDimension()
                    );
            response = new StatusResponse(
                    StatusCode.SUCCESS,
                    "Request queued:"
                    +parameters.getShapeName()+","
                    +parameters.getCalcType()+","+
                    parameters.getDimension());

        } catch (Exception e) {
            logger.error("Error attempting to queueCalculationRequest",e);
            response = new StatusResponse("Error Attempting to Queue Calculation Request",e);
        }

        return response;
    }

3. Try a Build

And we get a clean build.

4. Try a Server Start


We got a good start however not much happened. I don't see any servlet-related, spring-related, or hibernate-related output.

5. Add Web App Initialization

We can take a copy directly from one of our previous SOAP projects and use it as-is.


More Dependencies

Seems we are missing Spring Web and CXF transports.

    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-transports-http</artifactId>
        <version>${cxf.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.version}</version>
    </dependency>

Those cleared the issues with the WebAppInitializer. However, if you try a Maven install, you'll find it is still missing another dependency:

    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
    </dependency>

5. Web Service Context

We have a web application initialization, but we need more. We also copied over the WebServiceContext, however as it now is, the code is to initialize a SOAP service, not REST. That is, we have it configuring a JAX-WS endpoint, but we need JAX-RS.

Here is the WebServiceContext as it now stands without any changes:


It complains because of a missing dependency, this one:

    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-frontend-jaxws</artifactId>
        <version>${cxf.version}</version>
    </dependency>

Except we don't want that one since it is for JAX-WS. We want one for JAX-RS. The dependency we need is similar:

    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-frontend-jaxrs</artifactId>
        <version>${cxf.version}</version>
    </dependency>

(Interesting how a single-letter change (r) makes all the difference.)

Some Review of WebServiceContext

Briefly, at the class level, the @Configuration and the @ComponentScan(....  annotations tell Spring about setting up context. The packages named to be scanned and exist in the base/child Shape Caluclator project. If you go there, you will see more configuration.

I added a default constructor just to see at what point during the server startup did this class get involved.

Primary Goal of WebServiceContext: Create Our Service

In order to do that, we'll need to create a Jax-RS Server.

jaxRsServer()

We are going to add and expand the following:

@Bean
public Server jaxRsServer() {

    logger.debug("\n\n\n\nWebServiceContext JaxRsServer \n\n\n\n");

    //TO DO : return a real server

    return null;
}

The  @Bean , of course, indicates to Spring to manage it.

Step By Step

Let's really break down the steps and increment slowly as we study what we add to the WebServiceContext. We do this to learn what exactly do we need, and what errors might we encounter.

1. Build.Deploy.Start

At this point, you should have started MySQL. Please go here for more details.

I saw a clean start in the Eclipse console, and also in the above logging statement go-by. The WebAppInitializer did its thing. Now, we see a lot of Spring context and Hibernate because of the annotations at the top of the WebServiceContext.

Then we hit our first error.


We saw this problem before when we first began exploring the bottom-up SOAP service.

Add the following to get past the error:

    @Bean(name = Bus.DEFAULT_BUS_ID) //this is REQUIRED or you get "No bean named 'cxf' is defined"
    public SpringBus cxf() {
        logger.debug("\n\n\n\nWebServiceContext cxf (SpringBus) \n\n\n\n");
        return new SpringBus();
    }

We now have a clean start up.

2. Test

If we try a browser, we get:


3. Back to jaxRsServer()

Let's add to the method.

@Bean
public Server jaxRsServer() {

    logger.debug("\n\n\n\nWebServiceContext JaxRsServer \n\n\n\n");

    JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean();

    return factory.create();
}

4. Build.Deploy.Start

And we get this:


...of course, because the service that we wish to expose is the resource, and we have not informed this new server factory about it.

5. Back to jaxRsServer()

We want to initialize the implementation (ShapeCalculatorWebServiceImpl). We could just do a "new" inside the jaxRsServer() method. However, the next one will need Spring to manage implementation because the underlying calculator component or service is @Autowired.

Let's add to the method:

@Bean
public Server jaxRsServer() {

    logger.debug("\n\n\n\nWebServiceContext JaxRsServer \n\n\n\n");

    JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean();
    factory.setServiceBean(shapeCalculatorWebServiceImpl());

    return factory.create();
}

6. Add Implementation Bean

And that means we need a new method:

    @Bean
    public ShapeCalculatorWebService shapeCalculatorWebServiceImpl() {
        logger.debug("\n\n\n\nWebServiceContext shapeCalculatorWebServiceImpl \n\n\n\n");
        return new ShapeCalculatorWebServiceImpl();
    }

7. Build.Deploy.Start

Interestingly enough, we still get the same no resource classes found. It turns out that it is not enough to merely initialize the bean and give it to the new server factory. We need to add some annotations to our SEI. There are different ways to handle resources.

I also want to take the time now and rename the SEI's methods, removing the "All" from each.

8. ShapeCalculatorWebService (SEI)

Here is one operation with some new annotations:

    @GET
    @Path("/pending")
    PendingRequestsResponse getPendingRequests();

9. Build.Deploy.Start.Test

We got a clean start this time. If we point our browser again to the base services URL:

 http://localhost:8080/1-ws-rest-p1/services 

...we get a blank page. The key console output is this:


10. More Changes to to jaxRsServer()

The reason for the above error is that we need to somehow map the service to a URL. So, we add:

@Bean
public Server jaxRsServer() {

    logger.debug("\n\n\n\nWebServiceContext JaxRsServer \n\n\n\n");

    JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean();
    factory.setServiceBean(shapeCalculatorWebServiceImpl());
    factory.setAddress("/shapecalc");

    return factory.create();
}

Do not miss the "/" (slash).

11. Build.Deploy.Start.Test

We got a clean start this time.  If we point our browser again to the base services URL:

 http://localhost:8080/1-ws-rest-p1/services 

we get:


So far, so good. Remember we not only map the service, but we mapped one operation to  /pending 

Let's try it:


Our service method did run and the console shows extensive hibernate output. However, the CXF framework doesn't know how to handle the PendingRequestsResponse.

12. Back to the SEI

We add the necessary annotation at the class-level to indicate to CXF to respond with XML:

@Produces({MediaType.APPLICATION_XML})

Here is just a relevant portion of the SEI:

@Produces({MediaType.APPLICATION_XML})
public interface ShapeCalculatorWebService {

    @GET
    @Path("/pending")
    PendingRequestsResponse getPendingRequests();

13. Build.Deploy.Start.Test

We got a clean start this time. If we point our browser again to the complete URL ending in  /pending, we get:


This time, the errors in the console are not as many in the above image with all of the yellow highlights. However, we still have a problem, and it is still related to CXF not knowing how to return the PendingRequestsResponse.

14. PendingRequestsResponse

Let's add an  @XmlRootElement to the response class.

@XmlRootElement
public class PendingRequestsResponse extends StatusResponse {

    private List<CalculationRequest> pendingRequests;

    private int numPending;

    //..... rest of the class code here..

15. Spring Bean Load Order

And we finally hit a wall of sorts. The frequency of getting "No services were found" has been increasing, seemingly as we made changes, without any set reason. In keeping with my tactic of not adding anything until necessary, I delayed adding this:  @DependsOn("cxf").

We have arrived at that moment.

16. More Changes to to jaxRsServer()

Add the above-mentioned annotation:

@Bean
@DependsOn("cxf")
public Server jaxRsServer() {

    logger.debug("\n\n\n\nWebServiceContext JaxRsServer \n\n\n\n");

    JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean();
    factory.setServiceBean(shapeCalculatorWebServiceImpl());
    factory.setAddress("/shapecalc");

    return factory.create();
}

17. Build.Deploy.Start.Test

We got a clean start. If we point our browser again to the complete URL ending in /pending, we get:


Excellent! We have arrived.

Coming Up Next

This is a good place to stop. Let's continue next time by examining some of the other operations and how to (or even if we can) relate them to RESTful principles using the HTTPmethods. We also want to add JSONcapabilities, and we want to take a look at a WADLand use it in the SOAPUI tool.

Latest Code

You can get all of this project's code that we have covered up to this point here. You can get the child project (Shape Calculator), either here, or via the article links I featured up top where we were deciding what project to use as a starting point.

Discover how you can achielve enterpriese agility with microservices and API management

Topics:
integration ,rest api ,soap ,web applications

Opinions expressed by DZone contributors are their own.

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

{{ parent.tldr }}

{{ parent.urlSource.name }}