Organizations rely on different software applications, each with their own business purpose. These different software applications run on different platforms and operating systems, and are implemented in different programming languages. So, it is very difficult for different applications to communicate with each other and share their resources in a coordinated way. Heterogeneous applications can communicate with each other via web services. The following are the web service characteristics:
Web services are client and server applications that communicate over HyperText Transfer Protocol (HTTP) and provide a standard means for interoperating between software applications running on a variety of platforms and frameworks. Web services are characterized by interoperability and extensibility.
We'll be looking at two of the most common tools for building web services in Java, namely, the Java API for XML Web Services (JAX-WS) and Java API for RESTful Web Services (JAX-RS):
Visit the following URL for more details on JAX-WS and JAX-RS:
http://docs.oracle.com/javaee/6/tutorial/doc/gijti.html
In the following section, we'll explore the JAX-WS web services with Eclipse.
JAX-WS web services require a service description written in WSDL.
A WSDL document defines services as collections of network endpoints or ports. In WSDL, the abstract definition of endpoints and messages is separated from their concrete network deployment or data format bindings. This allows the reuse of abstract definitions: messages, which are abstract descriptions of the data being exchanged and port types, which are abstract collections of operations.
A WSDL document uses the following elements in the definition of network services:
JAX-WS web services can be developed using two approaches:
In this section, we'll follow the bottom-up approach.
To develop a web service in Eclipse, the following components should be installed on your machine:
The following are steps to create a web service with Apache Axis and Apache Tomcat:
DNACheckWS
.TH01
, and corresponding allele A
and allele B
values such as TH01
, 8
, and 11
. The DNA profile will be examined against the database, and if the exact match is found, the person's details will be sent back as a response.ProfileElement
class in the com.packt.webservice.jaxws.dto
package, add three string attributes geneticMarker
, alleleA
, and alleleB
, and generate getters and setters for the three attributes. The following is the code for the class details:package com.packt.webservice.jaxws.dto; public class ProfileElement { private String geneticMarker; private String alleleA; private String alleleB; //The getters and setters are ignored for brevity }
DNAProfile
class with an array of ProfileElements
. The following is the code for the class details:package com.packt.webservice.jaxws.dto; public class DNAProfile { private ProfileElement[] dnaElements; //The getters and setters are ignored for brevity }
DNAFingerPrintService
for matching a DNA profile with an existing set of DNAs; it just returns a fixed value here. The following are the details:package com.packt.webservice.jaxws.service; import com.packt.webservice.jaxws.dto.DNAProfile; public class DNAFingerPrintService { public String findMatch(DNAProfile dnaProfile){ return "sujoy"; } }
DNAFingerPrintService
with runtime Axis and Tomcat. Right-click on the project and select the Web Services menu, expand the menu, and select Create Web Service. The following screenshot displays the steps:Do not generate the client; set the slider to No client:
findMatch
method, select the Style and use option as document/literal (wrapped), and hit the Next or Finish button:http://localhost:8080/DNACheckWS/services/DNAFingerPrintService
:
http://localhost:8080/DNACheckWS/services/DNAFingerPrintService?wsdl
The following is the WSDL output:
We're done with the web service server component; next we'll build the client component to invoke the web service:
DNAWsClient
.wsdl
file. Copy the wsdl
folder from the DNACheckWS
project's WebContent
folder to the DNAWsClient
project's src
folder, as shown in the following screenshot:wsdl
file and select Web Services | Generate Client, as shown in the following screenshot:dto
classes and stubs. The following are the generated classes:DNAFingerPrintWsInvoker
to invoke the web service. The DNAFingerPrintServiceServiceLocator
class is a facade class and it hides the underlying service invocation details. We'll create a findMatch
method to invoke the web service and return the result. The following is the client code:package com.packt.webservice.jaxws.client; import java.rmi.RemoteException; import javax.xml.rpc.ServiceException; import com.packt.webservice.jaxws.dto.DNAProfile; import com.packt.webservice.jaxws.service.DNAFingerPrintServiceServiceLocator; public class DNAFingerPrintWsInvoker { public String findMatch(DNAProfile dnaProfile) throws RemoteException, ServiceException { DNAFingerPrintServiceServiceLocator locator = new DNAFingerPrintServiceServiceLocator(); return locator.getDNAFingerPrintService().findMatch(dnaProfile); } }
findMatch
method instantiates the DNAFingerPrintServiceServiceLocator
class and delegates call to the locator for accessing the web service. The DNAFingerPrintServiceServiceLocator
class makes a network call and, hence, it can be considered as a testing impediment. We need to bypass the instantiation of the testing impediments, to make our test reliable, with a mock object. Add Mockito and JUnit JAR files to the project's classpath and create a test class DNAFingerPrintWsInvokerTest
under the test
source package.DNAFingerPrintWsInvoker
class and extract a protected method getServiceLocator()
to return a new instance of the DNAFingerPrintServiceServiceLocator
class. Replace the new DNAFingerPrintServiceServiceLocator()
call with the getServiceLocator()
call. The following code shows the modified class. Also, as a better alternative, DNAFinderPrintWsInvoker
could take a DNAFingerPrintServiceServiceLocation
call as a constructor argument, which is to be provided by whoever uses it. The tests can then provide a stub to that constructor. This reduces coupling between the test and the class under the test's internals and ensures that we're actually definitely testing the code, which is as close as possible to what's going to be running in production:public class DNAFingerPrintWsInvoker { public String findMatch(DNAProfile dnaProfile) throws RemoteException, ServiceException { return getServiceLocator().getDNAFingerPrintService().findMatch(dnaProfile); } protected DNAFingerPrintServiceServiceLocator getServiceLocator() { return new DNAFingerPrintServiceServiceLocator(); } }
@RunWith(MockitoJUnitRunner.class) public class DNAFingerPrintWsInvokerTest { DNAFingerPrintWsInvoker invoker; @Mock DNAFingerPrintService mockService; @Mock DNAFingerPrintServiceServiceLocator mockLocator; @Before public void setup() throws ServiceException { invoker = new DNAFingerPrintWsInvoker(){ protected DNAFingerPrintServiceServiceLocator getServiceLocator() { return mockLocator; } }; when(mockLocator.getDNAFingerPrintService()).thenReturn(mockService); } @Test public void finds_DNA_match() throws Exception { when(mockService.findMatch(isA(DNAProfile.class))).thenReturn("Sherlock"); assertEquals("Sherlock", invoker.findMatch(new DNAProfile())); } }
We created a fake instance of the invoker to return a mock service locator class, stubbed the service locator to return a mock web service, and finally, from the JUnit test, we stubbed the mock service to return the name Sherlock
for any DNA profile.
slowtest
and add the test DNAFingerPrintWsInvokerIntegrationTest
under the com.packt.webservice.jaxws.client
package. The following is the integration test. Do you remember we hardcoded the web service to return sujoy
? Check the DNAFingerPrintService
class at step 5. You need to make sure that you still have the web service running at this stage, as we should not break the functionality for JUnit testing.public class DNAFingerPrintWsInvokerIntegrationTest { DNAFingerPrintWsInvoker invoker; @Before public void setup() throws ServiceException { invoker = new DNAFingerPrintWsInvoker(); } @Test public void finds_DNA_match() throws Exception { assertEquals("sujoy", invoker.findMatch(new DNAProfile())); } }
The following is the integration test output:
We are done with the testing and client validation. We can start writing JUnit for the server side; but the server-side code is not tied up with the web service APIs, so we can easily write the JUnit test for the server side, hence skipping the topic.
Representational State Transfer (REST) is an architectural style consisting of a coordinated set of architectural constraints applied to components, connectors, and data elements, within a distributed hypermedia system.
REpresentational State Transfer (REST) / RESTful web services are built to work best on the Web. REST is an architectural style that specifies constraints (such as the uniform interface) that, if applied to a web service, induces desirable properties, such as performance, scalability, and modifiability, which enable services to work best on the Web.
In REST, architectural data and functionality are considered resources and are accessed using Uniform Resource Identifiers (URIs) and hyperlinks on the Web. The REST architectural style has a constraint to have a stateless HTTP communication protocol in a client/server architecture. In the REST architectural style, clients and servers exchange representations of resources by using a standardized interface and protocol.
Basically, RESTful web services consist of the following components:
POST
: This signifies a CREATE
operation or a new resource creation. For example, an HTTP POST operation on the http://my.colleage.com/students URL with the following data will create a student with the roll number 102
:{ "roleNumber": "102", "name": "Bob Biswas", "class" : "XII", "email" : "[email protected]" }
GET
: This implies a READ
operation. For example, an HTTP GET operation on the http://my.colleage.com/students URL will return the following data:{ students =[ { "roleNumber": "101", "name": "Leo Anthony", "class" : "X", "email" : "[email protected]" }, { "roleNumber": "102", "name": "Bob Biswas", "class" : "XII", "email" : "[email protected]" }, }
PUT
: This stands for the MODIFY
/UPDATE
operation. For example, an HTTP PUT operation that can help us to update the e-mail ID of the student whose roll number is 101
.DELETE
: This represents a DELETE
operation. For example, an HTTP DELETE operation on the http://my.colleage.com/students/101 URL will delete the student whose roll number is 101
.
Status code |
Description |
---|---|
200 |
OK |
201 |
Created |
202 |
Accepted |
203 |
Non-authoritative Information |
204 |
No Content |
205 |
Reset Content |
300 |
Multiple Choices |
304 |
Not Modified |
400 |
Bad Request |
401 |
Unauthorized |
403 |
Forbidden |
404 |
Not Found |
405 |
Method Not Allowed |
408 |
Request Timeout |
409 |
Conflict |
500 |
Internal Server Error |
501 |
Not Implemented |
For RESTful web service details, visit the following URL:http://docs.oracle.com/javaee/6/tutorial/doc/gijqy.html
Spring MVC was built to provide a flexible framework for web application developers. Spring's DispatcherServlet
class acts as a front controller; it receives all the incoming requests and delegates the processing of the requests to handlers. It allows developers to concentrate on business logic rather than work on the boilerplate of a custom front controller. This section describes the Spring MVC architecture and how RESTful web applications can be unit tested using Spring MVC.
In Spring MVC, the following is a pattern of a simplified request handling mechanism:
DispatcherServlet
receives a request, confers with handler mappings to find out which controller can handle the request, and then passes the request to the selected controller.DispatcherServlet
for user display or response. Instead of sending the information (model) directly to the user, the controller returns a view name that can render the model.DispatcherServlet
then resolves the physical view from the view name and passes the model object to the view. This way, DispatcherServlet
is decoupled from the view implementation. The view renders the model. A view could be a JSP page, a servlet, a PDF file, an Excel report, or any presentable component.The following sequence diagram represents the flow and interaction of the Spring MVC components:
For a RESTful web service, instead of forwarding the model and the view object or the logical view name from controller, we can directly return response data from the controller using Spring's @ResponseBody
annotation.
We'll build a Spring RESTful web service and unit test the code using JUnit. The following are the steps to be performed:
RESTfulStudentWS
.web.xml
and enter the following lines:<display-name>RESTfulStudentWS</display-name> <servlet> <servlet-name>rest</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>rest</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/rest-servlet.xml </param-value> </context-param> </web-app>
The dispatcher is named rest
, and it maps all requests. Note the contextConfigLocation
parameter; it indicates that the Spring beans are defined in /WEB-INF/rest-servlet.xml
.
rest-servlet.xml
in WEB-INF
and add the following lines:<?xml version="1.0"encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="com.packt.restful.controller" /> <bean class= "org.springframework.web.servlet.view.InternalResourceViewResolver"> "" <mvc:annotation-driven /> </beans>
The preceding XML code instructs the Spring configuration that all beans are configured under the com.packt.restful.controller
package with the Spring annotations.
/WEB-INF/lib
folder:com.packt.restful.model.Student
and add the following members and getters/setters in it:private String roleNumber; private String name; private String className; private String emailId;
StudentDao
to mimic the JDBC data access. We'll set up a map of students to bypass the real database interaction. Add the following lines to the StudentDao
class. Note that the retrieveAll
and retrieve(roleId)
methods are public methods for retrieving all the students and a specific student respectively.@Component public class StudentDao { private Map<String, Student> database = new HashMap<String, Student>(); public StudentDao(){ load(); } public Collection<Student> retrieveAll() { return database.values(); } public Student retrieve(String roleId) { return database.get(roleId); } private void load() { Student student = new Student(); student.setClassName("X"); student.setEmailId("[email protected]"); student.setName("Sujoy Acharya"); student.setRoleNumber("100"); database.put(student.getRoleNumber(), student); student = new Student(); student.setClassName("XII"); student.setEmailId("[email protected]"); student.setName("Leo Anthony"); student.setRoleNumber("101"); database.put(student.getRoleNumber(), student); student = new Student(); student.setClassName("XII"); student.setEmailId("[email protected]"); student.setName("John Paul"); student.setRoleNumber("7"); database.put(student.getRoleNumber(), student); student = new Student(); student.setClassName("XII"); student.setEmailId("[email protected]"); student.setName("Subodh Chavan"); student.setRoleNumber("3"); database.put(student.getRoleNumber(), student); } }
StudentController
class and annotate the class with @Controller
to notify the Spring framework that the class is a Spring controller class. Also annotate the class with @RequestMapping("/college")
to map requests for "/college"
to StudentController
. The following is the class:@Controlle @RequestMapping("/college") public class StudentController { @Autowired StudentDao studentDao; @RequestMapping(value = "/students/{roleNumber}", method = RequestMethod.GET) public @ResponseBody Student retrieve(@PathVariable String roleNumber) { return studentDao.retrieve(roleNumber); } @RequestMapping(value = "/students/", method = RequestMethod.GET) public @ResponseBody List<Student> retrieveAll() { return new ArrayList<Student>(studentDao.retrieveAll()); } }
Note that the methods are annotated with @RequestMapping
; this annotation maps a URL to a method. An HTTP GET request with the /college/students/n
URL (where n
is a roll number) will be handled by the retrieve()
method. We can change the method type to POST in order to handle HTTP POST requests; the default method type is GET.
In MVC, controller methods return a model and a view object or a logical view name, but when we annotate a method with @ResponseBody
, it implies that the response will be sent back directly to the caller instead of getting processed by a view.
http://localhost:8080/RESTfulStudentWS/college/students/
. Please change the server name and port as per your Tomcat settings. You will get a JSON response back for all the students; the following is the output:7
as the roll number:null
. The following URL passes the number 1000
and gets the following output:In real life application, the StudentDao
class will be replaced by a real database access class. The real DAO class makes unit testing the controller difficult. Separating the data access layer from the business logic layer helps us change the database without affecting the business logic layer and allows us to unit test the business logic layer in isolation from the database. Suppose you are using the MySQL database and you want to migrate to SQLServer, then you don't have to touch the business logic layer. We'll use Mockito to isolate the DAO layer from the service layer; the following are the steps to be performed:
StudentControllerTest
under the source folder test
and add mockito-all-1.9.5.jar
to the lib
folder under WEB-INF
. The controller class calls the studentDao
class and Spring autowires the DAO layer to the controller class. We need to modify the controller class to pass as a setter from the JUnit tests. The following is the modified controller class (other methods are skipped for brevity):@Controller @RequestMapping("/college") public class StudentController { @Autowired private StudentDao studentDao; public void setStudentDao(StudentDao studentDao) { this.studentDao = studentDao; } }
@RunWith(MockitoJUnitRunner.class) public class StudentControllerTest { @Mock StudentDao studentDao; @InjectMocks StudentController controller; }
The @RunWith(MockitoJUnitRunner.class)
annotation allows us to use the @Mock
annotation to automatically mock the objects. The @InjectMocks
annotation injects the mock objects as a setter, constructor, or property injection.
retrieveAll
method of the DAO class to return the students. The following is the modified test:@RunWith(MockitoJUnitRunner.class) public class StudentControllerTest { @Mock StudentDao studentDao; @InjectMocks StudentController controller; @Before public void setUp(){ Student student = new Student(); student.setClassName("X"); student.setEmailId("[email protected]"); student.setName("sujoy"); student.setRoleNumber("7"); Collection<Student> studentColl = new ArrayList<Student>(); studentColl.add(student); when(studentDao.retrieveAll()).thenReturn(studentColl); } @Test public void retrieves_students() throws Exception { List<Student> retrieveAll = controller.retrieveAll(); assertFalse(retrieveAll.isEmpty()); } }
null
is returned when the roll number doesn't match. The following is the test:@Test public void retieves_a_student() throws Exception { when(studentDao.retrieve(eq("100"))).thenReturn(new Student()); assertNotNull(controller.retrieve("100")); } @Test public void when_invalid_role_number_returns_null(){ assertNull(controller.retrieve("100")); }
3.145.78.136