Creating a REST Web Service With Java and Spring

Creating a REST Web Service With Java and Spring

In this article, you'll learn how to create a REST Web Service With Java and Spring

Originally published by Justin Albano at https://dzone.com

In the modern world of interconnected software, web applications have become an indispensable asset. Foremost among these web applications is the Representational State Transfer (REST) web service, with Java becoming one of the most popular implementation languages. Within the Java REST ecosystem, there are two popular contenders: Java Enterprise Edition (JavaEE) and Spring. While both have their strengths and weaknesses, this article will focus on Spring and create a simple order management RESTful web application using Spring 4. Although this management system will be simple compared to the large-scale RESTful services found today, it will nonetheless demonstrate the basic thought process, design decisions, and implementation tests required to create a Level 3 (hypermedia-driven) Spring REST web service.

By the end of this article, we will have created a fully functional Spring REST order management system. While the source code illustrated in this article covers the essential aspects of the order management system, there are other components and code (such as test cases) that support the main service that are not shown. All of the source code, including these supporting aspects, can be found in the following GitHub repository:

The source code snippets in this article are not in-and-of-themselves sufficient for creating a fully functioning REST web service. Instead, they serve as a snapshot or reflection of the source code contained in the above repository. Therefore, as we walk through each step in creating our REST service, the source code in the above repository should be visited consistently and used as the authoritative reference for all design and implementation choices. For the remainder of this article, when we refer to our order management system, we are actually referring to the Spring REST service contained in the above repository.

Prerequisite Knowledge

It is expected that the reader has at least a novice understanding of dependency injection (DI), particularly DI using the Spring framework. Although we will explore the DI framework configurations used and the DI components utilized in our order management system, it is assumed that the reader has at least a conceptual understanding of the need for and premise of DI. For more information on DI in Spring, see the Spring Framework Guide

This article also assumes that the reader has a foundational understanding of REST and RESTful web services. While we will deep dive into the design and implementation intricacies of creating a REST web service in Spring, we will not focus on the conceptual aspects of REST (such as the use of an HTTP GET or POST call).

Development Cycle and Tools

Our order management system was created using Test Driven Development (TDD), where tests were created first and each design decision and implemented component was focused on passing the created test cases. This not only resulted in a simple set of classes, but a more easily distinguishable set of components. For example, persistence logic and domain logic are not intertwined. Apart from the process used to create the service, there are also numerous tools used to build, test, and deploy the system, including:

  • Spring Model-View-Controller (MVC): the core framework of our web service; this framework provides the necessary annotations and structure required to create our REST endpoints and serve these endpoints over HTTP.
  • Spring Boot: a convention-over-configuration framework that removes a majority of the boilerplate Spring code and configuration; this framework allows us to develop and launch of web service with a fraction of the hassle of a standard Spring web service.
  • Apache Maven: a build and dependency management tool that is used to build, execute tests, and package our web service into a Java Archive (JAR) file that will be executed to run our RESTful web service.
  • JUnit: an automated unit testing framework that will be used for unit and integration tests, as well as to automate the execution of our acceptance tests.
  • Cucumber: an automated acceptance testing framework that allows us to create text-based acceptance criteria and exercise our web service to ensure that all top-level functionality is correct during the development of the service.
  • Java 8: at the time of writing, the latest version of Java; we will utilize the streams API to reduce the code needed to filter through the domain objects in our system.
  • GitHub: a free hosting service for Git-based projects; our web service will be hosted here and the commit history for the web service can be viewed here.
  • Travis Continuous Integration (CI): a free continuous integration platform that executes the automated tests in order of web service each time a commit is pushed to our GitHub repository; the build and test history of our web service can be viewed here.

Although we are using a wide array of frameworks and tools, each has a very important task when building our web service. Before we jump into the implementation, though, we first need to devise a design for our order management system.

Designing the Web Service

The first step to designing our web service is deciding on what we want the service to accomplish. Since we are required to process orders, the definition of an order (i.e. creating a domain model) is a good place to start. Each order that we process should have an identifier (ID) so that we can uniquely point to it, a description that describes the order in a human-readable form, a cost, and a completion status.

Note that the cost is not a trivial issue to deal with in software: It is very easy to lose track of cents and make simple rounding errors. In order to avoid these subliminal issues, we will simply store the cost for our order in cents. This allows us to remove the decimal place and perform simple arithmetic without worrying that we will lose a penny in the mix.

Using this simple definition of an order, we devise the following domain model:

With our order designed, we can move onto designing how we will expose our orders through our RESTful web service. Using the standard HTTP verbs, we can quickly come up with a set of REST endpoints that cover the usual Create, Read, Update, and Delete (CRUD) operations:

It is important to note that we should not simply enumerate the REST endpoints we intended to create, but also include the expected behavior if the endpoint successfully completes the request or if it fails to do so. For example, if a client requests to update a non-existent order, we should return a 404 error to inform the client that this resource does not exist. If we successfully update the resource, we should return a 200 status to inform the client that its request was successfully completed.

At this point, it is also useful to think about what the response bodies for the various REST endpoints will look like. Due to its simplicity, we will consume Javascript Object Notation (JSON) objects in the request bodies we receive and produce JSON objects in the response bodies we send, respectively. For example, if we follow our domain model, the response body for getting an order with a specified ID would resemble:

{
    "id": 1,
    "description": "Some sample order",
    "costInCents": 250,
    "complete": false
}

We will see later in this article that other attributes, such as hypermedia links, will also be included. Irrespective of hypermedia link, thinking about the expected request and response bodies for our REST endpoints allows us to devise test cases in advance that ensure we are handling and producing the expected results when we implement our REST endpoints.

With our domain model and REST endpoints defined, we can move to the last piece of the puzzle: How to store our orders. For example, when we create a new order, we need some means of storing that order so that a client, at some future time, can retrieve the created order.

In a true REST web service, we would decide on the best database or persistence framework that supports our domain model and design a persistence layer interface to use to interact with this database. For example, we could select a Neo4j database if our data was well-suited for a graph domain model, or MongoDB if our domain model fits nicely into collections. In the case of our system, for simplicity, we will use an in-memory persistence layer. Although there are various useful in-memory databases, our model is simple enough to create the in-memory database ourselves. In doing so, we will see the basic functionality of a database attached to a REST service, as well as understand the simple interfaces that are common among repositories in RESTful services.

Abstracting Our Design

At this point in our design, we have three discrete sections of our system: (1) a domain model, (2) a series of REST endpoints, and (3) a means of storing our domain objects, or a persistence layer. This set of three sections is so common, it has its own name: A 3-Tier application. Whatsmore, Martin Fowler has written an entire book, Patterns of Enterprise Architecture, on the patterns that surround this application architecture. The three tiers in this architecture are (1) presentation, (2) domain, and (3) data source (used interchangeably with persistence layer). In our case, our REST endpoints map to the presentation layer, our order domain model maps to the domain layer, and our in-memory database maps to the data source layer.

Although these three layers are usually depicted with one stacked on top of the other, with the presentation layer at the top, closest to the user, the domain layer in the middle, and the data source layer on the bottom, it can be more helpful to look at this architecture in terms of its interactions, as illustrated in the following diagram.

There is an important addition that is made to our architecture: Domain objects are not sent directly to the user. Instead, they are wrapped in resources and the resources are provided to the user. This provides a level of indirection between the domain object and how we present the domain object to the user. For example, if we wish to present the user with a different name for a field in our domain model (say orderName instead of simply name), we can do so using a resource. Although this level of indirection is very useful in decoupling our presentation from the domain model, it does allow duplication to sneak in. In most cases, the resource will resemble the interface of the domain object, with a few minor additions. This issue is addressed later when we implement our presentation layer.

The resource object also provides an apt place for us to introduce our hypermedia links. According to the Richardson model for REST web services, hypermedia-driven services are the highest capability level of a REST application and provide important information associated with the resource data. For example, we can provide links for deleting or updating the resource, which removes the need for the client consuming our REST web service to know the REST endpoints for these actions. In practice, the returned resource (deserialized to JSON) may resemble the following:

{
    "id": 1,
    "description": "Some sample order",
    "costInCents": 250,
    "complete": false
    "_links": {
        "self": {
            "href": "http://localhost:8080/order/1"
        },
        "update": {
            "href": "http://localhost:8080/order/1"
        },
        "delete": {
            "href": "http://localhost:8080/order/1"
        }
    }
}

Given these links, the consumer is no longer required to build the URLs for the update, delete, or self-reference REST endpoints. Instead, it can simply use the links provided in our hypermedia-driven response. Not only does this reduces the logic necessary for interacting with our REST web service (no longer do the URLs need to be built), but it also encapsulates the logic for the construction of the URLs. For example, suppose we change our REST service to require a query parameter, such as sorting: if we provide the links to the consumer, we can adjust for that change, without making any changes to the consumer:

{
    "id": 1,
    "description": "Some sample order",
    "costInCents": 250,
    "complete": false
    "_links": {
        "self": {
            "href": "http://localhost:8080/order/1?sorting=default"
        },
        "update": {
            "href": "http://localhost:8080/order/1?sorting=default"
        },
        "delete": {
            "href": "http://localhost:8080/order/1?sorting=default"
        }
    }
}

Although generating these links could be tedious and subject to a large number of bugs (i.e. what if the IP address of the machine hosting the web service changes?), the Spring Hypermedia as the Engine of Application State (HATEOAS, commonly pronounced hay-tee-os) framework provides numerous classes and builders that allow us to create these links with ease. This topic will be explored further when we delve into the implementation of our presentation layer.

Before moving to the implementation of our web service, we must pull our design together and devise a plan of action to create it. At the moment, we have a single domain object, Order, instances of whom will be persisted in an in-memory database and served up (within a resource) to clients using our REST endpoints. This design leaves us with four main steps:

  • Implement the domain model
  • Create the Order domain class
  • Implement the data source layer
  • Create an in-memory database
  • Implement the CRUD operations for the Order domain class
  • Implement the presentation layer
  • Create the REST endpoints
  • Create the Order resource
  • Create assembler to construct an Order resource with proper HATEOAS links
  • Pull the application together
  • Create the main method that will run the application
Implementing the Web Service

Finding where to start can be difficult, but we will take a systematic approach to creating our web service. Start with the layer that depends on the fewest other layers and is depended on by the greatest number of layers. Thus, we will first implement the domain layer, which does not depend on the other two layers but is depended on by both. Then we will implement the data source layer, which depends on the domain layer and is likewise depended on by the presentation layer. Lastly, we will implement the presentation layer, which is not depended on by any other layer but depends on both the data source and domain layers.

Implementing the Domain Layer

The first step in creating our RESTful web service is creating the domain layer. While our domain layer is very simple, we will soon see that it requires some very specific details to be accounted for in order to be properly used by the rest of our system. The first of these details is identity.

All domain objects that will be persisted must have some unique means of identity; the simplest among these is a unique ID. While there are many data structures that can be used as IDs, such as Universally Unique IDs (UUIDs), we will keep it as simple from the start and use a numeric value. Each class in our domain layer must, therefore, have a means of obtaining the ID associated with the object. In addition, to allow our persistence layer to set the ID of a newly created domain object, we must have a means of setting the ID for an object as well. Since we do not want the persistence layer to depend on any concrete class in our domain layer for this functionality, we will create an interface to accomplish this task:

public interface Identifiable extends org.springframework.hateoas.Identifiable<Long> {
    public void setId(Long id);
}

While we could have simply created an interface with a getter and a setter for ID, we instead extend the Identifiable interface provided by the Spring HATEOAS framework. This interface provides a getId() method and extending this interface allows us to use our domain class within the Spring HATEOAS framework, which will come in handy when we implement the presentation layer. With our identity interface complete, we can now create our Orderclass:

public class Order implements Identifiable {
    private Long id;
    private String description;
    private long costInCents;
    private boolean isComplete;
    @Override
    public Long getId() {
        return id;
    }
    @Override
    public void setId(Long id) {
        this.id = id;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public void setCostInCents(long cost) {
        costInCents = cost;
    }
    public long getCostInCents() {
        return costInCents;
    }
    public void setComplete(boolean isComplete) {
        this.isComplete = isComplete;
    }
    public void markComplete() {
        setComplete(true);
    }
    public void markIncomplete() {
        setComplete(false);
    }
    public boolean isComplete() {
        return isComplete;
    }
}

Our Order class is strikingly simple, but that is the beauty of separating domain classes from the remainder of the system: we can create Plain Old Java Objects (POJOs) that are independent of the rest of the intricacies and idiosyncrasies of our web service (such as how the POJO is persisted and how it is presented through a RESTful interface). Due to the simplicity of this class (it is only composed of getters and setters), we have forgone unit tests.

Although automated unit tests are important, they should be used with discretion. If our Order class contained complicated business logic, such as "an order can only be created between 8am and 5pm EST," we would be wise to implement this logic by first creating a test that exercises this functionality and implement our Orderclass to pass this test. As more functionality is added, we can create more tests and implement this functionality to pass these tests as well (the core of TDD). This leaves us with a general rule about unit testing, especially for domain objects:

Unit tests can be forgone for simple methods, such as getters and setters, but should be used in conjunction with TDD for more complex methods, such as those containing business logic.  

It is also important to note that we have created three methods for setting the completion status of our order: (1) setComplete, (2) markComplete, and (3) markIncomplete. Although it is usually not good practice to set the value of a boolean flag using a setter that takes a boolean argument, we have added this method to our Order class because it allows for simplified updated logic. For example, if we did not have this method, we would have to update the completion status of an order in a manner akin to:

public void updateOrder(Order original, Order updated) {
    // Update the other fields of the order
    if (updated.isComplete()) {
        original.markComplete();
    }
    else {
        original.makeIncomplete();
    }
}

By providing a setter with a boolean argument, we can reduce this update logic to:

public void updateOrder(Order original, Order updated) {
    // Update the other fields of the order
    original.setComplete(updated.isComplete());
}

We have also provided the remaining two setter methods for the completion state to allow for clients to set the completion status in the recommended manner (without passing a boolean flag to a setter method).

In general, we want to externalize the update logic of our domain objects. Instead of creating anupdate(Order updated) method within our Orderclass, we leave it up to external clients to perform the update. The reasoning behind this is that our Order class does not have enough knowledge of the update process needed by external clients to internalize this logic. For example, when we update an Order in the persistence layer, do we update the ID of our order or leave the original ID? That is a decision that should be made in the persistence layer, and therefore, we leave it up to the persistence layer to implement the update logic for our Order class.

Implementing the Data Source Layer

The first step to developing our data source layer is to define the requirements that this layer must fulfill. At its most basic level, our data source must be able to perform the following fundamental operations:

  • Retrieve a list of all orders.
  • Retrieve a specific order based on its ID.
  • Create a new order.
  • Delete an existing order.
  • Update an existing order.

These five operations are common among a vast majority of data sources, and unsurprisingly, follows the basic set of CRUD operations. In this particular case, we create a specialization of the read operation (the R in CRUD) by allowing a caller to supply an ID and retrieve only the order that corresponds to that ID (if such an order exists).

This specialization is particularly useful when dealing with databases, where reads and writes are relatively expensive operations. If we were only capable of retrieving all order and then filtering by ID to find the order of interest, we would wastefully perform a read on each and every order in the database. In a large system with possibly thousands or even millions of entries, this strategy is completely untenable. Instead, we provide methods to target our searches for orders, which in turn allows our data source to optimize the logic used to retrieve entries from its database.

Although we will use an in-memory database in our system, we will include this specialized retrieval method in order to maintain a consistent interface with clients. Although we internally know that the data source is an in-memory database, any clients should not have to change depending on our internal data source implementation.

Since we will be using an in-memory persistence scheme, we cannot rely on a database to provide a new ID for each domain class that we persist. Instead, we will need to create an ID generator that will provide us with unique IDs for each of the Orders we will persist. Our implementation of this generator is as follows:

@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class IdGenerator {
    private AtomicLong nextId = new AtomicLong(1);
    public long getNextId() {
        return nextId.getAndIncrement();
    }
}

Although our ID generator is very simple, there are some important aspects that require special attention. First, we have marked our generator as a Spring component using the @Component annotation. This annotation allows our Spring boot application to recognize (through component scanning) our generator class as an injectable component. This will allow us to use the@Autowired annotation in other classes and have our component injected without having to create the generator using the new operator. For more information on Spring DI and autowiring, see the Spring Reference Document for Inversion of Control (IoC) Container.

We have also denoted that the scope of the generator (using the @Scope annotation) is PROTOTYPE, which ensures that a new object is created each time is it autowired. By default, when Spring injects an autowired dependency, it treats each component as a singleton, injecting the same object into each autowired location (within the same context). Using this default logic would result in each caller receiving the same generator, which would result in inconsistent ID generation.

For example, suppose we inject an ID generator into two data sources. If the same generator is used, if source one gets an ID, the resulting ID will be 1. If source two then requests an ID, the resulting ID will then be 2. This is contrary to our desired scheme, where each data source starts with an ID of 1 and increments only when a new object is created within that data source (the IDs can be differentiated based on the type of the object, i.e. "this is order 1" and "this is user 1"). Thus, the first object created in data source one should have an ID of 1, while the first object created in data source two should have an ID of 1 as well. This can only be accomplished if we have different ID generator objects, hence the prototype scoping of our ID generator class.

A second point is the use of AtomicLong to store our next ID. It may be tempting to use a simple long to store the next ID, but this could lead to a very nuanced situation: What if two different calls are made to generate an ID? Since we are working within the realm of a concurrent web application, if we forgo any type of synchronization logic, we would run into a classic race condition, where two identical IDs may be produced. In order to eliminate these difficult-to-debug issues, we use the built-in concurrency mechanisms provided by Java. This ensures that even if two callers request IDs at the same time, each will be unique and the consistency of the next ID will be maintained.

With our ID generator in place, we are now ready to implement our data source. Since much of the in-memory logic is common for all types of objects, we will create an Abstract Base Class (ABC) that contains the core logic for managing our persisted objects (note that Spring uses the nomenclature Repository to mean a data source, and hence we follow the same convention):

public abstract class InMemoryRepository<T extends Identifiable> {
    @Autowired
    private IdGenerator idGenerator;
    private List<T> elements = Collections.synchronizedList(new ArrayList<>());
    public T create(T element) {
        elements.add(element);
        element.setId(idGenerator.getNextId());
        return element;
    }
    public boolean delete(Long id) {
        return elements.removeIf(element -> element.getId().equals(id));
    }
    public List<T> findAll() {
        return elements;
    }
    public Optional<T> findById(Long id) {
        return elements.stream().filter(e -> e.getId().equals(id)).findFirst();
    }
    public int getCount() {
        return elements.size();
    }
    public void clear() {
        elements.clear();
    }
    public boolean update(Long id, T updated) {
        if (updated == null) {
            return false;
        }
        else {
            Optional<T> element = findById(id);
            element.ifPresent(original -> updateIfExists(original, updated));
            return element.isPresent();
        }
    }
    protected abstract void updateIfExists(T original, T desired);
}

The basic premise of this InMemoryRepository is simple: Provide a mechanism for all five of the previously enumerated persistence methods for any object that implements the Identifiable interface. This is where the importance of our Identifiable interface is made apparent: Our in-memory repository knows nothing about the objects it is storing, except that it can get and set the ID of those objects. Although we have created a dependency between the data source and domain layers, we have made that dependency very weak, where the data source layer only depends on an interface (rather than an abstract or concrete class that contains executable code) with only two methods. The fact that this interface is very unlikely to change also ensures that any changes to the domain layer will be unlikely to affect the data source layer.

As previously stated, our ID generator is capable of being injected into other classes, and we have done so using the @Autowired annotation. In addition, we have used a synchronized list to store our managed objects. For the same reason as the AtomicLong in our ID generator, we must ensure that we do not allow for a race condition if multiple callers try to perform concurrent operations on our data source.

While each of the operations is simple in their implementation (due to the simple in-memory design), there is one piece of information that requires knowledge of the stored object to perform: an update if the object exists. As previously expressed, our domain objects do not have enough knowledge on how to perform an update on themselves, therefore, it is the responsibility of our data source to perform this update.

Since this operation requires information we do not have, we mark it as abstract and require that concrete subclasses provide an implementation of this template method. In our case, we have only one subclass:

@Repository
public class OrderRepository extends InMemoryRepository<Order> {
    protected void updateIfExists(Order original, Order updated) {
        original.setDescription(updated.getDescription());
        original.setCostInCents(updated.getCostInCents());
        original.setComplete(updated.isComplete());
    }
}

This concrete class simply provides an implementation for updating all non-ID fields of an Order. We have also decorated our class with the @Repository annotation, which is the same as the@Component annotation (allows our class to be injected into other classes by the Spring DI framework) but also includes additional database-related capabilities. For more information, see the Javadocs for the @Repository annotation.

Although we were required to account for synchronization and DI capabilities, our entire data source layer consists of three classes and less than 70 lines of code (not counting package and import declarations). This is where the power of using a framework such as Spring comes to the forefront: many of the common tasks and conventions in web application development have been codified and made as simple as decorating classes and fields with the correct annotations. With the completion of our data source layer, we are ready to implement the final layer of our RESTful web service: the presentation layer.

Implementing the Presentation Layer

Without the aid of a web application framework, creating a presentation layer would be a daunting task, but after many years, the patterns and conventional designs of RESTful web services have been captured in the Spring Model-View-Controller (MVC) framework. This framework allows us to create RESTful endpoints with much the same ease as we saw during the development of our data source layer, using annotations and helper classes to do most of the heavy lifting for us.

Starting with the class that is the most depended on and requires the least dependencies, we will create the OrderResource first:

public class OrderResource extends ResourceSupport {
    private final long id;
    private final String description;
    private final long costInCents;
    private final boolean isComplete;
    public OrderResource(Order order) {
        id = order.getId();
        description = order.getDescription();
        costInCents = order.getCostInCents();
        isComplete = order.isComplete();
    }
    @JsonProperty("id")
    public Long getResourceId() {
        return id;
    }
    public String getDescription() {
        return description;
    }
    public long getCostInCents() {
        return costInCents;
    }
    public boolean isComplete() {
        return isComplete;
    }
}

The OrderResource class is strikingly similar to our Order class, but with a few main differences. First, we inherit from the ResourceSupport class provided by the Spring HATEOAS packages, which allows us to attach links to our resource (we will revisit this topic shortly). Second, we have made all of the fields final. Although this is not a requirement, it is a good practice because we wish to restrict the values of the fields in the resource from changing after they have been set, ensuring that they reflect the values of the Order class for which it is acting as a resource.

In this simple case, the OrderResource class has a one-to-one field relationship with the Order class, which begs the question: Why not just use the Order class? The primary reason to create a separate resource class is that the resource class allows us to implement a level of indirection between the Order class itself and how that class is presented. In this case, although the fields are the same, we are also attaching links to the fields in the Order class. Without a dedicated resource class, we would have to intermix the domain logic with the presentation logic, which would cause serious dependency issues in a large-scale system.

A happy medium between the duplication between the OrderResource and Order classes is the use of Jackson annotations in order to use the fields of the Order class to act as the fields of theOrderResource class when the OrderResource class is serialized to JSON. In the Spring MVC framework, our resource class will be converted to JSON before being sent over HTTP to the consumers of our web service. The default serialization process takes each of the fields of our class and uses the field names as the keys and the field values as the values. For example, a serialized Order class may resemble the following:

{
    "id": 1,
    "description": "Some test description",
    "costInCents": 200,
    "complete": true
}

If we tried to directly embed the Order object inside our OrderResource object (implemented our OrderResourceclass to have a single field that holds an Order object in order to reuse the fields of the Orderobject), we would end up with the following:

{
    "order": {
        "id": 1,
        "description": "Some test description",
        "costInCents": 200,
        "complete": true
    }
}

Instead, we can annotate the nested Orderobject with the Jackson @JsonUnwrapped annotation, which removes the nesting when the OrderResource object is serialized. Such an implementation would result in the following definition for the OrderResource class:

public class OrderResource extends ResourceSupport {
    @JsonUnwrapped
    private final Order order;
    public OrderResource(Order order) {
        this.order = order;
    }
}

Serializing this class would result in our desired JSON:

{
    "id": 1,
    "description": "Some test description",
    "costInCents": 200,
    "complete": true
}

While unwrapping the nested Orderobject significantly reduces the size of the OrderResource class, it has one drawback: When the internal fields of the Order changes, so do the resulting serialized JSON produced from the OrderResource object. In essence, we have coupled theOrderResource class and the internal structure of the Orderclass, breaking encapsulation. We walk a fine line between the duplication seen in the first approach (replicating the Order fields within OrderResource) and the coupling seen in the JSON unwrapping approach. Both have advantages and drawbacks, and judgment and experience will dictate the best times to use each.

One final note on our OrderResource class: We cannot use the getId()method as our getter for our ID since the ResourceSupport class has a default getId() method that returns a link. Therefore, we use the getResourceId() method as our getter for our id field; thus, we have to annotate our getResourceId() method since, by default, our resource would serialize the ID field to resourceId due to the name of the getter method. To force this property to be serialized to id, we use the @JsonProperty("id") annotation.

With our resource class in place, we need to implement an assembler that will create anOrderResource from an Order domain object. To do this, we will focus on two methods: (1)toResource, which consumes a single Order object and produces an OrderResource object, and (2) toResourceCollection, which consumes a collection of Order objects and produces a collection of OrderResource objects. Since we can implement the latter in terms of the former, we will abstract this relationship into an ABC:

public abstract class ResourceAssembler<DomainType, ResourceType> {
    public abstract ResourceType toResource(DomainType domainObject);
    public Collection<ResourceType> toResourceCollection(Collection<DomainType> domainObjects) {
        return domainObjects.stream().map(o -> toResource(o)).collect(Collectors.toList());
    }
}

In our implementation of toResourceCollection, we simply map the consumed list of Order objects to OrderResource objects by calling the toResource method on each of the Order objects in the consumed list. We then create an OrderResourceAssembler class that provides an implementation for the toResource method:

@Component
public class OrderResourceAssembler extends ResourceAssembler<Order, OrderResource> {
    @Autowired
    protected EntityLinks entityLinks;
    private static final String UPDATE_REL = "update";
    private static final String DELETE_REL = "delete";
    @Override
    public OrderResource toResource(Order order) {
        OrderResource resource = new OrderResource(order);
        final Link selfLink = entityLinks.linkToSingleResource(order);
        resource.add(selfLink.withSelfRel());
        resource.add(selfLink.withRel(UPDATE_REL));
        resource.add(selfLink.withRel(DELETE_REL));
        return resource;
    }
}

In this concrete class, we simply extend the ResourceAssembler ABC, declaring the domain object type and the resource object type, respectively, as the generic arguments. We are already familiar with the @Component annotation, which will allow us to inject this assembler into other classes as needed. The autowiring of the EntityLinks class requires some further explanation.

As we have already seen, creating links for a resource can be a difficult task. In order to remedy this difficulty, the Spring HATEOAS framework includes an EntityLinks class that provides helper methods that provide for the construction of links using just the domain object type. This is accomplished by having a REST endpoint class (which we will define shortly) use the@ExposesResourceFor(Class domainClass) annotation, which tells the HATEOAS framework that links built for the supplied domain class should point to that REST endpoint.

For example, suppose we create a REST endpoint that allows a client to create, retrieve, update, and delete Order objects. In order to allow for Spring HATEOAS to help in the creation of links to delete and update Order objects, we must decorate the REST endpoint class with@ExposesResourceFor(Order.class). We will see shortly how this ties into the path used by the REST endpoint. For the time being, it suffices to say that the EntityLinks class allows us to create links to objects that have a corresponding @ExposesResourceFor somewhere in our system. For more information on how this exposure occurs, see the Spring HATEOAS reference documentation.

The remainder of our OrderResourceAssembler class is devoted to the creation of OrderResource objects from Order objects. The creation of the resource object itself is straightforward, but the creation of the links requires some explanation. Using the EntityLinks class, we can create a link to our own resource by specifying (using the linkToSingleResource method) that we wish to create a link to an Order , which uses the Spring HATEOAS Identifiable interface to obtain the ID of the object. We then reuse this link to create three separate links: (1) a self link, (2) an update link, and (3) a delete link. We set the relative value (rel) of the link using the withRel method. We then return the fully constructed resource object. Given the three links we have created, our resulting OrderResource, when serialized to JSON, looks as follows:

{
    "id": 1,
    "description": "Some sample order",
    "costInCents": 250,
    "complete": false
    "_links": {
        "self": {
            "href": "http://localhost:8080/order/1"
        },
        "udpate": {
            "href": "http://localhost:8080/order/1"
        },
        "delete": {
            "href": "http://localhost:8080/order/1"
        }
    }
}

The self link tells the consumer that if a link to this resource is needed, the provided HREF can be used. The update and delete links tell the consumer that if this resource should be updated or deleted, respectively, the provided HREF should be used.

With the OrderResource class and its assembler completed, we can move onto the last, and arguably most essential step: creating the REST endpoints. In the Spring MVC framework, a REST endpoint is created by implementing a controller class (a class annotated with @Controller or @RestController) and adding methods that correspond to the desired REST endpoints. We will list our controller class first and then explain the meaning of each section of code:

@CrossOrigin(origins = "*")
@RestController
@ExposesResourceFor(Order.class)
@RequestMapping(value = "/order", produces = "application/json")
public class OrderController {
    @Autowired
    private OrderRepository repository;
    @Autowired
    private OrderResourceAssembler assembler;
    @RequestMapping(method = RequestMethod.GET)
    public ResponseEntity<Collection<OrderResource>> findAllOrders() {
        List<Order> orders = repository.findAll();
        return new ResponseEntity<>(assembler.toResourceCollection(orders), HttpStatus.OK);
    }
    @RequestMapping(method = RequestMethod.POST, consumes = "application/json")
    public ResponseEntity<OrderResource> createOrder(@RequestBody Order order) {
        Order createdOrder = repository.create(order);
        return new ResponseEntity<>(assembler.toResource(createdOrder), HttpStatus.CREATED);
    }
    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public ResponseEntity<OrderResource> findOrderById(@PathVariable Long id) {
        Optional<Order> order = repository.findById(id);
        if (order.isPresent()) {
            return new ResponseEntity<>(assembler.toResource(order.get()), HttpStatus.OK);
        }
        else {
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }
    }
    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
    public ResponseEntity<Void> deleteOrder(@PathVariable Long id) {
        boolean wasDeleted = repository.delete(id);
        HttpStatus responseStatus = wasDeleted ? HttpStatus.NO_CONTENT : HttpStatus.NOT_FOUND;
        return new ResponseEntity<>(responseStatus);
    }
    @RequestMapping(value = "/{id}", method = RequestMethod.PUT, consumes = "application/json")
    public ResponseEntity<OrderResource> updateOrder(@PathVariable Long id, @RequestBody Order updatedOrder) {
        boolean wasUpdated = repository.update(id, updatedOrder);
        if (wasUpdated) {
            return findOrderById(id);
        }
        else {
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }
    }
}

Note that the @CrossOrigin annotation provides for support of Cross Origin Resource Sharing (CORS) for our controller; for more information, see the Spring Enabling Cross Origin Requests for a RESTful Web Service tutorial. The @RestControllera annotation, as stated above, tells Spring that this class is a controller and will include REST endpoints. This annotation is coupled with the @ExposesResourceFor(Order.class) annotation, which denotes that if a link is needed to anOrder object, this controller should be used to provide the path for that link. The path information for the controller (i.e. the /order in http://localhost:8080/order) is supplied using the @RequestMapping, which maps the supplied string as the path for the controller.

For example, if the URL of the machine that the controller is executed on is http://localhost:8080, the path to reach this controller will be http://localhost:8080/order. We also include the type of the data produced by the controller, or application/json, in the request mapping to instruct Spring that this controller class produces JSON output (Spring will, in turn, include Content-Type: application/json in the header of any HTTP responses sent).

Within the controller class, we inject the OrderRepository and OrderResourceAssembler components which will allow us to access the stored Order objects and create OrderResource objects from these domain objects, respectively. Although we have a dependency to the data store layer within our controller class, we lean on Spring to provide us with an instance of the OrderRepository, ensuring that we are only dependent on the external interface of the repository, rather than on the creation process.

The last portion of the controller class is the most crucial: the methods that will perform the REST operations. In order to declare a new REST endpoint, we use the @RequestMapping to annotate a method and supply the HTTP verb that we wish to use. For example, if we look at the findAllOrders method,

@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<Collection<OrderResource>> findAllOrders() {
    List<Order> orders = repository.findAll();
    return new ResponseEntity<>(assembler.toResourceCollection(orders), HttpStatus.OK);
}

we use the @RequsetMapping annotation to inform the Spring MVC framework thatfindAllOrders is intended to be called when an HTTP GET is received. This process is called mapping, and as we will see later, Spring will establish this mapping during deployment. It is important to note that the path of the mapping is relative to the path declared at the controller level. For example, since our OrderController is annotated with @RequestMapping(“/order”) and no path is explicitly declared for our findAllOrders method, the path used for this method is /orders.

The return type of ourfindAllOrders method is particularly important. The ResponseEntity class is provided by the Spring MVC framework and represents an HTTP response to an HTTP request. The generic parameter of this class represents the class of the object that will be contained in the response body of the call; this response body object will be serialized to JSON and then returned to the requesting client as a JSON string.

In this case, we will return a collection of OrderResource objects (the list of all existing orders) after obtaining them from the OrderRepository. This list is then assembled into a list of OrderResource objects and packed into a ResponseEntity object in the following line:

return new ResponseEntity<>(assembler.toResourceCollection(orders), HttpStatus.OK);

The second argument to the new ResponseEntity class represents the HTTP status code that should be used if no exceptions or errors occur while sending the response to the requesting client. In this case, we will accompany our collection of OrderResource objects with an HTTP status code of 200 OK using the enum value HttpStatus.OK.

The remainder of the REST endpoints uses this same basic structure, with a few notable exceptions. In the case of our findOrderByIddeleteOrder, and updateOrder methods, we adjust the path of the REST endpoint to include /{id}. As previously stated, this path is relative to the controller path, and thus the resolved path for each of these methods is /order/{id}. The use of curly braces ({ and }) in a path denotes a variable whose value will be resolved to a parameter in the method it annotates. For example, if we look at the findOrderById method

@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ResponseEntity<OrderResource> findOrderById(@PathVariable Long id) {
    // …Body hidden for brevity…
}

we see that the name of the parameter (id) matches the variable in the path and is decorated with the @PathVariable annotation. The combination of these two adornments tells Spring that we wish to have the value of the id variable in the path passed as the runtime value of theid parameter in our findOrderById method. For example, if a GET request is made to /order/1, the call to our findOrderById method will be findOrderById(1).

Another difference that must be addressed is the return value of the deleteOrder method: The return value of ResponseEntity<Void> tells Spring MVC that we are returning a ResponseEntity with an associated HTTP status code but we are not including a response body (the response body is void). This results in an empty response body for the response sent to the requester.

The last difference deals with the parameters of updateOrder. In the case of updating an order, we must use the contents of the request body, but doing so as a string would be tedious. In that case, we would have to parse the string and extract the desired data, ensuring that we do not make an easy error during the parsing process. Instead, Spring MVC will deserialize the request body into an object of our choice. If we look at the updateOrder method

@RequestMapping(value = "/{id}", method = RequestMethod.PUT, consumes = "application/json")
public ResponseEntity<OrderResource> updateOrder(@PathVariable Long id, @RequestBody Order updatedOrder) {
    // …Body hidden for brevity…
}

we see that the updatedOrder parameter is decorated with the @RequestBody annotation. This instructs Spring MVC to deserialize the HTTP request body into the updateOrder parameter, which takes the JSON request body (denoted by the consumes = “application/json” field in the @RequestMapping annotation) and deserializes it into an Order object (the type of updateOrder). We are then able to use the updatedOrder parameter in the body of ourupdateOrder method.

With our REST endpoints defined, we are now ready to create the main method that will be executed to start our RESTful web service.

Pulling it Together

The main method used to start our web service is as follows:

@EnableEntityLinks
@EnableHypermediaSupport(type = HypermediaType.HAL)
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

The @EnableEntityLinks annotation configures Spring to include support for the EntityLinks class in our system (allowing us to inject the EntityLinks object). Likewise, the@EnableHypermediaSupport annotation instructs Spring to include support for HATEOAS, using the Hypermedia Application Language (HAL) when producing links. The final annotation,@SpringBootApplication, marks our application a Spring Boot application, which configures the boilerplate code needed to start Spring and also instructs Spring to component scan our packages to find injectable classes (such as those annotated with @Component or @Repository).

The remainder of the main method simply runs the Spring Boot application, passing the current class and the command line arguments to the run method. Using Spring Boot, starting our web application is nearly trivial, which leaves us with only one thing left to do: Deploy and consume our RESTful web service. 

Running and Consuming the Web Service

Since we are using Maven to manage the dependencies and build the lifecycle of our application, and Spring Boot to configure our application, we can build our project and start the HTTP server using the following command (once Maven has been installed):

mvn spring-boot:run

This will host the REST web service on http://localhost:8080. If we look closely at the output, we can see the following statements:

INFO 15204 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/order/{id}],methods=[PUT],produces=[application/json]}" onto public org.springframework.http.ResponseEntity<com.dzone.albanoj2.example.rest.resource.OrderResource> com.dzone.albanoj2.example.rest.controller.OrderController.updateOrder(java.lang.Long,com.dzone.albanoj2.example.rest.domain.Order)
INFO 15204 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/order],methods=[POST],produces=[application/json]}" onto public org.springframework.http.ResponseEntity<com.dzone.albanoj2.example.rest.resource.OrderResource> com.dzone.albanoj2.example.rest.controller.OrderController.createOrder(com.dzone.albanoj2.example.rest.domain.Order)
INFO 15204 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/order/{id}],methods=[GET],produces=[application/json]}" onto public org.springframework.http.ResponseEntity<com.dzone.albanoj2.example.rest.resource.OrderResource> com.dzone.albanoj2.example.rest.controller.OrderController.findOrderById(java.lang.Long)
INFO 15204 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/order],methods=[GET],produces=[application/json]}" onto public org.springframework.http.ResponseEntity<java.util.Collection<com.dzone.albanoj2.example.rest.resource.OrderResource>> com.dzone.albanoj2.example.rest.controller.OrderController.findAllOrders()
INFO 15204 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/order/{id}],methods=[DELETE],produces=[application/json]}" onto public org.springframework.http.ResponseEntity<java.lang.Void> com.dzone.albanoj2.example.rest.controller.OrderController.deleteOrder(java.lang.Long)

This output explicitly tells us that Spring has successfully found our controller class and has mapped the enumerated URLs to the respective methods of our OrderController class. With our web application started, we can retrieve the list of all existing orders by navigating tohttp://localhost:8080/order in any browser. When we enter this URL into a browser, the browser sends an HTTP GET to our web service, which in turn calls the findAllOrders() method of our OrderController class and returns a list of all orders, wrapped asOrderResource objects and displayed as serialized JSON. By default, the following should be returned when we navigate to http://localhost:8080/order:

[]

Since we have not created any orders yet, we see an empty list. In order to create an order, we must use an HTTP client program, such as Postman (for more information on how to use Postman to send HTTP requests, see the official Postman documentation). Using this client, we can create an order by setting the request type to POST, the request URL to http://localhost:8080/order, and the request body to the following:

{
    "description": "Our first test order",
    "costInCents": 450,
    "complete": false
}

After clicking the Send button, we should see a response status code of 201 Created and response body of the following:

{
    "description": "Our first test order",
    "costInCents": 450,
    "complete": false,
    "_links": {
        "self": {
            "href": "http://localhost:8080/order/1"
        },
        "update": {
            "href": "http://localhost:8080/order/1"
        },
        "delete": {
            "href": "http://localhost:8080/order/1"
        }
    },
    "id": 1
}

