JUnit5 Important Annotations

Junit 5 comes with some new annotations that were not available in previous releases. Understanding these Annotations will help in writing efficient tests. This article covers some of the most commonly used annotations. In the next section, we will example of those annotations.
The following are some of the important annotations in JUnit 5.

@Test

@Test : Denotes that a method is a test method. Without this annotation, JUnit will ignore the method

@ParameterizedTest

Denotes a method to be a parameterized test method. Parameterized tests make it possible to run a test multiple times with different arguments.

@RepeatedTest

@RepeatedTest : Test methods marked with @RepeatedTest can be executed repeatedly

@TestMethodOrder

@TestMethodOrder: Helps maintain order in which test methods are executed.

@DisplayName

@DisplayName : Helps in using a custom name to be printed either in the console or test report for the test method.

@DisplayNameGeneration

@DisplayNameGeneration : Declares a custom display name generator for the test class

@BeforeEach

@BeforeEach: Analogous to JUnit 4 @Before. Denotes the annotated method should be executed before each test. The method should not return any value, should not be private.

@AfterEach

@AfterEach : Analogous to JUnit 4 @After. Denotes the annotated method should be executed after each test. The method should not return any value, should not be private.

@BeforeAll

@BeforeAll: Denotes that the annotated method should be executed before all @Test, @RepeatedTest, @ParameterizedTest, and @TestFactory methods in the current class; analogous to JUnit 4’s @BeforeClass. Such methods are inherited (unless they are hidden or overridden) and must be static (unless the "per-class" test instance lifecycle is used).

@AfterAll

@AfterAll: Denotes that the annotated method should be executed after all @Test, @RepeatedTest, @ParameterizedTest, and @TestFactory methods in the current class; analogous to JUnit 4’s @AfterClass. A method marked with @AfterAll is inherited (unless they are hidden or overridden) and must be static.

@Nested

@Nested:Denotes that the annotated class is a non-static nested test class. @BeforeAll and @AfterAll methods cannot be used directly in a @Nested test class unless the "per-class" test instance lifecycle is used. Such annotations are not inherited.

@Tag

@Tag: Used to declare tags for filtering tests, either at the class or method level; analogous to test groups in TestNG or Categories in JUnit 4. Such annotations are inherited at the class level but not at the method level.

@Disabled

@Disabled : Used to disable a test class or test method; analogous to JUnit 4’s @Ignore. Such annotations are not inherited.

@Timeout

@Timeout: Used to fail a test, test factory, test template, or lifecycle method if its execution exceeds a given duration. Such annotations are inherited.

Next

JUnit 5 Test Suites

The test suite is a container that has a set of tests that can be executed together. For instance, say we have multiple tests and want to execute all of them or subset of them under certain conditions. Like we want to run all the tests in the integration stage, a subset of tests in the developer environment before pushing code. All these can be done using Test Suite. Using JUnit 5 test suites, we can run tests spread into multiple test classes and different packages. JUnit 5 provides two annotations: @SelectPackages and @SelectClasses to create test suites. Additionally, you can use other annotations for filtering test packages, classes, or even test methods.

Filtering Tests in Suites

Tests in JUnit5 can be executed with @RunWith. The same annotation is also used for Junit 4. The only difference Is in Junit 5 we pass JUnitPlatform as a parameter. In this article we will go though some sample use cases of creating TestSuite in JUnit 5 and how to filter test classes, test methods in the test suite etc.

@SelectPackages

With this annotation, we can tell the Test Suite to scan all the mention packages and its sub-packages and execute all the Test Classes. We can also specify a signal package or multiple packages. Following example demonstrates this.
Select a single package
With @SelectPackages we can select test classes only from a specified package or list of packages. In the following example, all the Test classes from package com.bootng will be executed.
import org.junit.platform.runner.JUnitPlatform;
import org.junit.platform.suite.api.SelectPackages;
import org.junit.runner.RunWith;

@RunWith(JUnitPlatform.class)
@SelectPackages({ "com.bootng" })
public class TestSuiteWithPackageSingle {

}
Select multiple packages
Similarly, if we want to support multiple packages then we can specify multiple package names. The following example will execute tests from the two packages.
@RunWith(JUnitPlatform.class)
@SelectPackages({ "com.bootng.assertions", "com.bootng.display" })
public class TestSuiteWithPackageMultiple {

}

@SelectClasses

Select Two Classes
In this example rather than selecting packages, we can specify individual test classes that we want to include in the Test Suite. In the following example essentially we will have only two Test classes in the Test Suite.
@RunWith(JUnitPlatform.class)
@SelectClasses({ DisplayExampleTest.class, DisplayGeneratorExampleTest.class })
public class TestSuiteWithSelectClass {

}

@IncludePackages and @ExcludePackages

@IncludePackages and @ExcludePackages work only at the specified package level and it won't scan sub-packages. This annotation is used with @SelectPackages annotation to fine-tune the filtering. Son in the following example Test Classes from only com.bootng.assertions will be included.
IncludePackages Example
Here
@RunWith(JUnitPlatform.class)
@SelectPackages({ "com.bootng" })
@IncludePackages({ "com.bootng.assertions" })
public class TestSuite_IncludePackage {

}
ExcludePackages Example
Includes all the test form package com.bootng, but excludes tests from sub package com.bootng.assertions.
@RunWith(JUnitPlatform.class)
@SelectPackages({ "com.bootng" })
@IncludePackages({ "com.bootng.assertions" })
public class TestSuite_IncludePackage {

}

