5

Is it possible to read data from DB, process it and in ItemWriter send to another system using RestAPI (REST TEMPLATE) in Spring batch project? All I can see is fetch data and write it in a csv file.

  • Web service response on http call only, in your http call you can call other api or other http web url. But if you are talking about reading from database and returning to web ui or then the process is simple, let's know more clear question – Popeye Sep 27 '21 at 03:27
  • @Popeye , So basically i am working on a project where I have to fetch data from DB and send it to another downstream system, not a web UI. System A triggers a batch process to fetch from DB and send it to System B, where it will process data. – shubham sharma Sep 27 '21 at 04:30
  • You can create a custom writer that sends the REST request, as shown in the answer by "Asif A Fasih" (except doing a POST or PUT operation instead of GET as this is what typically an item writer does). – Mahmoud Ben Hassine Sep 27 '21 at 06:26
  • Thanks @MahmoudBenHassine, I asked a follow up question with Asif and let me as this to you too. " I have to send payload in loop, waiting for response for 1st payload will increase time and block us from sending 2nd payload till 1st payload's response is not received, Any way I can send List of payloads atomically and concurrently?? " – shubham sharma Sep 27 '21 at 07:56

2 Answers2

2

It is possible to create your own custom ItemWriter.

In your case, please add the spring-boot-starter-web dependency to either your pom.xml or build.gradle

Example:

package com.example.batch;

import lombok.extern.log4j.Log4j2;
import org.springframework.batch.item.ItemWriter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@Log4j2
public class RestItemWriter implements ItemWriter<String> {
    @Autowired
    RestTemplate restTemplate;

    public RestItemWriter(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @Override
    public void write(List<? extends String> items) throws Exception {
        ResponseEntity<Users> users = restTemplate.getForEntity("https://jsonplaceholder.typicode.com/users/1", Users.class);

        log.info("Status code is: " + users.getStatusCode());
    }
}


package com.example.batch;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Users {

    public String id;
    public String name;
    public String username;
    public String email;
    public String phone;
}

More information about custom item writers here

Asif A Fasih
  • 157
  • 7
  • Thanks @Asif, I have to send payload in loop, waiting for response for 1st payload will increase time and block us from sending 2nd payload till 1st payload's response is not received, Any way I can send List of payloads atomically and concurrently?? – shubham sharma Sep 27 '21 at 04:37
  • have you looked into using WebFlux? https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html – Asif A Fasih Sep 27 '21 at 14:07
  • yes I looked into it, but I want to know if this could be done using CompleteableFuture? I have seen lot of examples of CF being used for DB call, but not for rest api call. Is it possible? – shubham sharma Sep 28 '21 at 13:06
  • yes, it is possible. I found this blog which has some good examples - https://www.twilio.com/blog/asynchronous-api-requests-java-completablefutures – Asif A Fasih Sep 28 '21 at 14:27
1

Of course! you can send the processed records to another system using REST call in ItemWriter.

Use the below code in your RestItemWriter class.

private RestTemplate restTemplate;

@Override
public void write(@NonNull Chunk<? extends Payload> chunk) throws Exception {

    for((Payload payload : chunk){

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("Authorization", "xx-tokenString-xx");
                    
        HttpEntity<Payload> requestEntity = new HttpEntity<>(payload, headers);

        try{
            ResponseEntity<Object> response = restTemplate.exchange(url, HttpMethod.PATCH, requestEntity, Object.class);
            LOGGER.info("Request hits the server {}", response.getBody());
        } catch(HttpClientErrorException e){
            LOGGER.error("HttpClientErrorException occured during connection {}", e.getMessage());
        } catch (Exception e) {
            LOGGER.error("Exception occured during connection {}", e.getMessage());
        }

    }
}

To make HTTP Patch request using RestTemplate, below configurations are mandatory, for other HTTP calls you may ignore it.

 @Bean
    public RestTemplate restTemplate() {
        LOGGER.info("restTemplate Bean has bean created");
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
        RestTemplate restTemplate = new RestTemplate(factory);
        return restTemplate;
    }

You have to add the below dependency in pom.xml to use CloseableHttpClient, and HttpClients.

<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
</dependency>