Monday, September 21, 2015

REST APIs tutorial with Java & Spring

In my previous post I talked about the API economy and I thought it would be fun to write a small tutorial on how to expose a REST API using Java and Spring. The application will be quite simple, just services that allow to perform CRUD operations in a repository. In order to keep the tutorial on the point and focused on showcasing how to expose a REST API, I will not use a real database for persistence and will not implement authentication mechanisms. Also, I will not provide instructions for installation of required components (e.g., server, build manager and so on) but I will list them. This post will be simple but I believe readers  will be able to turn this tutorial to more sophisticated applications. Before I get into the fun part, I would like to give a brief introduction to Web services and the restful architectural style.

Web services are software applications designed to support machine-to-machine interaction over a network. They communicate using open protocols, they are self-contained and self-describable. Web services provide machine processable descriptions (i.e., WSDL, WADL) so that other applications can dynamically adapt to the provided interface and use it to request the Web service to perform operations, which essentially means enhanced interoperability. Use of Web services allows the realization of software systems which are built by composing independent well defined units of functionality called services. In  such systems the services are loosely coupled and each service acts independently from the others and the functionalities it provides are also decoupled. Web services use standardized formats for message formatting, processing and data communication. Interested readers should be familiar with  SOAP, REST , XML, JSON and WSDL and if not, they are advised to familiarize themselves with these important technologies, protocols and data formats.

Web services which employ REST based communications are often called as Web APIs. REST stands for Representational State Transfer, a term first coined by Fielding in 2002. REST is an architectural style that prescribes rules for the description of an abstract model of Web architecture. In REST, URLs (Uniform Resource Locators) are used to identify and exposure services. These exposed services allow operations over resources which are identified by unique URLs (URIs) such as retrieval, update and deletion. REST is built over HTTP and uses HTTP verbs to indicate the type of operation to be performed. The most used verbs are GET, POST, UPDATE and DELETE and other useful verbs include HEAD, OPTIONS and PATCH. Well-designed REST APIs should provide proper HTTP response codes to provide efficient dialogue between clients and servers regarding the status of requests. Readers can learn more about HTTP responses in W3C’s website. Another important component of well-designed REST APIs is resource naming. REST API resource naming should be intuitive and descriptive of the identifying service, similar to methods naming in software development. A good guide for resource naming can be found here.

Before I go on with the tutorial on REST APIs I would like to point you to Twitter’s REST API, which is an excellent example of well designed APIs. It can be found here .

Now, to the tutorial! We will build an application that manages (geek) diary entries. The application will expose a REST API which will be used to perform CRUD operations on diary entries.  The tutorial will be written on Java and will use the Spring framework to expose the REST API.

The requirements for replicating and running this tutorial are:
  • JDK 1.7 or higher
  • Maven 
  • Server such as Tomcat 7 or higher
  • Git
  • and use of IDE such as eclipse is also advised
The setup of the project is simple: the ‘config’ package contains the configuration classes for Spring, the ‘data’ package contains our diary entries POJO and the DAOs for data persistence, and finally, in the ‘web’ package is placed the controller with the rest services. The setup is presented in the figure below:



In the pom.xml file of your project, we add the Spring dependencies, necessary javax dependencies for the servlet containers and Jackson for un-/marshalling of the POJOs. The complete pom.xml file is shown below:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.tasosmartidis</groupId>
  <artifactId>rest-api-tutorial</artifactId>
  <packaging>war</packaging>
  <version>0.1</version>
  <name>rest-api-tutorial Maven Webapp</name>
  <url>http://maven.apache.org</url>
  
  <properties>
        <springframework.version>4.0.6.RELEASE</springframework.version>
    </properties>
    
  <dependencies> 
   <!-- Spring -->
   <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${springframework.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${springframework.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${springframework.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${springframework.version}</version>
    </dependency>    
    <!-- javax -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp.jstl</groupId>
        <artifactId>jstl-api</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>javax.servlet.jsp-api</artifactId>
        <version>2.3.1</version>
    </dependency>
    <!-- Jackson -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.3.0</version>
    </dependency>   
    <dependency> <!-- For Spring 4.1 and on, minimum Jackson version is 2.1 and 2.3 is recommended-->
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.3.0</version>
    </dependency>    
    <!-- JUnit -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
    </dependency> 
  </dependencies>
  
  <build>
    <finalName>rest-api-tutorial</finalName>
    <plugins>
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.0</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-eclipse-plugin</artifactId>
            <version>2.9</version>
            <configuration>
                <downloadSources>true</downloadSources>
                <downloadJavadocs>true</downloadJavadocs>
            </configuration>
        </plugin>       
    </plugins>
  </build>
</project>

Let’s  now create a simple POJO which will represent our diary entries and will be composed by an id, a diary entry title and the main text. The DiaryEntry class is listed below:

package com.tasosmartidis.rest_api_tutorial.data;


public class DiaryEntry {

    private String entryId;
    private String entryTitle;
    private String entryText;
    
    public String getEntryId() {
        return entryId;
    }
    public void setEntryId(String entryId) {
        this.entryId = entryId;
    }
    public String getEntryTitle() {
        return entryTitle;
    }
    public void setEntryTitle(String entryTitle) {
        this.entryTitle = entryTitle;
    }
    public String getEntryText() {
        return entryText;
    }
    public void setEntryText(String entryText) {
        this.entryText = entryText;
    }
    
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((entryId == null) ? 0 : entryId.hashCode());
        result = prime * result
                + ((entryTitle == null) ? 0 : entryTitle.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        DiaryEntry other = (DiaryEntry) obj;
        if (entryId == null) {
            if (other.entryId != null)
                return false;
        } else if (!entryId.equals(other.entryId))
            return false;
        if (entryTitle == null) {
            if (other.entryTitle != null)
                return false;
        } else if (!entryTitle.equals(other.entryTitle))
            return false;
        return true;
    }
    
}


On a side-note, I like using eclipse's functionality for creating the hashCode and equals methods for POJOs. Just go 'Source' and then 'Generate hashCode() and equals()'.

Next, we will define an interface which defines the CRUD methods for the persisted data, The implementation of the interface will vary for each database (i.e., MongoDB, MySQL implementation and so on). The interface for the CRUD operations is described below:
package com.tasosmartidis.rest_api_tutorial.data;

import java.util.Map;

public interface Dao {

    public DiaryEntry createDiaryEntry(DiaryEntry newEntry);
    
    public DiaryEntry getDiaryEntry(String entryId);
    
    public DiaryEntry updateDiaryEntry(DiaryEntry updatedEntry);
    
    public String deleteDiaryEntry(String entryId);
    
    public Map<String,DiaryEntry> getAllDiaryEntries();
}


 In this tutorial we use a mock repository to persist the data, in favor of simplicity. The DaoMock class is presented below:
package com.tasosmartidis.rest_api_tutorial.data;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository("dao")
@Transactional
public class DaoMock implements Dao {

    private static Map<String, DiaryEntry> diaryEntries = new HashMap<>();
        
    public DiaryEntry createDiaryEntry(DiaryEntry newEntry) {
        //set our own unique diary id
        newEntry.setEntryId("diary-entry-"+UUID.randomUUID().toString());
        
        diaryEntries.put(newEntry.getEntryId(),newEntry);
        
        if(diaryEntries.containsValue(newEntry))
            return newEntry;
        else
            throw new RuntimeException();
    }

    public DiaryEntry getDiaryEntry(String entryId) {
        return diaryEntries.get(entryId);
    }

    public DiaryEntry updateDiaryEntry(DiaryEntry updatedEntry) {
        diaryEntries.replace(updatedEntry.getEntryId(), updatedEntry);
        
        if(diaryEntries.containsValue(updatedEntry))
            return updatedEntry;
        else
            throw new RuntimeException();
    }

    public String deleteDiaryEntry(String entryId) {
        diaryEntries.remove(entryId);
        
        if(diaryEntries.containsKey(entryId))
            throw new RuntimeException();
        else
            return entryId;
    }

    public Map<String, DiaryEntry> getAllDiaryEntries() {
        return diaryEntries;
    }
 
}


Now the controller, which will expose the REST services over the net. The implementation of the REST service is presented below:
package com.tasosmartidis.rest_api_tutorial.web;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.tasosmartidis.rest_api_tutorial.data.DaoMock;
import com.tasosmartidis.rest_api_tutorial.data.DiaryEntry;

@RestController
@RequestMapping("/service/geek-diaries")
public class DiaryService {
    
    @Autowired
    DaoMock dao; 
    
    @RequestMapping(value="/entry/{entryId}", method=RequestMethod.GET)
    public ResponseEntity<DiaryEntry> getDiaryEntry(@PathVariable String entryId) {
        DiaryEntry diaryEntry = dao.getDiaryEntry(entryId);
        
        return new ResponseEntity<DiaryEntry>(diaryEntry,HttpStatus.OK); 
    }
    
    @RequestMapping(value="/entry/", method=RequestMethod.POST)
    public ResponseEntity<DiaryEntry> createDiaryEntry(@RequestBody DiaryEntry newEntry) { 
         DiaryEntry newDiaryEntry = dao.createDiaryEntry(newEntry);

        return new ResponseEntity<DiaryEntry>(newDiaryEntry,HttpStatus.OK); 
    }
    
    @RequestMapping(value="/entry/", method=RequestMethod.PUT)
    public ResponseEntity<DiaryEntry> updateDiaryEntry(@RequestBody DiaryEntry newEntry) { 
         DiaryEntry updatedDiaryEntry = dao.updateDiaryEntry(newEntry);

        return new ResponseEntity<DiaryEntry>(updatedDiaryEntry,HttpStatus.OK); 
    }
    
    @RequestMapping(value="/entry/{entryId}", method=RequestMethod.DELETE)
    public ResponseEntity<String> deleteDiaryEntry(@PathVariable String entryId) { 
         String deletedDiaryEntry = dao.deleteDiaryEntry(entryId);

        return new ResponseEntity<String>(deletedDiaryEntry,HttpStatus.OK); 
    }
    
    @RequestMapping(value="/entry/", method=RequestMethod.GET)
    public ResponseEntity<Map<String, DiaryEntry>> getDiaryEntries() { 
        Map<String, DiaryEntry> diaryEntries = dao.getAllDiaryEntries();

        return new ResponseEntity<Map<String, DiaryEntry>>(diaryEntries,HttpStatus.OK); 
    }
}


Finally, for the configuration we will not use XML but instead, we will use Java to configure the DispatcherServlet in the servlet container. This is made possible since Servlet 3.0 specification and from Spring 3.1 and over. Specifically we will extend the ‘AbstractAnnotationConfigDispatcherServletInitializer’ class which registers a ContextLoaderlistener and a DispatcherServlet. This simplifies our work and we can define configuration classes (e.g. for the servlets) and servlet mappings. 

The WebAppInitializer class has only 3 methods. The getServletMappings() indicates the paths in which the DispatcherServlet will be mapped to. We map it to the default servlet which will handle all requests coming into our application. The getRootConfigClasses() handles the configuration of the application context created by ContextLoaderListener. Finally the getServletConfigClasses() handles the configuration for the DispacherServlet.

The  WebAppInitializer class is presented below:
package com.tasosmartidis.rest_api_tutorial.config;


import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] {RootConfig.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] {WebConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] {"/"};
    }
}