@IncludeClassNamePatterns and @ExcludeClassNamePatterns

Rather than filtering the test class from packages, we can also filter them based on the name of the Test Classes. To specify test class names patterns to exclude or include, you can use @IncludeClassNamePatterns and @ExcludeClassNamePatterns annotations.
IncludeClassNamePatterns Example
In this example, we can including only the test whose name ends with "Test" from the package (and sub packages) of com.bootng
@RunWith(JUnitPlatform.class)
@SelectPackages({ "com.bootng" })
@IncludeClassNamePatterns("^.*Test*$")
public class TestSuite_NamePattern {

}

@IncludeTags and @ExcludeTags

IncludeTags Example
With IncludeTags and Exclude Tags we can filter Classes or Methods that are Tagged with the specified tag. For Instance, the following example, we are scanning package com.bootng.assertions and then only interested in including Test Classes or Test Methods tagged with Weather.
@RunWith(JUnitPlatform.class)
@SelectPackages({ "com.bootng.assertions" })
@IncludeTags({ "Weather" })
public class TestSuite_TagInclude {

}
ExcludeTags Example
Similar to IncludeTags tag to filter Test Methods or Test Classes but for exclusion. The following example will exclude all the Tests tagged with "Weather"
@RunWith(JUnitPlatform.class)
@SelectPackages({ "com.bootng.assertions" })
@ExcludeTags({ "Weather" })
public class TestSuite_TagExclude {

}

Summary

JUnit 5 provides powerful ways to filter Test cases in Test Suites. We have several options starting from including/excluding a specific packages to include/exclude specific Classes to Include/Exclude specific methods.

Test Classes and Methods Requirement

JUnit 5 is the latest version of Junit. It has a brand new architecture and more capabilities compared to the previous generation of Junit. In this article, we will quickly go through some of the main features and concepts of JUnit 5. In this article we go over the high-level structural requirement for writing Unit Tests. Generally, we write test method to represent a test case or a certain scenario which we want to validate. Such test methods would reside inside a Class. With Junit5 we don't need to mark or annotate the main class with any special JUnit specific annotations. Junit5 will be able to

Test Class

Any top-level class, static member class, or @Nested class that contains at least one test method. We don't need to annotate the Class, JUnit5 will be able to find test methods defined inside it.
Class Requirement
The Class should contain only one Constructor and the Class should not be an abstract Class.

Test Method

Any not abstract instance method that is annotated with @Test, @RepeatedTest, @ParameterizedTest, @TestFactory, or @TestTemplate.
Test Method
The test method should not return any value and should not be abstract.

Lifecycle Method

Any method that is directly annotated or meta-annotated with @BeforeAll, @AfterAll, @BeforeEach, or @AfterEach Lifecycle methods can be inherited from parent class. Lifecycle methods should not be private and should not return any value.
Lifecycle Method
Should not be private and should not return value. @BeforeAll and @AfterAll should be used on a static method

Example

Following Example shows a Test Class with one test method annotated with @Test and four Lifecylcle emthods
SimpleHelloTest3
package com.bootng.start;

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;

public class SimpleHelloTest3 {

	@BeforeAll
	public static void method1() {
		System.out.println("method 1");
	}
	
	@BeforeEach
	public void method2() {
		System.out.println("method 2");
	}
	
	@AfterAll
	public static void method3() {
		System.out.println("method 3");
	}
	
	@AfterEach
	public void method4() {
		System.out.println("method 4");
	}
	
	@Test
	public void test_hello() {
		String msg = "hello";
		assertEquals(msg, "hello", " message is equal Hello");
		System.out.println("");
	}

}
Running the above test would output the following. method1 and method2 would be executed before the actual test and then method4 and method3 would be executed.
Console Output
method 1 method 2 test_hello method 4 method 3

JUnit5 Writing first Test

JUnit 5 is the latest version of Junit. It has a brand new architecture and more capabilities compared to the previous generation of Junit. In this article, we will quickly go through some of the main features and concepts of JUnit 5. In this Article we will start with our first JUnit 5 Test.
SimpleHelloTest
First Junit5 Test. This test has only one test method which asserts that the message string is equal to "hello"
package com.bootng.start;

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class SimpleHelloTest {

	@Test
	public void test_hello() {
		String msg = "hello";
		assertEquals(msg, "hello", " message is equal Hello");

	}

}
When running this test, the test will surely pass since the assert condition will be successfull.

Introduction to JUnit5

August 21, 2020

JUnit 5 Introduction

 
JUnit 5 is the latest version of JUnit. It has a brand new architecture and more capabilities compared to the previous generation of JUnit. In this article, we will quickly go through some of the main features and concepts of JUnit 5 through hands on examples.

JUnit 5 Project

