Web Services

30
Dec

SOAP Web-service by means of Spring-WS

Posted by eugene as Spring Framework, Web Services

Webservices

Once I’ve got a task to start developing of the web services and had the sources of a simple project without any explanation. The project, for sure couldn’t be laundched. What is Spring and how it works, I couldn’t also imagine. I also couldn’t find any adequate articles of developing the web services by means of Spring – neither Russian, nor English. I had to learn everything by myself, and it appeared all wasn’t so awful. And just recently I decided to see what new opportunities were added in Spring since then, and to update old services what entailed writing of this article.

This article is the guide of simple Web service development that uses SOAP protocol.

Thus we’ll write the simple service that accepts the username and sends a greeting and the current time on the server.

what will we need?

Preparation for work

Let’s create a new Web application project. In Eclipse this will be: “File => New => Dynamic Web Project”. I’ve called the project HelloService. Then let’s copy libraries from Spring, XMLBean, wsdl4j, commons-logging into the WEB-INF/lib project catalogue. If you wish you can add them to the server libraries in order not to move them with each application.

Creation the WSDL scheme

In fact, WSDL scheme is intended to describe the service. We won’t certainly create it manually. The scheme will be generated automatically by Spring, but more on that will be mentioned later.

Defining input and output

Input data:

  • String name

Output data:

  • Greeting string;
  • Current time.

Creating the description of input and output data

Let’s create HelloService.xsd file in the WEB-INF catalogue. This file will be necessary for generating WSDL scheme and creating the corresponding Java classes.

File contents:

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/HelloService" elementFormDefault="qualified">
  <element name="ServiceRequest">
    <complexType>
      <sequence>
        <element name="name" type="string" maxOccurs="1" minOccurs="1"/>
      </sequence>
    </complexType>
  </element>
  <element name="ServiceResponse">
    <complexType>
      <sequence>
        <element name="hello" type="string" maxOccurs="1" minOccurs="1"/>
        <element name="currentTime" type="time" maxOccurs="1" minOccurs="1"/>
      </sequence>
    </complexType>
  </element>
</schema>

The targetNamespace attribute is used namespace. I.e. all the created objects will be located in the org.example.hellpService package.

The ServiceRequest and ServiceResponse describe input and output data correspondly (request/answer).

The minOccurs and maxOccurs attributes define the number of repeats of the given component within one single element. If not to specify these options, then by default they are considered to be equal to 0. It’s necessary to specify minOccurs = 0 for the optional component. When the number of components is unlimited: maxOccurs = unbounded.

You can read about XML schemes in more details here.

Creating JavaBeans

Based on the created scheme we’ll create Java classes. To do this, create build.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<project name="imsjob" default="build" basedir=".">
<property name="WS_HOME" value="C:\AST\lib\standart"/>
<property name="encoding" value="UTF-8"/>
<path id="xbean.classpath">
    <fileset dir="${WS_HOME}">
      <include name="*.jar"/>
    </fileset>
  </path>
    <taskdef name="xmlbean" classname="org.apache.xmlbeans.impl.tool.XMLBean" classpathref="xbean.classpath" />
      <target name="init">
    <echo message="Start init"/>
  </target>
    <target name="build" depends="init">
    <xmlbean schema="HelloService.xsd" destfile="lib\helloservice.jar" classpathref="xbean.classpath"/>
  </target>
</project>

The WS_HOME parameter should point on the catalogue where XMLBeans located. HelloService.xsd is the path to the created scheme. lib\helloservice.jar is the created java-library.

Then let’s run Ant-build (I hope you’ve already installed it). In Eclipse you can run it so: right-clicking build.xml=> Run As => Ant Build. If via the command line: ant -buildfile build.xml. And then just wait for the build completion. After that, you can check the WEB-INF\lib project catalogue for the presence of the corresponding library (helloservice.jar).

Service realization

Create interface and service class

Service interface:HelloServiceImpl.java:

package org.example;
import java.util.Calendar;
public interface HelloService {
  public String getHello(String name) throws Exception;
  public Calendar getCurrentTime();
}

Service realization: HelloServiceImpl.java:

package org.example;
import java.util.Calendar;
import org.springframework.stereotype.Service;
@Service
public class HelloServiceImpl implements HelloService {
  public String getHello(String name) throws Exception {
    return "Hello, " + name + "!";
  }
  public Calendar getCurrentTime() {
    return Calendar.getInstance();
  }
}

I think there’s no need to comment the code. The only thing that people which haven’t met Spring before could have questions what is the @Service annotation. But I’ll talk about it a bit later.

Endpoint

Endpoint is the class which will be responsible for the processing of input requests (like the entry point).

Let’s create the HelloServiceEndpoint.java file:

package org.example;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.example.helloService.ServiceRequestDocument;
import org.example.helloService.ServiceRequestDocument.ServiceRequest;
import org.example.helloService.ServiceResponseDocument;
import org.example.helloService.ServiceResponseDocument.ServiceResponse;
 
