Core Java

Consuming Page Entity Response From RestTemplate

In this tutorial, we will explore how to effectively consume Page Entity Response From RestTemplate using the Jackson library. When building Java applications that interact with RESTful services, one common task is to use RestTemplate to make HTTP requests and handle the corresponding responses. We’ll follow a step-by-step approach to set up a basic RESTful endpoint, create a client class to consume the endpoint, handle potential exceptions, and write an integration test to verify the behavior.

1. Introduction

In modern web development, the interaction between applications often involves consuming RESTful APIs. RestTemplate simplifies this process by providing an abstraction to make HTTP requests and process responses. Our tutorial will focus on using RestTemplate to fetch data from a RESTful endpoint and deserialize it into a Page of entities. Additionally, we will utilize Jackson, a popular JSON library, for deserialization.

2. Create a Spring Boot Project

You can start by creating a new Spring Boot project using your preferred IDE or by using Spring Initializr online.

  1. Visit the Spring Initializr website: https://start.spring.io/
  2. Select the necessary project options, such as project type, language, and packaging.
  3. Add the required dependencies:
    • Web
    • Jackson
    • Lombok (optional, but recommended for cleaner code)
    • H2 Database (if needed for storing data)
  4. Generate the project and download the ZIP file.

3. Configure Dependencies

If you’re using Maven, your pom.xml should contain the following dependencies:

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.15.2</version>
        </dependency>

    </dependencies>

4. Create Entity and Repository

Let’s assume you’re working with an Employee entity. Create the entity class in the src/main/java/com/example/demo directory:

package org.example;

import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Data;

@Entity
@Data
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @JsonProperty("firstName")
    private String firstName;
    @JsonProperty("lastName")
    private String lastName;

    public Employee(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public Employee() {

    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}

This Java class represents an Employee entity, typically used in a Spring Boot application with database persistence. Let’s break down the key components and annotations in this class:

  1. @Entity Annotation:
    • This annotation is from the Java Persistence API (JPA) and marks this class as a JPA entity. In the context of Spring Boot and JPA, it means that instances of this class can be mapped to database records.
  2. @Data Annotation:
    • The @Data annotation is from the Lombok library. It generates common boilerplate code for getter and setter methods, toString(), equals(), and hashCode() methods, which are typically required for Java classes.
    • With @Data, you don’t need to write these methods explicitly; Lombok generates them during compilation.
  3. Fields:
    • id: This field represents the unique identifier for an employee. It is annotated with @Id, indicating that it’s the primary key for database purposes.
    • @GeneratedValue(strategy = GenerationType.IDENTITY): This annotation is used in conjunction with @Id to specify that the id field’s values should be generated by the database using an identity strategy. This typically corresponds to auto-incrementing primary keys in relational databases.
    • firstName and lastName: These fields represent the first name and last name of an employee. They are annotated with @JsonProperty, which is typically used to specify the names of properties when serializing and deserializing JSON. In this case, it ensures that the JSON keys for these properties will be “firstName” and “lastName.”
  4. Constructors:
    • The class defines two constructors:
      • public Employee(String firstName, String lastName): This constructor allows you to create an Employee object with the first name and last name provided as parameters. It is likely used for creating new employee records.
      • public Employee(): This is a no-argument constructor, which is typically required for JPA entities. It allows for the creation of instances without specifying initial values.
  5. Getter and Setter Methods:
    • For each field (id, firstName, and lastName), there are corresponding getter and setter methods. Lombok’s @Data annotation generates these methods automatically. These methods allow you to access and modify the values of the fields.

This class defines the structure of an Employee entity with three properties: id, firstName, and lastName. It also provides constructors and getter/setter methods for interacting with instances of this class. In a Spring Boot application with JPA, instances of this class can be mapped to database records, allowing you to perform database operations such as saving, updating, and retrieving employee data. Additionally, the @JsonProperty annotations ensure proper JSON serialization and deserialization when working with this entity in RESTful services.

Create a repository interface in the same directory:

import org.springframework.data.jpa.repository.JpaRepository;

public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}

This code defines a Spring Data JPA repository interface named EmployeeRepository. Spring Data JPA simplifies the process of creating data repositories (commonly used for database access) by providing a set of conventions and a framework for generating repository implementations at runtime.

5. Create RestController

Create a EmployeeController class in the src/main/java/com/example directory:

package org.example;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@RestController
public class EmployeeController {

    private EmployeeRepository employeeRepository;

    public EmployeeController(EmployeeRepository employeeRepository) {
        this();
        this.employeeRepository = employeeRepository;
    }

    public EmployeeController() {

    }

    @GetMapping("/employee/data")
    public ResponseEntity<Page<Employee>> getData(@RequestParam(defaultValue = "0") int page,
                                                     @RequestParam(defaultValue = "10") int size) {
        List<Employee> empList = listImplementation();


        int totalSize = empList.size();
        int startIndex = page * size;
        int endIndex = Math.min(startIndex + size, totalSize);

        List<Employee> pageContent = empList.subList(startIndex, endIndex);

        Page<Employee> employeeDtos = new PageImpl<>(pageContent, PageRequest.of(page, size), totalSize);

        return ResponseEntity.ok()
                .body(employeeDtos);
    }

