All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.naver.spring.batch.extension.item.rest.AbstractAsyncRestItemReader Maven / Gradle / Ivy

There is a newer version: 1.1.1
Show newest version
/*
Copyright 2018 NAVER Corp.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
 */
package com.naver.spring.batch.extension.item.rest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.NonTransientResourceException;
import org.springframework.batch.item.ParseException;
import org.springframework.batch.item.UnexpectedInputException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.task.AsyncListenableTaskExecutor;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.util.Assert;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.web.client.AsyncRestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 

* 페이징 처리된 Rest API 로부터 데이터를 읽어 들이기 위한 Item Reader. * * 여러개의 variables 로부터 요청하는 경우 {@link AsyncListenableTaskExecutor} 를 통해 Async 요청을 수행한다. * response 에 대해서 {@link #convertResponse(ResponseEntity, Map)} ()} 를 통해 Object 매핑 방식을 하위 클래스에서 구현한다. *

* * @author yongkyu.lee * @since 0.1 */ public abstract class AbstractAsyncRestItemReader implements ItemReader, InitializingBean { private Logger log = LoggerFactory.getLogger(AbstractAsyncRestItemReader.class); private AsyncRestTemplate asyncRestTemplate; private Map, ListenableFuture>> responseMap = null; private Iterator itemIterator = null; private Iterator> uriVariablesIterator; private String apiUri; private List> uriVariables; private AsyncListenableTaskExecutor asyncListenableTaskExecutor; private HttpHeaders headers; private int responseTimeout = 30; public void setApiUri(String apiUri) { this.apiUri = apiUri; } public void setUriVariables(List> uriVariables) { this.uriVariables = uriVariables; } public void setAsyncListenableTaskExecutor(AsyncListenableTaskExecutor asyncListenableTaskExecutor) { this.asyncListenableTaskExecutor = asyncListenableTaskExecutor; } public void setHeaders(HttpHeaders headers) { this.headers = headers; } public void setResponseTimeout(int responseTimeout) { this.responseTimeout = responseTimeout; } /** * responseEntiry 로부터 response body 를 parsing 해 List 로 리턴하도록 구현한다. * * @param responseEntity null 이면 responseEntity 반환에 실패. * @param uriVariable uri 에 포함된 변수 목록 * @return response 의 List 형태 */ abstract protected List convertResponse(ResponseEntity responseEntity, Map uriVariable) throws Exception; @Override public T read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException { requestAsyncRestApis(); if (itemIterator == null || !itemIterator.hasNext()) { while (this.uriVariablesIterator.hasNext()) { Map uriVariable = this.uriVariablesIterator.next(); ListenableFuture> listenableFuture = responseMap.get(uriVariable); ResponseEntity responseEntity = listenableFuture.get(this.responseTimeout, TimeUnit.SECONDS); List results = convertResponse(responseEntity, uriVariable); if (results != null && !results.isEmpty()) { this.itemIterator = results.iterator(); break; } } } return (itemIterator != null && itemIterator.hasNext()) ? itemIterator.next() : null; } @Override public void afterPropertiesSet() throws Exception { Assert.notNull(apiUri, "'apiUri' is required"); if (asyncListenableTaskExecutor != null) { asyncRestTemplate = new AsyncRestTemplate(asyncListenableTaskExecutor); } else { asyncRestTemplate = new AsyncRestTemplate(); } asyncRestTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8"))); } private void requestAsyncRestApis() { if (this.responseMap != null) { return; } if (this.uriVariables == null || this.uriVariables.isEmpty()) { this.uriVariables = Collections.singletonList(Collections.emptyMap()); } this.uriVariablesIterator = this.uriVariables.iterator(); this.responseMap = new HashMap<>(); UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(apiUri); if (this.headers == null) { this.headers = new HttpHeaders(); } for (Map uriVariable : this.uriVariables) { URI uri = uriBuilder.buildAndExpand(uriVariable).toUri(); if (log.isDebugEnabled()) { log.debug("request uri: {}", uri.toString()); } ListenableFuture> resFuture = asyncRestTemplate.exchange(uri, HttpMethod.GET, new HttpEntity<>(this.headers), String.class); this.responseMap.put(uriVariable, resFuture); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy