package io.codef.api;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import org.apache.commons.codec.binary.Base64;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
*
* io.codef.easycodef
* |_ EasyCodefConnector.java
*
*
* Desc : CODEF 엑세스 토큰 및 상품 조회를 위한 HTTP 요청 클래스
* @Company : ©CODEF corp.
* @Author : notfound404@codef.io
* @Date : Jun 26, 2020 3:35:17 PM
*/
public class EasyCodefConnector {
private static ObjectMapper mapper = new ObjectMapper();
private static final int REPEAT_COUNT = 3;
/**
* Desc : CODEF 상품 조회 요청
* @Company : ©CODEF corp.
* @Author : notfound404@codef.io
* @Date : Jun 26, 2020 3:35:26 PM
* @param urlPath
* @param serviceType
* @param bodyMap
* @param properties
* @return
* @throws InterruptedException
*/
@SuppressWarnings("unchecked")
protected static EasyCodefResponse execute(String urlPath, int serviceType, HashMap bodyMap, EasyCodefProperties properties) throws InterruptedException {
/** #1.토큰 체크 */
String domain;
String clientId;
String clientSecret;
if(serviceType == 0) {
domain = EasyCodefConstant.API_DOMAIN;
clientId = properties.getClientId();
clientSecret = properties.getClientSecret();
} else if(serviceType == 1) {
domain = EasyCodefConstant.DEMO_DOMAIN;
clientId = properties.getDemoClientId();
clientSecret = properties.getDemoClientSecret();
} else {
domain = EasyCodefConstant.SANDBOX_DOMAIN;
clientId = EasyCodefConstant.SANDBOX_CLIENT_ID;
clientSecret = EasyCodefConstant.SANDBOX_CLIENT_SECRET;
}
String accessToken = getToken(clientId, clientSecret); // 토큰 반환
/** #2.요청 파라미터 인코딩 */
String bodyString;
try {
bodyString = mapper.writeValueAsString(bodyMap);
bodyString = URLEncoder.encode(bodyString, "UTF-8");
} catch (JsonProcessingException e) {
EasyCodefResponse response = new EasyCodefResponse(EasyCodefMessageConstant.INVALID_JSON);
return response;
} catch (UnsupportedEncodingException e) {
EasyCodefResponse response = new EasyCodefResponse(EasyCodefMessageConstant.UNSUPPORTED_ENCODING);
return response;
}
/** #3.상품 조회 요청 */
HashMap responseMap = requestProduct(domain + urlPath, accessToken, bodyString);
if(EasyCodefConstant.INVALID_TOKEN.equals(responseMap.get("error")) || "CF-00401".equals(((HashMap)responseMap.get(EasyCodefConstant.RESULT)).get(EasyCodefConstant.CODE))){ // 액세스 토큰 유효기간 만료되었을 경우 토큰 재발급 후 상품 조회 요청 진행
EasyCodefTokenMap.setToken(clientId, null); // 토큰 정보 초기화
accessToken = getToken(clientId, clientSecret); // 토큰 설정
responseMap = requestProduct(domain + urlPath, accessToken, bodyString);
} else if (EasyCodefConstant.ACCESS_DENIED.equals(responseMap.get("error")) || "CF-00403".equals(((HashMap)responseMap.get(EasyCodefConstant.RESULT)).get(EasyCodefConstant.CODE))) { // 접근 권한이 없는 경우 - 오류코드 반환
EasyCodefResponse response = new EasyCodefResponse(EasyCodefMessageConstant.UNAUTHORIZED, EasyCodefConstant.ACCESS_DENIED);
return response;
}
/** #4.상품 조회 결과 반환 */
EasyCodefResponse response = new EasyCodefResponse(responseMap);
return response;
}
/**
* Desc : CODEF HTTP POST 요청
* @Company : ©CODEF corp.
* @Author : notfound404@codef.io
* @Date : Jun 26, 2020 3:35:34 PM
* @param urlPath
* @param token
* @param bodyString
* @return
*/
private static HashMap requestProduct(String urlPath, String token, String bodyString) {
BufferedReader br = null;
try {
// HTTP 요청을 위한 URL 오브젝트 생성
URL url = new URL(urlPath);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setDoOutput(true);
con.setRequestMethod("POST");
con.setRequestProperty("Accept", "application/json");
if (token != null && !"".equals(token)) {
con.setRequestProperty("Authorization", "Bearer " + token); // 엑세스 토큰 헤더 설정
}
// 리퀘스트 바디 전송
OutputStream os = con.getOutputStream();
if (bodyString != null && !"".equals(bodyString)) {
os.write(bodyString.getBytes());
}
os.flush();
os.close();
// 응답 코드 확인
int responseCode = con.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
br = new BufferedReader(new InputStreamReader(con.getInputStream()));
} else if (responseCode == HttpURLConnection.HTTP_BAD_REQUEST) {
EasyCodefResponse response = new EasyCodefResponse(EasyCodefMessageConstant.BAD_REQUEST, urlPath);
return response;
} else if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
EasyCodefResponse response = new EasyCodefResponse(EasyCodefMessageConstant.UNAUTHORIZED, urlPath);
return response;
} else if (responseCode == HttpURLConnection.HTTP_FORBIDDEN) {
EasyCodefResponse response = new EasyCodefResponse(EasyCodefMessageConstant.FORBIDDEN, urlPath);
return response;
} else if (responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
EasyCodefResponse response = new EasyCodefResponse(EasyCodefMessageConstant.NOT_FOUND, urlPath);
return response;
} else {
EasyCodefResponse response = new EasyCodefResponse(EasyCodefMessageConstant.SERVER_ERROR, urlPath);
return response;
}
// 응답 바디 read
String inputLine;
StringBuffer responseStr = new StringBuffer();
while ((inputLine = br.readLine()) != null) {
responseStr.append(inputLine);
}
br.close();
// 결과 반환
return mapper.readValue(URLDecoder.decode(responseStr.toString(), "UTF-8"), new TypeReference>(){});
} catch (Exception e) {
EasyCodefResponse response = new EasyCodefResponse(EasyCodefMessageConstant.LIBRARY_SENDER_ERROR, e.getMessage());
return response;
} finally {
if(br != null) {
try {
br.close();
} catch (IOException e) {}
}
}
}
/**
* Desc : 엑세스 토큰 반환
* @Company : ©CODEF corp.
* @Author : notfound404@codef.io
* @Date : Jun 26, 2020 3:35:47 PM
* @param clientId
* @param clientSecret
* @return
* @throws InterruptedException
*/
private static String getToken(String clientId, String clientSecret) throws InterruptedException {
int i = 0;
String accessToken = EasyCodefTokenMap.getToken(clientId);
if(accessToken == null || "".equals(accessToken) || !checkToken(accessToken)) { //만료 조건 추가
while(i < REPEAT_COUNT) { // 토큰 발급 요청은 최대 3회까지 재시도
HashMap tokenMap = publishToken(clientId, clientSecret); // 토큰 발급 요청
if(tokenMap != null) {
String newToken = (String)tokenMap.get("access_token");
EasyCodefTokenMap.setToken(clientId, newToken); // 토큰 저장
accessToken = newToken;
}
if(accessToken != null || !"".equals(accessToken)) {
break; // 정상 발급시 반복문 종료
}
Thread.sleep(20);
i++;
}
}
return accessToken;
}
/**
* Desc : CODEF 엑세스 토큰 발급 요청
* @Company : ©CODEF corp.
* @Author : notfound404@codef.io
* @Date : Jun 26, 2020 3:36:01 PM
* @param clientId
* @param clientSecret
* @return
*/
protected static HashMap publishToken(String clientId, String clientSecret) {
BufferedReader br = null;
try {
// HTTP 요청을 위한 URL 오브젝트 생성
URL url = new URL(EasyCodefConstant.OAUTH_DOMAIN + EasyCodefConstant.GET_TOKEN);
String params = "grant_type=client_credentials&scope=read"; // Oauth2.0 사용자 자격증명 방식(client_credentials) 토큰 요청 설정
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
// 클라이언트아이디, 시크릿코드 Base64 인코딩
String auth = clientId + ":" + clientSecret;
byte[] authEncBytes = Base64.encodeBase64(auth.getBytes());
String authStringEnc = new String(authEncBytes);
String authHeader = "Basic " + authStringEnc;
con.setRequestProperty("Authorization", authHeader);
con.setDoInput(true);
con.setDoOutput(true);
// 리퀘스트 바디 전송
OutputStream os = con.getOutputStream();
os.write(params.getBytes());
os.flush();
os.close();
// 응답 코드 확인
int responseCode = con.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) { // 정상 응답
br = new BufferedReader(new InputStreamReader(con.getInputStream()));
} else { // 에러 발생
return null;
}
// 응답 바디 read
String inputLine;
StringBuffer responseStr = new StringBuffer();
while ((inputLine = br.readLine()) != null) {
responseStr.append(inputLine);
}
br.close();
HashMap tokenMap = mapper.readValue(URLDecoder.decode(responseStr.toString(), "UTF-8"), new TypeReference>(){});
return tokenMap;
} catch (Exception e) {
return null;
} finally {
if(br != null) {
try {
br.close();
} catch (IOException e) { }
}
}
}
/**
* 토큰 유효기간 확인
* @param accessToken
* @return
*/
private static boolean checkToken(String accessToken) {
HashMap tokenMap = null;
try {
tokenMap = EasyCodefUtil.getTokenMap(accessToken);
} catch (IOException e) {
// 확인 중 오류 발생 시
return false;
}
// 토큰의 유효 기간 확인
return EasyCodefUtil.checkValidity((int) (tokenMap.get("exp")));
}
}