We can now navigate back to http://localhost:8080/order in our browser (or perform a GET on that address in Postman) and see the following response:

[
    {
        "description": "Our first test order",
        "costInCents": 450,
        "complete": false,
        "_links": {
            "self": {
                "href": "http://localhost:8080/order/1"
            },
            "update": {
                "href": "http://localhost:8080/order/1"
            },
            "delete": {
                "href": "http://localhost:8080/order/1"
            }
        },
        "id": 1
    }
]

This is simply a list containing our only order. If we wish to make an update to our existing order, we can simply execute a PUT to http://localhost:8080/order/1 (note the /1 at the end of the URL, denoting that we are directing our request to the order with ID 1) with the following request body:

{
    "description": "Some updated description",
    "costInCents": 700,
    "complete": true
}

If we then perform a GET on http://localhost:8080/order/1, we will see the following response:

{
    "description": "Some updated description",
    "costInCents": 700,
    "complete": true,
    "_links": {
        "self": {
            "href": "http://localhost:8080/order/1"
        },
        "update": {
            "href": "http://localhost:8080/order/1"
        },
        "delete": {
            "href": "http://localhost:8080/order/1"
        }
    },
    "id": 1
}

The last remaining task we can complete is to delete the order. To do this, simply execute a DELETE with the URL http://localhost:8080/order/1, which should return a response status code of 204 No Content. To check that we have successfully deleted the order, we can execute a GET on http://localhost:8080/order/1 and we should receive a response status code of 404 Not Found.

With the completion of this delete call, we have successfully exercised all of the CRUD operations that we implemented for orders in our REST web service. An enumeration of the calls we have made is tabulated below:

Conclusion

RESTful web services are at the core of most systems today. From Google to Amazon to Netflix, there is hardly a major software system that does not include some form of a RESTful web service that interacts with clients. At the origin of the REST revolution, creating these applications was a tedious task, but with the advent of frameworks such as Spring, and supplemental frameworks such as Spring Boot, this task has been tremendously simplified. With such tools, we are left to focus on the simple creation of a domain layer, persistence (data source) layer, presentation layer, and minimal configuration code to spin up a web service.

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading

Spring & Hibernate for Beginners (includes Spring Boot)

Spring Framework Master Class - Learn Spring the Modern Way!

Master Microservices with Spring Boot and Spring Cloud

Spring Boot and OAuth2: Getting the Authorization Code

An Introduction to Spring Boot

How to build GraphQL APIs with Kotlin, Spring Boot, and MongoDB?

Build a Rest API with Spring Boot using MySQL and JPA

Angular 8 + Spring Boot 2.2: Build a CRUD App Today!

Spring Boot vs. Spring MVC vs. Spring: How Do They Compare?

Top 4 Spring Annotations for Java Developer in 2019

Web Service Tutorial: Streaming Data with Spring Boot RESTful

Web Service Tutorial: Streaming Data with Spring Boot RESTful

In this article, we are going to look at an example to download files using StreamingResponseBody. In this approach, data is processed and written in chunks to the OutputStream.

In this article, we are going to look at an example to download files using StreamingResponseBody. In this approach, data is processed and written in chunks to the OutputStream.

Streaming data is a radical new approach to sending data to web browsers which provides for dramatically faster page load times. Quite often, we need to allow users to download files in web applications. When the data is too large, it becomes quite a challenge to provide a good user experience.

Spring offers support for asynchronous request processing via StreamingResponseBody. In this approach, an application can write data directly to the response OutputStream without holding up the Servlet container thread. There are a few other methods in Spring to handle asynchronous request processing.

Setting Up Spring Boot Project

Create a sample Spring Boot application. Here is my sample project structure. I have created the project manually, but you could also create using Spring Intializer.

Project structure

Let us add some basic dependencies to Maven POM.

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.techshard.streamingresponse</groupId>
    <artifactId>springboot-download</artifactId>
    <version>1.0-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
        <relativePath />
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3.2</version>
        </dependency>
    </dependencies>
</project>

We will now create a controller and add an API endpoint for download. Here is my complete controller.

package com.techshard.download.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@RestController
@RequestMapping ("/api")
public class DownloadController {
    private final Logger logger = LoggerFactory.getLogger(DownloadController.class);
    @GetMapping (value = "/download", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<StreamingResponseBody> download(final HttpServletResponse response) {
        response.setContentType("application/zip");
        response.setHeader(
                "Content-Disposition",
                "attachment;filename=sample.zip");
        StreamingResponseBody stream = out -> {
            final String home = System.getProperty("user.home");
            final File directory = new File(home + File.separator + "Documents" + File.separator + "sample");
            final ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream());
            if(directory.exists() && directory.isDirectory()) {
                try {
                    for (final File file : directory.listFiles()) {
                        final InputStream inputStream=new FileInputStream(file);
                        final ZipEntry zipEntry=new ZipEntry(file.getName());
                        zipOut.putNextEntry(zipEntry);
                        byte[] bytes=new byte[1024];
                        int length;
                        while ((length=inputStream.read(bytes)) >= 0) {
                            zipOut.write(bytes, 0, length);
                        }
                        inputStream.close();
                    }
                    zipOut.close();
                } catch (final IOException e) {
                    logger.error("Exception while reading and streaming data {} ", e);
                }
            }
        };
        logger.info("steaming response {} ", stream);
        return new ResponseEntity(stream, HttpStatus.OK);
    }
}

In this API endpoint, we are reading multiple files from a directory and creating a zip file. We are executing this process within StreamingResponseBody*. It writes data directly to an OutputStream before passing that written information back to the client using aResponseEntity. *This means that the download process will start immediately on the client, while the server is processing and writing data in chunks.

Start the server and test this endpoint using http://localhost:8080/api/download.

When using StreamingResponseBody, it is highly recommended to configure TaskExecutor used in Spring MVC for executing asynchronous requests. TaskExecutor is an interface that abstracts the execution of a Runnable.

Let us configure the TaskExecutor. Here is the AsyncConfiguration class which configures timeout using WebMvcCofigurer and also registers an interceptor that is called when there's a timeout in case you need some special handling.

package com.techshard.download;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
import org.springframework.web.context.request.async.TimeoutCallableProcessingInterceptor;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.concurrent.Callable;
@Configuration
@EnableAsync
@EnableScheduling
public class AsyncConfiguration implements AsyncConfigurer {
    private final Logger log = LoggerFactory.getLogger(AsyncConfiguration.class);
    @Override
    @Bean (name = "taskExecutor")
    public AsyncTaskExecutor getAsyncExecutor() {
        log.debug("Creating Async Task Executor");
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(25);
        return executor;
    }
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new SimpleAsyncUncaughtExceptionHandler();
    }
    /** Configure async support for Spring MVC. */
    @Bean
    public WebMvcConfigurer webMvcConfigurerConfigurer(AsyncTaskExecutor taskExecutor, CallableProcessingInterceptor callableProcessingInterceptor) {
        return new WebMvcConfigurer() {
            @Override
            public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
                configurer.setDefaultTimeout(360000).setTaskExecutor(taskExecutor);
                configurer.registerCallableInterceptors(callableProcessingInterceptor);
                WebMvcConfigurer.super.configureAsyncSupport(configurer);
            }
        };
    }
    @Bean
    public CallableProcessingInterceptor callableProcessingInterceptor() {
        return new TimeoutCallableProcessingInterceptor() {
            @Override
            public <T> Object handleTimeout(NativeWebRequest request, Callable<T> task) throws Exception {
                log.error("timeout!");
                return super.handleTimeout(request, task);
            }
        };
    }
}

Conclusion

Using StreamingResponseBody, we can now stream data easily for highly-concurrent applications. I hope you enjoyed this article. Let me know if you have any comments or suggestion in the comments section below.