JUnit 5 is composed of mainly three sub-projects JUnit Platform, JUnit Jupiter, and JUnit Vintage. junit-jupiter-api (JUnit Platform): JUnit Jupiter API for writing tests and extensions.
junit-jupiter-engine(JUnit Jupiter) : JUnit Jupiter test engine implementation, only required atruntime.
junit-vintage-engine:(JUnit Vintage): To run tests wittern in older version of JUnit on JUnit 5 Platform.

This article is focused on JUnit5 with maven as a build tool. Other than the maven related configurations information in this articles holds true for other build environments also.
Java 8
JUnit 5 requires a minimum Java 8 version

JUnit 5 Artifacts

If you’re using Maven you can add the corresponding subprojects in your pom file (juinit-jupiter-api, junit-jupiter-engine, junit-vintage-engine). Starting with Juinit 5.4 we can also simply add aggregate artifact named junit-jupiter in our pom.xml to include all the JUnit 5 related dependencies to our project.
JUnit 5 Maven Dependencies
<dependency>
 <groupId>org.junit.jupiter</groupId>
 <artifactId>junit-jupiter</artifactId>
 <version>5.7.0-M1</version>
 <scope>test</scope>
</dependency>

Main Features of JUnit 5

Following are the main features in JUnit 5
Supports Lambda expressions.
Nested Unit Tests
Parameterized Unit Tests
Conditional Test inclusion or exclusion
Extension Model

Main Annotations

Tests written in JUnit5 follow some standard approach and it is done by utilizing the capabilities provided by the JUnit 5 framework. The following are some of the annotations provided by JUnit 5. This is just to provide some quick pointers to the rich feature sets of JUnit 5.
Common annotations
@Test – denotes a test method, it does not take any arguments. 
@DisplayName – specifies a custom name for the test class or method. 
@DisplayNameGeneration - specify a generator to generate a custom name for method or class. 
@Nested - it marks a class a nested test 
@Disabled – prevents a test class or method from executing; 
@BeforeEach, 
@AfterEach – runs the annotated method before or after each test method in the same class. 
@BeforeAll, 
@AfterAll – runs the annotated method before any or after all of the test methods in the class.

Next: Writing first Test in JUnit 5

References

Assert in Java Code

An assertion is a java statement. An assertion statement is used in the code to ensures the correctness of any assumptions on which the program depends.  When the java runtime executes the assert statement it will throw assertion exception. Assertion helps in maintaining the invariants in a program under check. We will explore how to use the assertion statement with an example.

Syntax for assertion

The following are the two different syntaxes for using assertion.
First way
If the expression is false the assert statement will throw java.lang.AssertionError
assert expression;
Second way
With two expressions where expression1 must be a boolean expression. and expression2 must return a value (must not return void). if exprerssion1 is false, the assert statement will throw java.lang.AssertError with expression2 as the message
assert expression1 : expression2;
By default assertion is disabled we can enable it by setting the jvm parameter ea or enableassertions.

Enabling assertion

By default, assertions are disabled in Java. If assertions are disabled, assertion statements do not have any effects. In order to enable them, we use the following command
java -ea JavaAssertionTest
(or)
java -enableassertions JavaAssertionTest

Example

In the following example, we have a Movie object containing movie name and minAge. From the main class we create a movie object and call the play method only if the minimum age requirement is met.
Movie.java
class Movie {
	private String title;
	private int minAge;

	public Movie(String title, int age) {
		this.title = title;
		this.minAge = age;
	}

	public void play() {
		System.out.print("Playing movie:  " + title);
	}

	public String getTitle() {
		return title;
	}

	public int getAgeRestriction() {
		return minAge;
	}
}
JavaAssertionTest
public class JavaAssertionTest {
	public static void main(String[] args) {
		Movie movieA = new Movie("movie 007 James bond", 11);
		Movie movieB = new Movie("movie Transformers", 18);

		//We want to play only movies suitable for age 11 or under
		assert (movieA.getAgeRestriction() <= 18);
		movieA.play();
		
		assert (movieB.getAgeRestriction() <= 11): "Underage for movie" + movieB.getTitle();
		movieB.play();
		
		System.out.println("played with all movies");
	}
}
Running the above example will result in the following output.
Console Output
Playing movie movie 007 James bond Exception in thread "main" java.lang.AssertionError: Underage for moviemovie Transformers at JavaAssertionTest.main(JavaAssertionTest.java:12)
Note in the above case the moment the assertion error is thrown, the program will exit.

Handling AssertionErrror

If an assertion error occurs the program will throw exception and exit. If we want to handle the assertion error, we can put the assert statement within a try-catch block. And in the catch block, we can have our logic if assert statement. The following example shows the same.
With Try Catch block
public static void main(String[] args) {
		Movie movieA = new Movie("movie 007 James bond", 11);
		Movie movieB = new Movie("movie Transformers", 18);
		
		System.out.println("Start playing movies");
		
		//We want to play only movies suitable for age 11 or under
		assert (movieA.getAgeRestriction() <= 18);
		movieA.play();
		
		try {
			assert (movieB.getAgeRestriction() <= 11): "Underage for movie with title:: " + movieB.getTitle();
			movieB.play();
		}catch (AssertionError e) {
			System.out.println("Oops this movie cannot be played because " + e.getMessage());
		}
		
		System.out.println("played with all movies");
	}