    private static List<Employee> listImplementation() {
        List<Employee> empList = new ArrayList<>();
        empList.add(new Employee("Jane", "Finance"));
        empList.add(new Employee("Sarah", "IT"));
        empList.add(new Employee("John", "IT"));
        return empList;
    }

}

This is a Java class annotated with @RestController, which indicates that it’s a Spring MVC controller class responsible for handling HTTP requests and providing responses. Let’s break down the key components and functionality of this EmployeeController class:

  1. Constructor Injection:
    • The class has two constructors. One constructor takes an EmployeeRepository as a parameter, implying that it can be used for dependency injection. Dependency injection allows you to provide the controller with the necessary dependencies (in this case, an EmployeeRepository) when it’s created.
    • The other constructor (public EmployeeController()) is a no-argument constructor. It’s typically used when you want to create an instance of the class without any dependencies explicitly injected. In this case, it seems to be empty and does nothing.
  2. Endpoint Mapping:
    • The class defines an HTTP GET endpoint mapped to the path “/employee/data”. This means that when a GET request is made to this path, the getData method will be executed to handle the request.
    • The @GetMapping annotation specifies that this method should handle GET requests.
  3. getData Method:
    • This method takes two optional query parameters: “page” and “size,” which have default values of 0 and 10, respectively. These parameters are commonly used for pagination, allowing clients to request specific pages of data.
    • Inside the method:
      • listImplementation() is called to generate a list of Employee objects. This is a simplified implementation, likely used for testing purposes. In a real application, you would typically retrieve data from a database using the injected EmployeeRepository.
      • It calculates the total size of the data and determines the range of elements to include on the requested page.
      • It creates a Page<Employee> using PageImpl, specifying the content (data on the current page), the PageRequest (containing the page number and size), and the total size of data.
      • Finally, it returns an HTTP response with the Page<Employee> as the response body and a status code of 200 (OK).
  4. listImplementation Method:
    • This private method generates a list of Employee objects. It’s used as a placeholder for generating sample data. In a real application, you would replace this method with code to retrieve data from a database or another data source.
  5. Employee Entity:
    • It appears that there is an Employee entity class used in this controller. However, its definition is not provided in the code snippet. Typically, an entity class would represent data stored in a database and might have fields like name, department, etc.

This EmployeeController class handles GET requests to “/employee/data” by returning paginated data in the form of a Page<Employee>. In a real-world scenario, you would replace the sample data generation with actual data retrieval from a database using the EmployeeRepository.

6. Configure RestTemplate Bean

In your main application class (typically named DemoApplication), create a RestTemplate bean:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

This code defines a Spring Boot application with a main method that can be used to run the application. It also configures a RestTemplate bean, which can be used for making HTTP requests within the application. The @SpringBootApplication annotation brings together various Spring Boot features, making it easier to develop and run Spring applications with minimal configuration.

@Bean public RestTemplate restTemplate(): This method is annotated with @Bean, indicating that it defines a Spring Bean of type RestTemplate. A RestTemplate is a class provided by Spring for making HTTP requests to external services or APIs. By defining it as a bean, you make it available for injection into other parts of your application.

The method name restTemplate is the name by which the RestTemplate bean can be accessed within the Spring application context.

The RestTemplate bean is created and returned by this method.

7. Create Integration Test

Create an integration test class named EmployeeClientIntegrationTest in the src/test/java/com/example directory:

package org.example;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ActiveProfiles;

import java.util.List;

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

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
public class EmployeeClientIntegrationTest<T> {

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    public void testFetchEmployeeData() {
        ResponseEntity<CustomPageImpl<EmployeeController>> responseEntity = restTemplate.exchange(
                "/employee/data?page=0&size=10",
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<CustomPageImpl<EmployeeController>>() {
                }
        );

        assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
        CustomPageImpl<EmployeeController> employeePage = responseEntity.getBody();
        assertNotNull(employeePage);

        List<EmployeeController> content = employeePage.getContent();
        assertNotNull(content);
        assertEquals(10, content.size());
    }

}

Here’s what’s happening in this integration test:

  1. Package and Imports: The test class is located in the org.example package. It imports various Spring Boot and JUnit classes necessary for testing.
  2. Annotations:
    • @SpringBootTest: This annotation tells Spring Boot to set up a test application context. It uses a random web port (WebEnvironment.RANDOM_PORT) for testing.
    • @ActiveProfiles("test"): This annotation activates a Spring profile named “test.” Profiles are typically used to configure different environments, and in this case, it’s likely used to configure test-specific settings.
  3. Autowired TestRestTemplate: An instance of TestRestTemplate is injected using @Autowired. This allows you to make HTTP requests to your application as if it were running.
  4. Test Method – testFetchEmployeeData:
    • This method defines a test case that fetches employee data from an endpoint.
    • restTemplate.exchange: It uses the TestRestTemplate to make an HTTP GET request to the “/employee/data” endpoint with query parameters for pagination.
    • ParameterizedTypeReference: It’s used to capture the generic type of the response, which is CustomPageImpl<EmployeeController>. This is necessary because generic type information is erased at runtime.
    • Assertions: The test asserts that:
      • The HTTP status code of the response is HttpStatus.OK.
      • The response body is not null.
      • The content of the response, representing employee data, is not null.
      • The size of the content list is 10, indicating that the pagination is working correctly.