@Endpoint
public class HelloServiceEndpoint{
  private static final String namespaceUri = "http://www.example.org/HelloService";
  private HelloService helloService;
  @Autowired
  public void HelloService (HelloService helloService) {
    this.helloService = helloService;
  }
    @PayloadRoot(localPart = "ServiceRequest", namespace = namespaceUri)
  public ServiceResponseDocument getService(ServiceRequestDocument request) throws Exception {
    ServiceRequestDocument reqDoc = request;
    ServiceRequest req = reqDoc.getServiceRequest();
    ServiceResponseDocument respDoc = ServiceResponseDocument.Factory.newInstance();
    ServiceResponse resp = respDoc.addNewServiceResponse();
 
    String userName = req.getName();
    String helloMessage = testNewService.getHello(userName);
    Calendar currentTime = testNewService.getCurrentTime();
 
    resp.setHello(helloMessage);
    resp.setCurrentTime(currentTime);
    return respDoc;
  }
}

So what is done here?

The @Endpoint annotation exactly defines that this class will process the input requests. namespaceUrl is the same namespace that was specified during the creation of xml scheme.

Now let’s go a bit back and remember about the @Service annotation. Without going into details in order not to overload the reader with the extra information, the annotation tells Spring to create the appropriate object. The @Autowired annotation serves for injection (automatic replacement) of the corresponding object. Certainly when building simple application there is no sense in using these annotations but I decided not to exclude them in this example.

Hence, let’s go forward. The @PayloadRoot annotation defines before the method by receiving of which request this method will be called. In our case, it’s a “ServiceRequest”.

In all the rest everything should be clear. Pay attention that ServiceRequest, ServiceResponse and so on are exactly those classes which have been created on the basis of our xml scheme.

Spring configuration of the service

Here is almost the completion. Let’s create the service-ws-servlet.xml.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:sws="http://www.springframework.org/schema/web-services"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 
  <context:component-scan base-package="org.example" />
  <sws:annotation-driven />
 
  <bean class="org.springframework.ws.server.endpoint.adapter.GenericMarshallingMethodEndpointAdapter">
<property name="marshaller" ref="marshaller" />
<property name="unmarshaller" ref="marshaller" />
  </bean>
 
  <bean id="marshaller" class="org.springframework.oxm.xmlbeans.XmlBeansMarshaller"/>
 
  <sws:dynamic-wsdl id="HelloService" portTypeName="service" locationUri="/HelloService" >
    <sws:xsd location="/WEB-INF/HelloService.xsd" />
  </sws:dynamic-wsdl>
</beans>

sws:annotation-driven – tells exactly about that annotations are used in this project. And context:component-scan points on the package where annotation searching will be performed, by the same time searching is performed in subpackages as well.

Both subsequent beans will always be constant. Their sense is to receive and to convert the request from XML into Java object and further reverse conversion.

sws:dynamic-wsdl is responsible for the automatic generation of WSDL-based document created by XML scheme. location points on the path to scheme. locationUrl is the address (relating to container) which will be available for WSDL scheme. In my case WSDL is available over the following address: http://localhost/HelloService/HelloService.wsdl.

Deployment descriptor

And finally, the last one. In the WEB-INF catalogue let’s modify and create the web.xml file.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC
    "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
  <display-name>HelloService</display-name>
  <description>HelloService</description>
  <servlet>
    <servlet-name>service-ws</servlet-name>
    <servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
    <init-param>
<param-name>transformWsdlLocations</param-name>
<param-value>true</param-value>
    </init-param>
  <servlet-mapping>
    <servlet-name>service-ws</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>

I won’t describe this file, most of them you should know. In fact it shouldn’t change for simple projects. It’s worth to note that the servlet name should correspond to the file name of Spring configuration of the service-ws-servlet.xml service.

Then, let’s deploy the application on the server. Creation of the service is completed at this stage. If we haven’t missed anything, the service should function well.