The WebConfig class will extend the WebMvcConfigurerAdapter and we will override the configureDefaultServletHandling() method. We will configure the DispatcherServlet to forward requests for static resources to the servlet container’s default servlet by enabling the DefaultServletHandlerConfigurer. The class is listed below:
package com.tasosmartidis.rest_api_tutorial.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
@EnableWebMvc
@ComponentScan("com.tasosmartidis.rest_api_tutorial")
public class WebConfig extends WebMvcConfigurerAdapter {
    
    @Override 
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

The RootConfig class is simple since in this tutorial we focus on the REST API. RootConfig 's source code is listed below:
package com.tasosmartidis.rest_api_tutorial.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@Configuration
@EnableWebMvc
@ComponentScan("com.tasosmartidis.rest_api_tutorial")
public class RootConfig {

}

That was it! We are now ready to package our application into a .war, deploy on Tomcat and start using the REST API. We will use the POSTMAN application from Google Chrome to send our CRUD requests to the service.

First we start by creating a couple of diary entries to our repository. First, let's create the first (geek) diary entry:



After sending the POST request the service will respond with the HTTP code and the created entry:
















Next, we will create a second diary entry in a similar fashion, and we will receive the appropriate response:

















Let's now have an overview of the entries we created by retrieving all entries persisted in our application:













Careful readers probably noticed, that when creating a new diary entry, we assign a new ID to the entry to make sure that the entries are uniquely identified, We can use this unique identification to retrieve a specific diary entry:



Next, let's update one of our diary entries, by changing the title and the text of the entry:

















As can be seen in the above picture, we submitted a PUT request with the changed title and text and we received the HTTP code (200-OK) and the updated object. Next, let's DELETE it:










The diary entry specified was deleted from the repository and we received back the id of the deleted entry. Now if we ask for all the available entries, only one should be left:











Done! We performed all CRUD operations through our REST API and the service performed as expected. 

This tutorial introduced users to web services and REST architecture. It demonstrated a simple REST API for performing CRUD operations and used the services using the POSTMAN Chrome application. The complete maven project can be located in my github repos.

In the future I plan to build on this simple project, starting with a client for the REST services, adding persistence to a real database and introducing authentication. So keep in touch!

No comments:

Post a Comment