This integration test verifies the behavior of the “/employee/data” endpoint in your Spring Boot application by making an HTTP request and checking the response. It ensures that the endpoint returns the expected HTTP status code and that the pagination is functioning as expected, delivering a page of 10 employee records. This type of test helps ensure that your application’s API endpoints work correctly in a real-world scenario.

8. CustomPageImpl Class

The CustomPageImpl class appears to be a custom implementation of the Spring Data Page interface used in a Spring Boot application. It’s typically used when you want to customize the behavior of pagination responses in your application, particularly when working with Spring Data JPA or other data sources. Let’s explain its usage and some key methods within the class:

8.1 Usage of CustomPageImpl Class

In a Spring Boot application, when you retrieve a list of data that might be paginated (e.g., a list of records from a database), Spring Data often returns the results as a Page<T> object. The Page interface represents a page of data along with metadata like the total number of items and the number of items per page.

Here’s how the CustomPageImpl class is used:

  • Custom Pagination Response: CustomPageImpl is a custom implementation of the Page interface that allows you to customize the behavior of pagination responses. It’s particularly useful when you need to customize the deserialization of pagination responses from JSON.
  • Custom Constructors: CustomPageImpl provides custom constructors that are used for deserializing JSON responses into instances of this class. These constructors take various parameters, including the content of the page, page number, page size, total elements, and more.
  • @JsonCreator Annotation: The @JsonCreator annotation is used on one of the constructors to indicate that this constructor should be used for deserialization. It specifies the mode as JsonCreator.Mode.PROPERTIES, which means that Jackson will use the provided properties (e.g., “content,” “number,” “size,” “totalElements”) to map the JSON data to the constructor’s parameters.
  • Flexibility: By creating a custom CustomPageImpl class, you can add custom logic to the constructors, modify how pagination data is deserialized, or even add additional metadata to the pagination response.

8.2 Key Methods of CustomPageImpl Class

While the CustomPageImpl class primarily consists of constructors for deserialization purposes, it also inherits methods from the Page interface provided by Spring Data. Some key methods from the Page interface that you can expect to find in the CustomPageImpl class include:

MethodDescription
getContent()Returns the content of the current page as a List.
getNumber()Returns the page number (0-based) of this page.
getSize()Returns the size (number of items) of the page.
getTotalElements()Returns the total number of elements available across all pages.
getTotalPages()Returns the total number of pages.
hasPrevious()Checks if there is a previous page.
hasNext()Checks if there is a next page.

8.3 CustomPageImpl

Create the CustomPageImpl class in the same directory as your test class:

package org.example;

import java.util.ArrayList;
import java.util.List;

import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;

@JsonIgnoreProperties(ignoreUnknown = true)
public class CustomPageImpl<T> extends PageImpl<T> {
    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
    public CustomPageImpl(@JsonProperty("content") List<T> content, @JsonProperty("number") int number,
                          @JsonProperty("size") int size, @JsonProperty("totalElements") Long totalElements,
                          @JsonProperty("pageable") JsonNode pageable, @JsonProperty("last") boolean last,
                          @JsonProperty("totalPages") int totalPages, @JsonProperty("sort") JsonNode sort,
                          @JsonProperty("numberOfElements") int numberOfElements) {
        super(content, PageRequest.of(number, 1), 10);
    }

    public CustomPageImpl(List<T> content, Pageable pageable, long total) {
        super(content, pageable, total);
    }

    public CustomPageImpl(List<T> content) {
        super(content);
    }

    public CustomPageImpl() {
        super(new ArrayList<>());
    }
}

9. Run the Application and Test

Now, you can run your Spring Boot application by executing the main method in your DemoApplication class.

For testing, you can run the EmployeeClientIntegrationTest class as a JUnit test. This test will start a test server, send an HTTP request to the /employee/data endpoint, and verify the response.

Here is the result of our test:

Fig. 1: Consuming Page Entity Response From RestTemplate Test Output.
Fig. 1: Consuming Page Entity Response From RestTemplate Test Output.

10. Conclusion

By following these steps, you have set up a Spring Boot project with RestTemplate and Jackson for consuming a RESTful endpoint and handling JSON deserialization. This will allow you to effectively fetch data from RESTful APIs and integrate it into your Java application. Remember to customize the code according to your specific use case and requirements.

11. Download the Source Code

This was an example of how to consume Page Entity Response From RestTemplate!

Download
You can download the full source code of this example here: Consuming Page Entity Response From RestTemplate

Odysseas Mourtzoukos

Mourtzoukos Odysseas is studying to become a software engineer, at Harokopio University of Athens. Along with his studies, he is getting involved with different projects on gaming development and web applications. He is looking forward to sharing his knowledge and experience with the world.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button