The very first sign of correct performance is the created WSDL scheme. In order to check it, just go over the address of this scheme: (http://localhost/HelloService/HelloService.wsdl) and look: the xml file should be displayed there. If nothing displayed or any other error occurred, re-read the whole article and search what was done incorrectly.

For further verification, we need soapUI (I have version 3.0.1).

Let’s install and run it, and then create a new project: File => New soapUI Project.

In the Initial WSDL/WADL field insert the link to WSDL scheme: (http://localhost/HelloService/HelloService.wsdl).

Let’s open the necessary request in the created project.

request

In the Name field type the name and press the “Send request” button.

name

As a result we get the answer from the server with the greeting and current time.

answer

If something went wrong then read this article once again.

What’s next?

Then you should write client for the web service. But it’s the material for another article which could be probably written later if this material will be interesting for anyone.

12
Jun

Development and testing of Java REST web-services

Posted by eugene as Web Services

Introduction

In order to develop REST web services, Java provides a JSR-311 – JAX-RS: The Java ™ API for RESTful Web Services. As it usually happens in the world of Enterprise Java there are several implementations of this specification:

  • Jersey is the reference implementation of the specification from Sun Oracle
  • Apache CXF
  • JBoss RESTEasy

In the latest example of implementation I’ll show you how to write the REST-service.

Let’s write the REST-service

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
 
@Path("/service/entity")
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public interface EntityRestService {
    @GET
    @Path("/all")
    EntityList listAll();
 
    @GET
    @Path("/{id}")
    Entity findById(@PathParam("id") Integer id);
}

Let’s create an interface where we arrange the JAX-RS annotations.

  • The @Path annotation specifies the path that will be available to our service.
  • The @GET annotation specifies which HTTP-request will be processed by this method.
  • The @Produces annotation allows to specify the format in which the service provides the results.

Configuring JBoss RESTEasy

Configuration is very simple. At first, we need to update pom.xml and add the necessary dependencies:

Updating pom.xml
Add the compile-time dependency on JAX-RS API

org.jboss.resteasy
jaxrs-api

and runtime depending on the implementation

org.jboss.resteasy
resteasy-jaxrs
${resteasy-jaxrs.version}
runtime
 
org.jboss.resteasy
resteasy-jackson-provider
${resteasy-jaxrs.version}
runtime

Modify web.xml as follows

org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
 
resteasy.servlet.mapping.prefix
/rest
 
REST Easy
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
 
REST Easy
/rest/*

This code declares the HttpServletDispatcher servlet which will handle all the requests that come in the / rest / *. Listener ResteasyBootstrap performs all necessary initialization JBoss RESTEasy.

Support for SpringIn order to use the Spring Framework, we need to make the following changes: 1. Add a dependency in pom.xml

org.jboss.resteasy
resteasy-spring
${resteasy-jaxrs.version}
runtime

2. Add Spring-a marketing student to web.xml

org.jboss.resteasy.plugins.spring.SpringContextLoaderListener

Following these changes, JBoss RESTEasy will be aware of Spring, this will allow the implementation of REST-full services to use all the opportunities provided by Spring Framework.

Writing tests

Writing tests is a very useful thing, here I’ll show an example of test for REST-service. Under this test, we have built up a web server TJWSEmbeddedJaxrsServer to which we will refer to test our REST-services.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"/applicationContext-test.xml"})
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class})
public class TestEntityServiceRest {
 
    private static final int PORT = 8081;
    private static final String BASE_URL = "http://localhost:" + PORT;
 
    @Autowired
    EntityService entityService;
 
    @Autowired
    ConfigurableApplicationContext applicationContext;
 
    protected HttpClient client;
 
    protected TJWSEmbeddedJaxrsServer server;
 
    @Before
    public void setUpClient() throws Exception {
        client = new DefaultHttpClient();
    }
 
    @Before
    public void setUpServer() throws Exception {
        server = new TJWSEmbeddedJaxrsServer();
        server.setPort(PORT);
        ResteasyDeployment deployment = server.getDeployment();
        server.start();
        Dispatcher dispatcher = deployment.getDispatcher();
        SpringBeanProcessor processor = new SpringBeanProcessor(dispatcher, deployment.getRegistry(), deployment.getProviderFactory());
        applicationContext.addBeanFactoryPostProcessor(processor);
        SpringResourceFactory noDefaults = new SpringResourceFactory("entityServiceRestImpl", applicationContext, EntityRestServiceImpl.class);
        dispatcher.getRegistry().addResourceFactory(noDefaults);
    }
 
    @After
    public void stop() {
        server.stop();
    }
 
    @Test
    public void testListAll() throws Exception {
        int i = 0;
        final List returnedList = new ArrayList();
        int EXPECTED_SIZE = 6;
 
        while (i < EXPECTED_SIZE) {
            i++;
            final Entity entity = new Entity();
            entity.setId(i);
            entity.setName("test" + i);
            returnedList.add(entity);
        }
 
        when(entityService.findAll()).thenReturn(returnedList);
        HttpGet get = new HttpGet(BASE_URL + "/service/entity/");
        HttpResponse response = client.execute(get);
        InputStream content = response.getEntity().getContent();
        EntityList result = fromString(EntityList.class, content);
        content.close();
 
        Assert.assertNotNull(result);
        Assert.assertEquals(EXPECTED_SIZE, result.getEntities().size());
    }
 
    @Test
    public void testFindById() throws Exception {
        final Entity validEntity = getValidEntity();
        validEntity.setId(2);
        when(entityService.findById(validEntity.getId())).thenAnswer(new Answer...
    }
}

Running the Tests

Half of work is done, now we need to set up the infrastructure for testing of our newly written REST-services. To do this, we need to make the following modifications in pom.xml

org.apache.maven.plugins
maven-surefire-plugin
2.11
 
Unit tests
test
 
test
 
**/IT*.java
 
Integration tests
integration-test
 
test
 
**/IT*.java
 
org.apache.httpcomponents
httpclient
4.1.2
test
 
org.jboss.resteasy
resteasy-jaxrs
test
 
org.jboss.resteasy
resteasy-spring
test
 
org.jboss.resteasy
tjws
${resteasy-jaxrs.version}
 
javax.servlet
servlet-api
test
 
commons-io
commons-io
test

Conclusion

In general, nothing complicated, important to do everything carefully. Have a good code!