The example for this article can be found on GitHub repository.

What is REST API? | Restful Web Service

What is REST API? | Restful Web Service

In this post "Restful Web Service", you'll learn: What is Web services, what is API, What is REST API, How REST works and Implementation of REST API

What is REST API? | Restful Web Service

A REST API defines a set of functions to process requests and responses via HTTP protocol.

REST is used in mobile application as well as in web applications.


Securing RESTful API with Spring Boot, Security, and Data MongoDB

Securing RESTful API with Spring Boot, Security, and Data MongoDB

A comprehensive step by step tutorial on securing or authentication RESTful API with Spring Boot, Security, and Data MongoDB

A comprehensive step by step tutorial on securing or authentication RESTful API with Spring Boot, Security, and Data MongoDB. Previously, we have shown you how to securing Spring Boot, MVC and MongoDB web application. In this tutorial, the secure endpoint will restrict the access from an unauthorized request. Every request to secure endpoint should bring authorization token with it. Of course, there will be an endpoint for login which will get authorization token after successful login.

Table of Contents:

The following software, tools, and frameworks are required for this tutorial:

We assume that you already installed all required software, tools, and frameworks. So, we will not cover how to install that software, tools, and frameworks.

1. Generate a New Spring Boot Gradle Project

To create or generate a new Spring Boot Application or Project, simply go to Spring Initializer. Fill all required fields as below then click on Generate Project button.

The project will automatically be downloaded as a Zip file. Next, extract the zipped project to your java projects folder. On the project folder root, you will find build.gradle file for register dependencies, initially it looks like this.

buildscript {
&nbsp;&nbsp; &nbsp;ext {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;springBootVersion = '2.1.2.RELEASE'
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;repositories {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;mavenCentral()
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;dependencies {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
&nbsp;&nbsp; &nbsp;}
}

apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.djamware'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
&nbsp;&nbsp; &nbsp;mavenCentral()
}

dependencies {
&nbsp;&nbsp; &nbsp;implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
&nbsp;&nbsp; &nbsp;implementation 'org.springframework.boot:spring-boot-starter-security'
&nbsp;&nbsp; &nbsp;implementation 'org.springframework.boot:spring-boot-starter-web'
&nbsp;&nbsp; &nbsp;testImplementation 'org.springframework.boot:spring-boot-starter-test'
&nbsp;&nbsp; &nbsp;testImplementation 'org.springframework.security:spring-security-test'
}

Now, you can work with the source code of this Spring Boot Project using your own IDE or Text Editor. We are using Spring Tool Suite (STS). In STS, import the extracted zipped file as Existing Gradle Project.

Next, we have to add the JWT library to the build.gradle as the dependency. Open and edit build.gradle then add this line to dependencies after other implementation.

implementation 'io.jsonwebtoken:jjwt:0.9.1'

Next, compile the Gradle Project by type this command from Terminal or CMD.

./gradlew compile

Or you can compile directly from STS by right-clicking the project name then choose Gradle -> Refresh Gradle Project. Next, open and edit src/main/resources/application.properties then add these lines.

spring.data.mongodb.database=springmongodb
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017

2. Create Product, User and Role Model or Entity Classes

We will be creating all required models or entities for products, user and role. In STS, right-click the project name -> New -> Class. Fill the package with com.djamware.SecurityRest.models, the name with Product, and leave other fields and checkbox as default then click Finish Button.

Next, open and edit src/main/java/com/djamware/SecurityRest/models/Product.java then add this annotation above the class name that will point to MongoDB collection.

@Document(collection = "products")

Inside Product class, add these variables.

@Id
String id;
String prodName;
String prodDesc;
Double prodPrice;
String prodImage;

Add constructors after the variable or fields.

public Product() {
}

public Product(String prodName, String prodDesc, Double prodPrice, String prodImage) {
&nbsp;&nbsp; &nbsp;super();
&nbsp;&nbsp; &nbsp;this.prodName = prodName;
&nbsp;&nbsp; &nbsp;this.prodDesc = prodDesc;
&nbsp;&nbsp; &nbsp;this.prodPrice = prodPrice;
&nbsp;&nbsp; &nbsp;this.prodImage = prodImage;
}

Generate or create Getter and Setter for each field.

public String getId() {
&nbsp;&nbsp; &nbsp;return id;
}

public void setId(String id) {
&nbsp;&nbsp; &nbsp;this.id = id;
}

public String getProdName() {
&nbsp;&nbsp; &nbsp;return prodName;
}

public void setProdName(String prodName) {
&nbsp;&nbsp; &nbsp;this.prodName = prodName;
}

public String getProdDesc() {
&nbsp;&nbsp; &nbsp;return prodDesc;
}

public void setProdDesc(String prodDesc) {
&nbsp;&nbsp; &nbsp;this.prodDesc = prodDesc;
}

public Double getProdPrice() {
&nbsp;&nbsp; &nbsp;return prodPrice;
}

public void setProdPrice(Double prodPrice) {
&nbsp;&nbsp; &nbsp;this.prodPrice = prodPrice;
}

public String getProdImage() {
&nbsp;&nbsp; &nbsp;return prodImage;
}

public void setProdImage(String prodImage) {
&nbsp;&nbsp; &nbsp;this.prodImage = prodImage;
}

Using STS you can organize imports automatically from the menu Source -> Organize Imports then you can see the imports after the package name.

package com.djamware.SecurityRest.models;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

You can do the same way as the above step for User and Role class. Here’s the User class looks like.

package com.djamware.SecurityRest.models;

import java.util.Set;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.IndexDirection;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "users")
public class User {

&nbsp;&nbsp; &nbsp;@Id
&nbsp;&nbsp; &nbsp;private String id;
&nbsp;&nbsp; &nbsp;@Indexed(unique = true, direction = IndexDirection.DESCENDING, dropDups = true)
&nbsp;&nbsp; &nbsp;private String email;
&nbsp;&nbsp; &nbsp;private String password;
&nbsp;&nbsp; &nbsp;private String fullname;
&nbsp;&nbsp; &nbsp;private boolean enabled;
&nbsp;&nbsp; &nbsp;@DBRef
&nbsp;&nbsp; &nbsp;private Set<Role> roles;
&nbsp;&nbsp; &nbsp;public String getId() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return id;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setId(String id) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.id = id;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public String getEmail() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return email;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setEmail(String email) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.email = email;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public String getPassword() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return password;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setPassword(String password) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.password = password;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public String getFullname() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return fullname;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setFullname(String fullname) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.fullname = fullname;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public boolean isEnabled() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return enabled;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setEnabled(boolean enabled) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.enabled = enabled;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public Set<Role> getRoles() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return roles;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setRoles(Set<Role> roles) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.roles = roles;
&nbsp;&nbsp; &nbsp;}

}

And the Role class will be like this.

package com.djamware.SecurityRest.models;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.IndexDirection;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "roles")
public class Role {

&nbsp;&nbsp; &nbsp;@Id
&nbsp; &nbsp; private String id;
&nbsp; &nbsp; @Indexed(unique = true, direction = IndexDirection.DESCENDING, dropDups = true)

&nbsp; &nbsp; private String role;
&nbsp;&nbsp; &nbsp;public String getId() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return id;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setId(String id) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.id = id;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public String getRole() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return role;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setRole(String role) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.role = role;
&nbsp;&nbsp; &nbsp;}

}

3. Create Product, User and Role Repository Interfaces

Next steps to create Product, User, and Role Repository Interfaces. From the STS, right-click the project name -> New -> Interface then fill all required fields and checkboxes as below before click Finish button.

Next, open and edit src/main/java/com/djamware/SecurityRest/repositories/ProductRepository.java then add extends to MongoDB CRUD Repository.

public interface ProductRepository extends CrudRepository<Product, String> {

}

Inside the class name add this method.

@Override
void delete(Product deleted);

Organize all required imports.

import org.springframework.data.repository.CrudRepository;
import com.djamware.SecurityRest.models.Product;

The same way can be applied to User and Role repositories. So, the User Repository Interface will look like this.

package com.djamware.SecurityRest.repositories;

import org.springframework.data.mongodb.repository.MongoRepository;
import com.djamware.SecurityRest.models.User;

public interface UserRepository extends MongoRepository<User, String> {

&nbsp;&nbsp; &nbsp;User findByEmail(String email);
}

And the Role Repository Interface will look like this.

package com.djamware.SecurityRest.repositories;

import org.springframework.data.mongodb.repository.MongoRepository;
import com.djamware.SecurityRest.models.Role;

public interface RoleRepository extends MongoRepository<Role, String> {

&nbsp;&nbsp; &nbsp;Role findByRole(String role);
}

4. Create a Custom User Details Service

To implements authentication using existing User and Role from MongoDB, we have to create a custom user details service. From the STS, right-click the project name -> New -> Class File then fill all required fields and checkboxes as below before clicking the finish button.

Next, open and edit src/main/java/com/djamware/SecurityRest/services/CustomUserDetailsService.java then give an annotation above the class name and implement the Spring Security User Details Service.

@Service
public class CustomUserDetailsService implements UserDetailsService {
}

Next, inject all required beans at the first line of the class bracket.

@Autowired
private UserRepository userRepository;

@Autowired
private RoleRepository roleRepository;

@Autowired
private PasswordEncoder bCryptPasswordEncoder;

Add a method to find a user by email field.

public User findUserByEmail(String email) {
&nbsp; &nbsp; return userRepository.findByEmail(email);
}

Add a method to save a new user.