Console output
Start playing movies Playing movie: movie 007 James bond Oops this movie cannot be played because Underage for movie with title:: movie Transformers played with all movies
Summary
  • assertions are a great way to confirm program invariants hold true.
  • we have two different syntaxes for writing assertion in java.
  • by default assertions are turned off, we can turn on using JVM flag.
  • if an assert statement fails, the program will throw AssertionError and exit.
  • we can catch AssertionError, and still get hold control of the program.

Consuming RESTful API in Spring Boot

In our earlier articles, we learned how to create rest full API's . In this article, we will learn how to invoke or call Rest full API's in spring boot. Essentially we are going to write a simple client to consume a few public RESTful API's. RESTTemplate is for just that, Spring's RESTTemplate is used to write client applications to consume RESTful API
In this article, we will consume two different public API. For that, we will write a standalone Spring Boot App, to consumes the REST API's as follows
API 1: Get all GitHub API's
GET https://api.github.com
API 2: Get all Github repositories for user bootng
GET https://api.github.com/users/bootng/repos

Technologies Used

  • Java 11
  • Apache Maven 3.5.0
  • Spring Boot 2.2.6
  • Eclipse IDE
Main Application Class
package com.javaexp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@SpringBootApplication
public class ResttemplateApplication {
  private static final Logger log = LoggerFactory.getLogger(ResttemplateApplication.class);
  public static void main(String args[]) {
    log.info("about to call ResttemplateApplication.run()");
    SpringApplication.run(ResttemplateApplication.class, args);
    log.info("completed executing ResttemplateApplication.run()");
  }
}
Our first REST client is as bellow, which calls a REST endpoint and then displays results in the console.
LoadAllGithubEndpoints.java
package com.bootng;

import java.util.Iterator;
import java.util.Map.Entry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.core.annotation.Order;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.JsonNode;

/**
 * This Class List all the endpoints from URL https://api.github.com
 *
 */
@Component
@Order(1)
public class LoadAllGithubEndpoints implements CommandLineRunner {
  private static final Logger log = LoggerFactory.getLogger(ResttemplateApplication.class);
  @Override
  public void run(String... args) throws Exception {
    log.info("about to call LoadAllEndpoint.run()");
    RestTemplate restTemplate = new RestTemplateBuilder().build();
    ResponseEntity<JsonNode> apis =
        restTemplate.getForEntity("https://api.github.com", JsonNode.class);
    StringBuilder result = new StringBuilder("\n List of Public API's");
    apis.getBody().fields().next().getValue();
    Iterator<Entry<String, JsonNode>> it = apis.getBody().fields();
    while (it.hasNext()) {
      result.append("\n").append(it.next().getValue().asText());
    }
    log.info(result.toString());
  }
}
Our second REST client is as bellow, which calls a REST endpoint and then displays all the repositories for user bootng in the console.
LoadGithubRepo
package com.bootng;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.core.annotation.Order;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.JsonNode;

@Component
@Order(2)
public class LoadGithubRepo implements CommandLineRunner {
  private static final Logger log = LoggerFactory.getLogger(ResttemplateApplication.class);
  @Override
  public void run(String... args) throws Exception {
    log.info("about to call LoadGithubRepo.run()");
    RestTemplate restTemplate = new RestTemplateBuilder().build();
    ResponseEntity<JsonNode> repos = restTemplate
        .getForEntity("https://api.github.com/users/bootng/repos", JsonNode.class);
    int counter = 1;
    StringBuilder result = new StringBuilder("\n List of Repositories");
    if (repos.getBody().isArray()) {
      for(JsonNode jsonNode : repos.getBody()) {
        result.append("\n Repo ").append(counter++).append("::");
        result.append(jsonNode.get("name").asText());
      }
    }
    log.info(result.toString());
  }
}
Notice that LoadGithubRepo is marked with annotation Order(2) and LoadAllGithubEndpoints is marked with annotation Order(1). That means Spring will execute LoadAllGithubEndpoints first and then LoadGithubRepo.
Building and Running the application
mvn clean install
mvn spring-boot:run
Console Output
20-June-18 16:25:17:627  INFO main c.b.ResttemplateApplication:27 - about to call LoadAllEndpoint.run()
20-June-18 16:25:18:570  INFO main c.b.ResttemplateApplication:40 -
List of Public API's
https://api.github.com/user
https://github.com/settings/connections/applications{/client_id}
https://api.github.com/authorizations
https://api.github.com/search/code?q={query}{&page,per_page,sort,order}
https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}
https://api.github.com/user/emails
https://api.github.com/emojis
https://api.github.com/events
https://api.github.com/feeds
https://api.github.com/user/followers
https://api.github.com/user/following{/target}
https://api.github.com/gists{/gist_id}
https://api.github.com/hub
https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}
https://api.github.com/issues
https://api.github.com/user/keys
https://api.github.com/search/labels?q={query}&repository_id={repository_id}{&page,per_page}
https://api.github.com/notifications
https://api.github.com/orgs/{org}
https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}
https://api.github.com/orgs/{org}/teams
https://api.github.com/gists/public
https://api.github.com/rate_limit
https://api.github.com/repos/{owner}/{repo}
https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}
https://api.github.com/user/repos{?type,page,per_page,sort}
https://api.github.com/user/starred{/owner}{/repo}
https://api.github.com/gists/starred
https://api.github.com/users/{user}
https://api.github.com/user/orgs
https://api.github.com/users/{user}/repos{?type,page,per_page,sort}
https://api.github.com/search/users?q={query}{&page,per_page,sort,order}
20-June-18 16:25:18:570  INFO main c.b.ResttemplateApplication:21 - about to call LoadGithubRepo.run()
20-June-18 16:25:19:069  INFO main c.b.ResttemplateApplication:36 -
 List of Repositories
 Repo 1::angular-word-merger
 Repo 2::hansini-static-deploy
 Repo 3::JUnit5-examples
 Repo 4::okhttp
 Repo 5::spring-boot-web-start
 Repo 6::springboot_docker