public void saveUser(User user) {
&nbsp; &nbsp; user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
&nbsp; &nbsp; user.setEnabled(true);
&nbsp; &nbsp; Role userRole = roleRepository.findByRole("ADMIN");
&nbsp; &nbsp; user.setRoles(new HashSet<>(Arrays.asList(userRole)));
&nbsp; &nbsp; userRepository.save(user);
}

Override the Spring Security User Details to load User by email.

@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {

&nbsp; &nbsp; User user = userRepository.findByEmail(email);
&nbsp; &nbsp; if(user != null) {
&nbsp; &nbsp; &nbsp; &nbsp; List<GrantedAuthority> authorities = getUserAuthority(user.getRoles());
&nbsp; &nbsp; &nbsp; &nbsp; return buildUserForAuthentication(user, authorities);
&nbsp; &nbsp; } else {
&nbsp; &nbsp; &nbsp; &nbsp; throw new UsernameNotFoundException("username not found");
&nbsp; &nbsp; }
}

Add a method to get a set of Roles that related to a user.

private List<GrantedAuthority> getUserAuthority(Set<Role> userRoles) {
&nbsp; &nbsp; Set<GrantedAuthority> roles = new HashSet<>();
&nbsp; &nbsp; userRoles.forEach((role) -> {
&nbsp; &nbsp; &nbsp; &nbsp; roles.add(new SimpleGrantedAuthority(role.getRole()));
&nbsp; &nbsp; });

&nbsp; &nbsp; List<GrantedAuthority> grantedAuthorities = new ArrayList<>(roles);
&nbsp; &nbsp; return grantedAuthorities;
}

Add a method for authentication purpose.

private UserDetails buildUserForAuthentication(User user, List<GrantedAuthority> authorities) {
&nbsp; &nbsp; return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), authorities);
}

5. Configure Spring Boot Security Rest

Now, the main purpose of this tutorial is configuring Spring Security Rest. First, we have to create a bean for JWT token generation and validation. Right-click the project name -> New -> Class File. Fill the package name as com.djamware.SecurityRest.configs and the Class name as JwtTokenProvider then click the Finish button. Next, open and edit that newly created class file then give it an annotation above the class name.

@Component
public class JwtTokenProvider {
}

Add variables and injected bean inside the class bracket at the top lines.

@Value("${security.jwt.token.secret-key:secret}")
private String secretKey = "secret";

@Value("${security.jwt.token.expire-length:3600000}")
private long validityInMilliseconds = 3600000; // 1h

@Autowired
private CustomUserDetailsService userDetailsService;

Add a method for initialization.

@PostConstruct
protected void init() {
&nbsp; &nbsp; secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
}

Add a method to create a JWT token.

public String createToken(String username, Set<Role> set) {
&nbsp; &nbsp; Claims claims = Jwts.claims().setSubject(username);
&nbsp; &nbsp; claims.put("roles", set);
&nbsp; &nbsp; Date now = new Date();
&nbsp; &nbsp; Date validity = new Date(now.getTime() + validityInMilliseconds);
&nbsp; &nbsp; return Jwts.builder()//
&nbsp; &nbsp; &nbsp; &nbsp; .setClaims(claims)//
&nbsp; &nbsp; &nbsp; &nbsp; .setIssuedAt(now)//
&nbsp; &nbsp; &nbsp; &nbsp; .setExpiration(validity)//
&nbsp; &nbsp; &nbsp; &nbsp; .signWith(SignatureAlgorithm.HS256, secretKey)//
&nbsp; &nbsp; &nbsp; &nbsp; .compact();
}

Add a method to load User by username.

public Authentication getAuthentication(String token) {
&nbsp; &nbsp; UserDetails userDetails = this.userDetailsService.loadUserByUsername(getUsername(token));
&nbsp; &nbsp; return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
}

Add a method to get the username by JWT token.

public String getUsername(String token) {
&nbsp; &nbsp; return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
}

Add a method to resolve JWT token from request headers of Authorization that has a Bearer prefix.

public String resolveToken(HttpServletRequest req) {
&nbsp; &nbsp; String bearerToken = req.getHeader("Authorization");
&nbsp; &nbsp; if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
&nbsp; &nbsp; &nbsp; &nbsp; return bearerToken.substring(7, bearerToken.length());
&nbsp; &nbsp; }
&nbsp; &nbsp; return null;
}

Add a method to validate a JWT token.

public boolean validateToken(String token) {
&nbsp; &nbsp; try {
&nbsp; &nbsp; &nbsp; &nbsp; Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
&nbsp; &nbsp; &nbsp; &nbsp; if (claims.getBody().getExpiration().before(new Date())) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return false;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; return true;
&nbsp; &nbsp; } catch (JwtException | IllegalArgumentException e) {
&nbsp; &nbsp; &nbsp; &nbsp; throw new JwtException("Expired or invalid JWT token");
&nbsp; &nbsp; }
}

Finally, organize imports like below.

package com.djamware.SecurityRest.configs;

import java.util.Base64;
import java.util.Date;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import com.djamware.SecurityRest.models.Role;
import com.djamware.SecurityRest.services.CustomUserDetailsService;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

Next, create a JWT filter class with the name JwtTokenFilter in configs package that extends Spring GenericFilterBean. Replace all Java codes with these lines of codes.

package com.djamware.SecurityRest.configs;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;

public class JwtTokenFilter extends GenericFilterBean {

&nbsp;&nbsp; &nbsp;private JwtTokenProvider jwtTokenProvider;

&nbsp; &nbsp; public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) {
&nbsp; &nbsp; &nbsp; &nbsp; this.jwtTokenProvider = jwtTokenProvider;
&nbsp; &nbsp; }

&nbsp; &nbsp; @Override
&nbsp; &nbsp; public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)
&nbsp; &nbsp; &nbsp; &nbsp; throws IOException, ServletException {
&nbsp; &nbsp; &nbsp; &nbsp; String token = jwtTokenProvider.resolveToken((HttpServletRequest) req);
&nbsp; &nbsp; &nbsp; &nbsp; if (token != null && jwtTokenProvider.validateToken(token)) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Authentication auth = token != null ? jwtTokenProvider.getAuthentication(token) : null;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SecurityContextHolder.getContext().setAuthentication(auth);
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; filterChain.doFilter(req, res);
&nbsp; &nbsp; }
}

Next, create a class with the name JwtConfigurer for JWT configuration in configs package then replace all codes with these lines of codes.

package com.djamware.SecurityRest.configs;

import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

public class JwtConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {

&nbsp;&nbsp; &nbsp;private JwtTokenProvider jwtTokenProvider;

&nbsp; &nbsp; public JwtConfigurer(JwtTokenProvider jwtTokenProvider) {
&nbsp; &nbsp; &nbsp; &nbsp; this.jwtTokenProvider = jwtTokenProvider;
&nbsp; &nbsp; }

&nbsp; &nbsp; @Override
&nbsp; &nbsp; public void configure(HttpSecurity http) throws Exception {
&nbsp; &nbsp; &nbsp; &nbsp; JwtTokenFilter customFilter = new JwtTokenFilter(jwtTokenProvider);
&nbsp; &nbsp; &nbsp; &nbsp; http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
&nbsp; &nbsp; }
}

Finally, we have to configure the Spring Security by creating a Java class file inside configs package with the name WebSecurityConfig. Give annotations to this class and extends Spring WebSecurityConfigurerAdapter.

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
}

Inject JWT token provider inside this class.

@Autowired
JwtTokenProvider jwtTokenProvider;

Add an override method to configure Authentication Manager Builder.

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
&nbsp;&nbsp; &nbsp;UserDetailsService userDetailsService = mongoUserDetails();
&nbsp;&nbsp; &nbsp;auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());

}

Next, add an override method to configure Spring Security Http Security.

@Override
protected void configure(HttpSecurity http) throws Exception {
&nbsp;&nbsp; &nbsp;http.httpBasic().disable().csrf().disable().sessionManagement()
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;.antMatchers("/api/auth/login").permitAll().antMatchers("/api/auth/register").permitAll()
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;.antMatchers("/api/products/**").hasAuthority("ADMIN").anyRequest().authenticated().and().csrf()
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;.disable().exceptionHandling().authenticationEntryPoint(unauthorizedEntryPoint()).and()
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;.apply(new JwtConfigurer(jwtTokenProvider));
}

Next, declare all required beans for this configuration.

@Bean
public PasswordEncoder bCryptPasswordEncoder() {
&nbsp;&nbsp; &nbsp;return new BCryptPasswordEncoder();
}

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
&nbsp;&nbsp; &nbsp;return super.authenticationManagerBean();
}

@Bean
public AuthenticationEntryPoint unauthorizedEntryPoint() {
&nbsp;&nbsp; &nbsp;return (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;"Unauthorized");
}

@Bean
public UserDetailsService mongoUserDetails() {
&nbsp;&nbsp; &nbsp;return new CustomUserDetailsService();
}

6. Create Product and Authentication Controllers

Now it’s time for REST API endpoint. All RESTful API will be created from each controller. Product controller will handle CRUD endpoint of product and Authentication controller will handle login and register endpoint. Right-click project name -> New -> Class then fill the package with com.djamware.SecurityRest.controllers and the class name as ProductController. Open and edit the newly created class file then replace all codes with these lines of codes.

package com.djamware.SecurityRest.controllers;

import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
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.djamware.SecurityRest.models.Product;
import com.djamware.SecurityRest.repositories.ProductRepository;

@RestController
public class ProductController {

&nbsp;&nbsp; &nbsp;@Autowired
&nbsp; &nbsp; ProductRepository productRepository;

&nbsp; &nbsp; @RequestMapping(method=RequestMethod.GET, value="/api/products")
&nbsp; &nbsp; public Iterable<Product> product() {
&nbsp; &nbsp; &nbsp; &nbsp; return productRepository.findAll();
&nbsp; &nbsp; }

&nbsp; &nbsp; @RequestMapping(method=RequestMethod.POST, value="/api/products")
&nbsp; &nbsp; public String save(@RequestBody Product product) {
&nbsp; &nbsp; &nbsp; &nbsp; productRepository.save(product);

&nbsp; &nbsp; &nbsp; &nbsp; return product.getId();
&nbsp; &nbsp; }

&nbsp; &nbsp; @RequestMapping(method=RequestMethod.GET, value="/api/products/{id}")
&nbsp; &nbsp; public Optional<Product> show(@PathVariable String id) {
&nbsp; &nbsp; &nbsp; &nbsp; return productRepository.findById(id);
&nbsp; &nbsp; }

&nbsp; &nbsp; @RequestMapping(method=RequestMethod.PUT, value="/api/products/{id}")
&nbsp; &nbsp; public Product update(@PathVariable String id, @RequestBody Product product) {
&nbsp; &nbsp; &nbsp;&nbsp; &nbsp;Optional<Product> prod = productRepository.findById(id);
&nbsp; &nbsp; &nbsp; &nbsp; if(product.getProdName() != null)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; prod.get().setProdName(product.getProdName());
&nbsp; &nbsp; &nbsp; &nbsp; if(product.getProdDesc() != null)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; prod.get().setProdDesc(product.getProdDesc());
&nbsp; &nbsp; &nbsp; &nbsp; if(product.getProdPrice() != null)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; prod.get().setProdPrice(product.getProdPrice());
&nbsp; &nbsp; &nbsp; &nbsp; if(product.getProdImage() != null)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; prod.get().setProdImage(product.getProdImage());
&nbsp; &nbsp; &nbsp; &nbsp; productRepository.save(prod.get());
&nbsp; &nbsp; &nbsp; &nbsp; return prod.get();
&nbsp; &nbsp; }

&nbsp; &nbsp; @RequestMapping(method=RequestMethod.DELETE, value="/api/products/{id}")
&nbsp; &nbsp; public String delete(@PathVariable String id) {
&nbsp; &nbsp; &nbsp; &nbsp; Optional<Product> product = productRepository.findById(id);
&nbsp; &nbsp; &nbsp; &nbsp; productRepository.delete(product.get());

&nbsp; &nbsp; &nbsp; &nbsp; return "product deleted";
&nbsp; &nbsp; }
}

For login, we need to create a POJO to mapping required fields of User. Create a new class file with the name AuthBody inside controllers package then replace all Java codes with these lines of codes.

package com.djamware.SecurityRest.controllers;

public class AuthBody {

&nbsp;&nbsp; &nbsp;private String email;
&nbsp; &nbsp; private String password;

&nbsp;&nbsp; &nbsp;public String getEmail() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return email;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setEmail(String email) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.email = email;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public String getPassword() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return password;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setPassword(String password) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.password = password;
&nbsp;&nbsp; &nbsp;}

}

Finally, create a controller for authentication with the name AuthController inside the controllers’ package. Open and edit that newly created file then replace all Java codes with these lines of codes.

package com.djamware.SecurityRest.controllers;

import static org.springframework.http.ResponseEntity.ok;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.djamware.SecurityRest.configs.JwtTokenProvider;
import com.djamware.SecurityRest.models.User;
import com.djamware.SecurityRest.repositories.UserRepository;
import com.djamware.SecurityRest.services.CustomUserDetailsService;

@RestController
@RequestMapping("/api/auth")
public class AuthController {

&nbsp;&nbsp; &nbsp;@Autowired
&nbsp;&nbsp; &nbsp;AuthenticationManager authenticationManager;

&nbsp;&nbsp; &nbsp;@Autowired
&nbsp;&nbsp; &nbsp;JwtTokenProvider jwtTokenProvider;

&nbsp;&nbsp; &nbsp;@Autowired
&nbsp;&nbsp; &nbsp;UserRepository users;

&nbsp;&nbsp; &nbsp;@Autowired
&nbsp;&nbsp; &nbsp;private CustomUserDetailsService userService;

&nbsp;&nbsp; &nbsp;@SuppressWarnings("rawtypes")
&nbsp;&nbsp; &nbsp;@PostMapping("/login")
&nbsp;&nbsp; &nbsp;public ResponseEntity login(@RequestBody AuthBody data) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;try {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;String username = data.getEmail();
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, data.getPassword()));
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;String token = jwtTokenProvider.createToken(username, this.users.findByEmail(username).getRoles());
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Map<Object, Object> model = new HashMap<>();
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;model.put("username", username);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;model.put("token", token);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return ok(model);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;} catch (AuthenticationException e) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;throw new BadCredentialsException("Invalid email/password supplied");
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;}

&nbsp;&nbsp; &nbsp;@SuppressWarnings("rawtypes")
&nbsp;&nbsp; &nbsp;@PostMapping("/register")
&nbsp;&nbsp; &nbsp;public ResponseEntity register(@RequestBody User user) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;User userExists = userService.findUserByEmail(user.getEmail());
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;if (userExists != null) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;throw new BadCredentialsException("User with username: " + user.getEmail() + " already exists");
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;userService.saveUser(user);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Map<Object, Object> model = new HashMap<>();
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;model.put("message", "User registered successfully");
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return ok(model);
&nbsp;&nbsp; &nbsp;}
}

7. Run and Test Spring Boot Security Rest using Postman

Before run and test the application, we have to populate a Role data first. Open and edit src/main/java/com/djamware/SecurityRest/SecurityRestApplication.java then add these lines of codes inside the initialization method.

@Bean
CommandLineRunner init(RoleRepository roleRepository) {

&nbsp; &nbsp; return args -> {

&nbsp; &nbsp; &nbsp; &nbsp; Role adminRole = roleRepository.findByRole("ADMIN");
&nbsp; &nbsp; &nbsp; &nbsp; if (adminRole == null) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Role newAdminRole = new Role();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; newAdminRole.setRole("ADMIN");
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; roleRepository.save(newAdminRole);
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; };

}

Next, make sure you have run the MongoDB server on your local machine then run the Gradle application using this command.

./gradlew bootRun

Or in STS just right-click the project name -> Run As -> Spring Boot App. Next, open the Postman application then change the method to GET and address to localhost:8080/api/products then click Send button.

You will see this response in the bottom panel of Postman.

{
&nbsp; &nbsp; "timestamp": "2019-03-07T13:16:34.935+0000",
&nbsp; &nbsp; "status": 401,
&nbsp; &nbsp; "error": "Unauthorized",
&nbsp; &nbsp; "message": "Unauthorized",
&nbsp; &nbsp; "path": "/api/products"
}

Next, change the method to POST then address to localhost:8080/api/auth/register then fill the body with raw data as below image then click Send button.

You will get the response in the bottom panel of Postman.

{
&nbsp; &nbsp; "message": "User registered successfully"
}

Next, change the address to localhost:8080/api/auth/login and change the body as below then click Send button.

{ "email":"[email&nbsp;protected]", "password": "q1w2we3r4" }

You will see this response in the bottom panel of Postman.

{
&nbsp; &nbsp; "username": "[email&nbsp;protected]",
&nbsp; &nbsp; "token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJpbmZvQGRqYW13YXJlLmNvbSIsInJvbGVzIjpbeyJpZCI6IjVjODBjNjIzYjIwMTkxNGIyYTY5N2U4ZCIsInJvbGUiOiJBRE1JTiJ9XSwiaWF0IjoxNTUxOTY0OTc3LCJleHAiOjE1NTE5Njg1Nzd9.j5CHZ_LCmeQtdxQeH9eluxVXcOsHPWV1p8WnBn0CULo"
}

Copy the token then back to the GET product. Add a header with the name Authorization and the value that paste from a token that gets by login with additional Bearer prefix (with space) as below.

Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJpbmZvQGRqYW13YXJlLmNvbSIsInJvbGVzIjpbeyJpZCI6IjVjODBjNjIzYjIwMTkxNGIyYTY5N2U4ZCIsInJvbGUiOiJBRE1JTiJ9XSwiaWF0IjoxNTUxOTY0OTc3LCJleHAiOjE1NTE5Njg1Nzd9.j5CHZ_LCmeQtdxQeH9eluxVXcOsHPWV1p8WnBn0CULo

You should see this response after clicking the Send button.

[
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; "id": "5c80dc6cb20191520567b68a",
&nbsp; &nbsp; &nbsp; &nbsp; "prodName": "Dummy Product 1",
&nbsp; &nbsp; &nbsp; &nbsp; "prodDesc": "The Fresh Dummy Product in The world part 1",
&nbsp; &nbsp; &nbsp; &nbsp; "prodPrice": 100,
&nbsp; &nbsp; &nbsp; &nbsp; "prodImage": "https://dummyimage.com/600x400/000/fff"
&nbsp; &nbsp; }
]

You can test the POST product with the token in headers using the same way.

That it’s, the Securing RESTful API with Spring Boot, Security, and Data MongoDB tutorial. You can get the full source code from our GitHub.

Learn More

Build a Simple CRUD App with Spring Boot and Vue.js

Creating RESTful APIs with NodeJS and MongoDB Tutorial

MongoDB with Python Crash Course - Tutorial for Beginners

How to build RESTful APIs with ASP.NET Core

Understanding the basics of RESTful APIs

Developing RESTful APIs with Lumen (A PHP Micro-framework)

Java Programming Masterclass for Software Developers

Java In-Depth: Become a Complete Java Engineer!

JSP, Servlets and JDBC for Beginners: Build a Database App

JSP, Servlet, JSLT + Hibernate: A complete guide