20-June-18 16:25:19:071  INFO main c.b.ResttemplateApplication:57 - Started ResttemplateApplication in 3.382 seconds (JVM running for 6.625)
20-June-18 16:25:19:071  INFO main c.b.ResttemplateApplication:17 - completed executing ResttemplateApplication.run()
Get source code and build
  • git clone https://github.com/siddharthagit/spring-boot-resttemplate
  • cd spring-boot-resttemplate
  • mvn clean install
  • mvn spring-boot:run
    Summary
    • In this article, we saw how to write a simple REST client using RESTTEmplate in Spring Boot.
    • In our next article, we will see more use cases of using RESTTemplate like for POST/PUT, etc.

    References

    ControllerAdvice

    ControllerAdvice is an annotation provided by Spring. It is used to manage Exception handling in a Spring application. It allows us to handle any exception from any Classes in the Application and not just a single Class or one method. This article explains the advantages of using ControllerAdvice and some important points to be considered while using it.
    In this article, we will walk through a few examples to demonstrate the functionality of ControllerAdvice annotation. To start will we will use the Spring Boot Rest application. This application has many endpoints specifically to drive a blog backed which can serve APIs for CRUD operations on Blog, Tags, Categories, etc.
    Let's examine the following endpoints from the application which allows us to retrieve a specific Blog or Comments by its id.
    Example REST API
    GET /blogapi/blogs/:Blog_ID
    GET /blogapi/comments/:Comments_ID
    GET /blogapi/categories/:CAT_ID
    All these endpoints return the respective Objects if they exist with the specified ID or throws NotFoundException. If we think about any other Resource that has a GET endpoint will have a similar use case, that is throwing NotFoundException if Object is not found with the specified id.
    So basically for any Resource Controller will need to deal with common exceptions that can occur while invoking the API. ControllerAdvice helps in handling all these exceptions from all the controllers globally in a separate class.
    We will see the implementation of a Blog controller without Controlleradvice and with Controlleradvice
    Let's see the Controller for the GET /blogs/id endpoint without Controlleradvice.

    Example 1

    Controller for Blog
    This controller method implements the GET blog by id. The controller uses BlogService to get the BlogStory object with the specified id. If the service can't find any blog with the specified id, it throws NotFoundException. In the controller, we catch that exception and return ResponseEntity with HTTP Status Not Found. The service can also throw AppExceptions which is also needed to be handled at the controller.
    @RequestMapping(value = {"/v1/blogs/{id}"}, method = RequestMethod.GET,
          produces = MediaType.APPLICATION_JSON_VALUE)
      public ResponseEntity getBlogStory(@PathVariable(value = "") String id) {
        ResponseEntity apiResponse;
          BlogStory blogStory;
          try {
            blogStory = blogService.getBlogStory(id);
            apiResponse = new ResponseEntity(blogStory, HttpStatus.OK);
          } catch (NotFoundException e) {
            //Handle NotFoundException
            apiResponse = new ResponseEntity(HttpStatus.NOT_FOUND);
            e.printStackTrace();
          }
          catch (AppException e) {
            //Handle AppException
            apiResponse = new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
            log.error(e.getMessage());
          }
        return apiResponse;
      }
    Nothing is wrong with this controller but, we are doing the same kind of stuff probably in all the controller methods, that are handling NotFoundException or AppException. For instance think about the implementations of other endpoints GET /categories/id and GET /comments/id.
    That's where ControllerAdvice comes into the picture. We will create a Global Exception handing class that can handle the specific type of Exceptions for example in our case it is NotFoundException. We mark the class with @ControllerAdvice so that Spring can apply the exception handling implemented in this class globally to all the controllers in this application.

    Example 2

    GlobalExceptionHandler
    package com.bootng.handler.exception;
    
    import java.util.Collections;
    import java.util.List;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.HttpMediaTypeNotSupportedException;
    import org.springframework.web.HttpRequestMethodNotSupportedException;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.context.request.WebRequest;
    import com.bootng.beans.AppException;
    
    @ControllerAdvice
    public class GlobalExceptionHandler {
    
      private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    /** Customize the response for NotFoundException. */
      @ExceptionHandler({NotFoundException.class})
      protected ResponseEntity<Object> handleNotFoundException(Exception ex, WebRequest request) {
        log.info("global exception handler " + ex.getMessage());
        List<String> errors = Collections.singletonList(ex.getMessage());
        ResponseEntity<Object> ret = ResponseEntity.status(HttpStatus.NOT_FOUND).body(errors);
        return ret;
      }
      
      /** Customize the response for AppException. */
      @ExceptionHandler({AppException.class})
      protected ResponseEntity<Object> handleException(Exception ex, WebRequest request) {
        log.info("global exception handler " + ex.getMessage());
        String errors = "We cannot serve the request now, please try later";
        ResponseEntity<Object> ret = ResponseEntity.status(HttpStatus.FORBIDDEN).body(errors);
        return ret;
      }
    }
    
    In this ControllerAdvice implementations, we are handing two exceptions, NotFoundException and AppException. If you see the source code in git, this ControllerAdvice has actually more methods to handle more Exceptions, but here we are just showing handling just these two Exceptions.
    Now let us write the same controller taking advantage of the Global Exception Handler class we defined above. Now we don't have to catch the exception in our controller, we can simply work with the core business logic and delegate the exception handling to the Global Exception Handler Class.
    Get Blog By ID v2
    @RequestMapping(value = {"/v2/blogs/{id}"}, method = RequestMethod.GET,
          produces = MediaType.APPLICATION_JSON_VALUE)
      public ResponseEntity<BlogStory> getBlogStoryV2(@PathVariable(value = "") String id)
          throws AppException, NotFoundException {
        BlogStory blogStory = blogService.getBlogStory(id);
        return new ResponseEntity<BlogStory>(blogStory, HttpStatus.OK);
      }
    For the end-user both the controller works exactly the same. But the implementation is different with regards to handling exceptions. If we have a ControllerAdvice and any of the controllers throws exception handled by ControllerAdvice then, Spring will invoke the respective method from the ControllerAdvice to handle the exception


    Summary
    • The @ControllerAdvice annotation was first introduced in Spring 3.2
    • We can have a single or multiple controller advice class per application. Having a single controller advice class is recommended per application.
    • ControllerAdvice should contain methods to handle specific errors in the application.
    • Using ControllerAdvice results in more clean and maintainable codes.
    • We can handle both custom Exceptions as well as Spring Exceptions like HttpMediaTypeNotSupportedException, HttpRequestMethodNotSupportedException etc.
    Git Source Code
    • git clone https://github.com/siddharthagit/spring-boot-references
    • cd spring-rest
    • mvn clean install
    • mvn spring-boot:run

      Using ObjectMapper with Spring Boot Controller

      If we are writing Spring Controller and want to get access to the request payload and convert it to the domain model. Then we have different ways of handling the request payload. For instance, we can use @RequestBody to convert the incoming payload to a domain object, or to a String, etc. In this article, we will see how we can use ObjectMapper to process incoming payload in s Spring Controller.
      In this article, we will show a controller that accepts a JSON payload and then uses ObjectMapper to create instance of a domain object and then pass it to the service layer to persist it.

      Example

      BlogController with addBlogStory method.
      Here we are reading the JSON input and converting it to a BlogStory Object. We are using ObjectMapper to convert the input JSON to a JsonNode object first. And then we are reading individual attributes from the JsonNode object and setting the corresponding field in our domain object.
      
      @RestController
      @RequestMapping("/blogapi")
      public class BlogAPIController {
        private static final Logger log = LoggerFactory.getLogger(BlogAPIController.class);
        @Autowired
        BlogService blogService;
      
        @RequestMapping(value = {"/blogs"}, method = RequestMethod.POST,
            produces = MediaType.APPLICATION_JSON_VALUE)
        public Object addBlogStory(@RequestBody String input) {
          log.info("inside blog POST method" + input);
          ObjectMapper mapper = new ObjectMapper();
          BlogStory newStory = new BlogStory();
          try {
            JsonNode jsonNode = mapper.readTree(input);
            if (jsonNode.get("id") != null) {
              newStory.setId(jsonNode.get("id").asText());
            }
            if (jsonNode.get("name") != null) {
              newStory.setName(jsonNode.get("name").asText());
            }
            if (jsonNode.get("category") != null) {
              newStory.setCategory(jsonNode.get("category").asText());
            }
            else {
              newStory.setCategory("Finance");
            }
            if (jsonNode.get("summary") != null) {
              newStory.setSummary(jsonNode.get("summary").asText());
            }
            if (jsonNode.get("description") != null) {
              newStory.setDescription(jsonNode.get("description").asText());
            }
      
            blogService.addStory(newStory);
          } catch (Exception e) {
            log.error(e.getMessage());
          }
      
          ResponseEntity apiResponse = new ResponseEntity(newStory, HttpStatus.OK);
          return apiResponse;
        }
      
      }
      
      Notice that we have access to the JsonNode object, which we got from the mapper after reading the JSON payload. Now we can set custom logic. For instance if the category is null in the request payload we are setting default category "Finance"
      Below is a sample JSON payload which will be acceptable by the above controller.
      JSON Payload
      {
          "id": "Java_15",
          "name": "Java 15",
          "summary": "Java 15 Blog Summary",
          "description": "Java 15 BlogLorem ipsum dolor sit amet, consectetur adipiscing elit",
          "category": "Technical"
      }
      Invoking the API
      curl -X POST \
        http://localhost:8080/blogapi/blogs \
        -H 'Content-Type: application/json' \
        -d '{
          "id": "Java_15",
          "name": "Java 15",
          "summary": "Java 15 Blog Summary",
          "description": "Java 15 BlogLorem ipsum dolor sit amet, consectetur adipiscing elit",
          "category": "Technical"
      }'
      Please find the repository link and build instruction as follows.
      Git Source Code
      • git clone https://github.com/siddharthagit/spring-boot-references
      • cd spring-rest
      • mvn clean install
      • mvn spring-boot:run

        Summary

        Using ObjectMapper to serialize JSON and then convert to a domain object gives more control. We then have access to the full JSON object and as we like we can populate the domain object after applying some transformation logic etc.

        References

        RequestBody and ResponseBody

        Spring RequestBody and ResponseBody annotations are used in Spring controllers, where we want to bind web requests to method parameters (RequestBody) or method return value (ResponseBody) to Web response. In this article, we will cover some quick examples of using both these annotations. Although Spring supports different styles of writing controller and accessing request, response object, using RequestBody and ResponseBody helps writing code quickly as all the parameters are already available in the controller, and Spring takes care of serialization and deserialization. Although using ResponseBody is not required if we use @RestController annotation. More about @Restcontroller can be found here @RestController

        Serialization

        Data sent over HTTP to server resources (Like a REST Controller) needs to be converted from serialized version to Objects. We can have different types of REST controllers based on the payload they support. For instance, we can design the REST controller to accept/send XML, JSON, TEXT, or HTML payloads/response. The Controller then should be able to read those data acts on it and return the response. Spring uses HTTP Message converters to convert the HTTP request body into domain object [deserialize request body to domain object], and to convert Domain object back to HTTP response body while returning the response. Spring provides a number of MessageConvertes like bellow, StringHttpMessageConverter: Read Write String
        FormHttpMessageConverter: Read Write HTTP Form Data
        MappingJackson2HttpMessageConverter: Read Write JSON
        MappingJackson2XmlHttpMessageConverter: Read Write XML etc.
        @RequestBody and @ResponseBody body annotation behind the scene uses these Message converter to serialize or deserialize the data. Following section, we will see each example of these two annotations.

        @RequestBody annotations

        Typically in each controller, we can have multiple methods. Each method is tied to a specific HTTP request path via @RequestMapping. That's how Spring knows for an incoming request which method needs to be invoked. If a method parameter is annotated with @RequestBody, Spring will bind the incoming HTTP request body (payload) to the parameter of the respective method tied to the request path. While doing that, Spring will [behind the scenes] use HTTP Message converters to convert the HTTP request body into domain object [deserialize request body to domain object], based on Accept header present in the request.

        @ResponseBody Annotation

        Similar to the @RequestBody annotation the @ResponseBody annotation is used to convert the return type of the method to the HTTP response. Here also Spring will use Message Converter to convert the return type of the method to the HTTP response body, set the HTTP headers, and HTTP status.
        Let's see an example, where we bind a JSON payload to a @RequestBody method parameter. Following the example, we will create a REST endpoint that can take a JSON payload to create a blog category object and then return the response back to the client.
        Our BlogCategory Model
        The model object representing a blog category.
        public class BlogCategory {
          private String id;
          private String name;
          //Getters and Setters removed for readability
        }
        Sample JSON Payload
        This JSON payload can be used to populate the BlogCategory object.
        {
            "id": "new_cat",
            "name": "new cat neme"
        }
        A controller method to add a new category.
        In the following example, we mark the method parameters "input" with @RequestBody. We also mark the return type of the method ResponseEntity with @ResponseBody.
        @RequestMapping(value = {"/categories"}, method = RequestMethod.POST,
              produces = MediaType.APPLICATION_JSON_VALUE)
          public @ResponseBody ResponseEntity addCats(@RequestBody BlogCategory input) {
            try {
              log.info("payload: " + input);
              blogService.addBlogCategories(input);
              return ResponseEntity.ok().body(input);
            } catch (Exception e) {
              log.info(e.getMessage());
              return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
            }
          }
        As a result, when the controller receives a request, the payload will be parsed and used to populate an instance of the BlogCategory object. Similarly when returning Web response back to client Spring will serialize the instance of and ResponseEntity to Web response.
        Invoke API with Curl
        We can invoke the API with payload by running the following curl command from a Terminal.
        curl -X POST \
          http://localhost:8080/blogapi/categories \
          -H 'Content-Type: application/json' \
          -d '{
            "id": "new_cat",
            "name": "new cat neme"
        }'
        API Response
        The API should return with the following JSON payload.
        {
            "id": "new_cat",
            "name": "new cat neme"
        }
        Summary
        • @RequestBody annotation facilitates deserialize the payload to a domain object.
        • @ResponseBody annotation facilitates serialization of the domain object to the payload.
        • @ResponseBody annotation is not required if we use @RestController rather then @Controller to annotate our Controller class.
        • @RestController is recommended over using @ResponseBody.
        • Another option is to use ResponseEntity as a return type of the controller method.
        Git Source code
        • git clone https://github.com/siddharthagit/spring-boot-references
        • cd spring-rest
        • mvn clean install
        • mvn spring-boot:run

          References

          ResponseEntity

          ResponseEntity class is used to represent HTTP response containing body, header and status. ResponseEntity class extends HttpEntity and used to encapsulate HTTP response. While writing the REST controller one of the common design questions is what should the controller method return? Also, we need to be cognizant about different REST endpoints and making sure all the responses maintain a standard pattern. All responses share common response building blocks like HTTP headers and HTTP Status. Using ResponseEntity is one of the options to write a REST controller, which helps in setting headers and status easily. ResponseEntity is not the only option we have while creating REST controllers. But It does provide some advantages In this short article, we will see the different ways of writing REST Controller and returning data from the controller.
          How to handing serialize the data from the controller depends on the use case we are dealing with. But we should keep in mind about easy maintenance of code and maintaining request-response structure across different endpoints.
          Using ResponseEntity is not only option to manipulate the response, but We can also return directly any Java object from the controller and let Spring do the serialization (Our Second example in this article).
          For setting the HTTP status only we can use @ResponseStatus annotation.
          For setting headers and status we can also javax.servlet.http.HttpServletResponse directly.

          ResponseEntity Class

          ResponseEntity Class is used to represent whole HTTP responses. It supports the
          the message body,
          headers,
          status, etc.
          Internally Response extends org.springframework.http.HttpEntity The advantage of using ResponseEntity is that it is easy to set headers status on this object directly which gets serialized as an HTTP response.
          Let's see an example controller using ResponseEntity

          Examples

          Example 1: Controller with ResponseEntity
          In this example, we are building a controller that returns a list of Tags as a response. Each tag is represented by a String object, so we are returning basically List
          We are wrapping a list of tags received form service in ResponseEntity> object. We can then set the HTTP status on the ResponseEntity object. If everything looks good, we are setting the status "OK 200", else we are setting HTTP status " 500 Internal Server Error".
          import java.util.List;
          import org.slf4j.Logger;
          import org.slf4j.LoggerFactory;
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.http.HttpHeaders;
          import org.springframework.http.HttpStatus;
          import org.springframework.http.MediaType;
          import org.springframework.http.ResponseEntity;
          import org.springframework.web.bind.annotation.RequestMapping;
          import org.springframework.web.bind.annotation.RequestMethod;
          import org.springframework.web.bind.annotation.RestController;
          import com.bootng.beans.AppException;
          
          @RestController
          @RequestMapping("/blogapi")
          public class TagAPIController {
          
          @RequestMapping(value = {"v1/tags"}, method = RequestMethod.GET,
                produces = MediaType.APPLICATION_JSON_VALUE)
            public ResponseEntity> getTagsV1() {
              try {
                List tags = blogService.getBlogTags();
                return ResponseEntity.ok().body(tags);
              } catch (AppException e) {
                log.error(e.getMessage());
                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
              }
            }
          }
          Following is a controller that also does the same stuff as the above controller. That is the return list of Tags. But here we are not using ResponseEntity, rather we are sending the Object directly from the controller method
          Let's see the example.
          Example 2: Controller without ResponseEntity
          Following controller which returns the list of Tags as a JSON response. Notice that the method returns List and not ResponseEntity.
          package com.bootng;
          
          import java.util.List;
          import org.slf4j.Logger;
          import org.slf4j.LoggerFactory;
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.http.HttpHeaders;
          import org.springframework.http.HttpStatus;
          import org.springframework.http.MediaType;
          import org.springframework.http.ResponseEntity;
          import org.springframework.web.bind.annotation.RequestMapping;
          import org.springframework.web.bind.annotation.RequestMethod;
          import org.springframework.web.bind.annotation.RestController;
          import com.bootng.beans.AppException;
          
          
          @RestController
          @RequestMapping("/blogapi")
          public class TagAPIController {
          
            private static final Logger log = LoggerFactory.getLogger(TagAPIController.class);
          
            @Autowired
            BlogService blogService;
            @RequestMapping(value = {"v3/tags"}, method = RequestMethod.GET,
                produces = MediaType.APPLICATION_JSON_VALUE)
            public List getTagsV3() {
              List tags = null;
              try {
                tags = blogService.getBlogTags();
              } catch (AppException e) {
                log.error(e.getMessage());
              }
              return tags;
            }
          }
          Response from both the above API / v3/tags and /v1/tags looks the same.
          Example 3
          In this example, we are writing the controller with HttpServletResponse as a parameter. This allows us to set the header and status directly on the object.
          package com.bootng;
          @RequestMapping(value = {"/welcome"}, method = RequestMethod.GET,
                produces = MediaType.APPLICATION_JSON_VALUE)
          void welcome(HttpServletResponse response) throws IOException {
              response.setHeader("Custom-Header-Message-Type", "welcome_user");
          response.setHeader("Custom-Header-Message-LANG", "en-us");
              response.setStatus(200);
              response.getWriter().println("Welcome");
          }
          Conclusions
          • We saw how to write a REST controller using ResponseEntity and without using ResponseEntity.
          • ResponseEntity makes it easy to set common HTTP Response elements like status code, headers, etc.
          • ResponseEntity is definitely useful but makes the overall controller method less readable the controller method return type is now ResponseEntity wrapped actual return type.

          References