From 30b04e6e19c106e620ecef2ce471a83c512214b7 Mon Sep 17 00:00:00 2001 From: curry772 Date: Wed, 17 Jun 2026 19:23:06 +0900 Subject: [PATCH] =?UTF-8?q?KJB=20=EA=B4=80=EB=A0=A8=20=EC=86=8C=EC=8A=A4?= =?UTF-8?q?=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kjb/HttpClient5AdapterServiceBase64.java | 897 ++++++++++++++++++ .../kjb/HttpClient5AdapterServiceBypass.java | 680 +++++++++++++ .../kjb/HttpClient5AdapterServiceKbank.java | 888 +++++++++++++++++ .../dynamic/filter/FilterCryptoException.java | 18 + .../http/dynamic/filter/FilterException.java | 21 + 5 files changed, 2504 insertions(+) create mode 100644 src/main/java/com/eactive/eai/adapter/http/client/impl/kjb/HttpClient5AdapterServiceBase64.java create mode 100644 src/main/java/com/eactive/eai/adapter/http/client/impl/kjb/HttpClient5AdapterServiceBypass.java create mode 100644 src/main/java/com/eactive/eai/adapter/http/client/impl/kjb/HttpClient5AdapterServiceKbank.java create mode 100644 src/main/java/com/eactive/eai/adapter/http/dynamic/filter/FilterCryptoException.java create mode 100644 src/main/java/com/eactive/eai/adapter/http/dynamic/filter/FilterException.java diff --git a/src/main/java/com/eactive/eai/adapter/http/client/impl/kjb/HttpClient5AdapterServiceBase64.java b/src/main/java/com/eactive/eai/adapter/http/client/impl/kjb/HttpClient5AdapterServiceBase64.java new file mode 100644 index 0000000..8221db4 --- /dev/null +++ b/src/main/java/com/eactive/eai/adapter/http/client/impl/kjb/HttpClient5AdapterServiceBase64.java @@ -0,0 +1,897 @@ +package com.eactive.eai.adapter.http.client.impl.kjb; + +import java.io.IOException; +import java.io.StringReader; +import java.math.BigDecimal; +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.StopWatch; +import org.apache.commons.codec.binary.Base64; +import org.apache.hc.client5.http.classic.HttpClient; +import org.apache.hc.client5.http.classic.methods.HttpDelete; +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.classic.methods.HttpPost; +import org.apache.hc.client5.http.classic.methods.HttpPut; +import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; +import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.entity.UrlEncodedFormEntity; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.*; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.hc.core5.http.io.entity.StringEntity; +import org.apache.hc.core5.http.message.BasicNameValuePair; +import org.apache.hc.core5.http.protocol.BasicHttpContext; +import org.apache.hc.core5.http.protocol.HttpContext; +import org.apache.hc.core5.net.URIBuilder; +import org.dom4j.Document; +import org.dom4j.Element; +import org.dom4j.io.SAXReader; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import org.springframework.web.util.UriComponents; +import org.springframework.web.util.UriComponentsBuilder; + +import com.eactive.eai.adapter.http.HttpMemoryLogger; +import com.eactive.eai.adapter.http.HttpMethodType; +import com.eactive.eai.adapter.http.client.HttpClient5AdapterServiceSupport; +import com.eactive.eai.adapter.http.client.HttpClientAdapterServiceKey; +import com.eactive.eai.adapter.http.client.HttpClientAdapterVO; +import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey; +import com.eactive.eai.common.TransactionContextKeys; +import com.eactive.eai.common.authoutbound.AccessTokenManagerByDB; +import com.eactive.eai.common.exception.ExceptionUtil; +import com.eactive.eai.common.message.MessageType; +import com.eactive.eai.common.util.CommonLib; +import com.jayway.jsonpath.DocumentContext; +import com.jayway.jsonpath.JsonPath; +import com.openbanking.eai.common.token.AccessTokenManager; +import com.openbanking.eai.common.token.AccessTokenVO; +import com.openbanking.eai.common.token.OAuth2AccessTokenVO; + +import com.eactive.eai.common.property.PropManager; + +public class HttpClient5AdapterServiceBase64 extends HttpClient5AdapterServiceSupport + implements HttpClientAdapterServiceKey { + + public static final String TYPE_VARIABLE_URL_REQUEST = "variableUrlRequest"; + public static final String TYPE_SIMPLE_REQUEST = "simpleRequest"; + public static final String REST_OPTION = "REST_OPTION"; + //public static final String HEADER_CONTENT_TYPE = "Content-Type"; + public static final String HEADER_GROUP = "HEADER_GROUP"; + public static final String HTTP_STATUS = "HTTP_STATUS"; + public static final String HEADER_KEYS = "HEADER_KEYS"; + public static final String AUTH_TOKEN = "AUTH_TOKEN"; + + /** + * 1. 기능 : REST API 통신에 사용
+ * 2. 처리 개요 : - 속성 정보를 설정 하고 수동 시스템 서비스를 호출 한다.
+ * 3. 주의사항
+ * + * @param prop Http Adapter 속성 정보 + * @return 반환 된 Object + * @exception Exception 수동 시스템 간 통신 중 발생 + **/ + @SuppressWarnings({ "unchecked" }) + public Object execute(Properties prop, Object data, Properties tempProp) throws Exception { + HttpClientAdapterVO vo = super.setting(prop, tempProp); + + // 2025.04.09 Authorization 헤더값을 프로퍼티(AUTH_TOKEN)에서 지정하여 설정할 수 있도록 수정 + String bizCode = ""; + String authToken = ""; + try { + Properties authProp = PropManager.getInstance().getProperties(AUTH_TOKEN); //Authorization + bizCode = vo.getAdapterGroupName().substring(1, 4); + authToken = authProp.getProperty(bizCode, ""); + } catch (Exception e) { + logger.debug("Property Group AUTH_TOKEN is not Exist !!"); + } + + logger.debug("authToken :::::::::::::::::::::::::::::::::::" + authToken); + + String headerGroupName = prop.getProperty(HEADER_GROUP); + String relayResponseHeaderKeys = prop.getProperty(HEADER_KEYS, ""); + boolean useAdapterToken = StringUtils.equalsIgnoreCase(prop.getProperty("ADAPTER_TOKEN_USE_YN", "N"), "Y"); + + boolean useForwardProxy = StringUtils.equalsIgnoreCase(prop.getProperty("FORWARD_PROXY_USE_YN", "N"), "Y"); + String forwardProxyUrl = prop.getProperty("FORWARD_PROXY_URL"); + + // ex) $.dataHeader.GW_RSLT_CD + String tokenErrorCodeKey = prop.getProperty("ADAPTER_TOKEN_ERROR_CODE_KEY"); + String tokenErrorCodeValues = prop.getProperty("ADAPTER_TOKEN_ERROR_CODE_VALUES"); + String tokenErrorHttpStatusCode = prop.getProperty("ADAPTER_TOKEN_ERROR_HTTP_STATUS_CODE"); // 200 or 400번대 + + // 레이아웃 메시지 타입 REST URL 추출 시 활용. Default JSON + String messageType = prop.getProperty("MESSAGE_TYPE", MessageType.JSON); + String inboundMethod = tempProp.getProperty(HttpAdapterServiceKey.INBOUND_METHOD, "POST"); + + /** + * @formatter:off + * TSEAIHE02.RESTOPTION 정보 => JSON 형태로 구성 + * - type : simpleRequest, variableUrlRequest + * - extraPath : 어댑터 프로퍼티 URL에 추가될 HTTP REST URL + * - method : get, delete, post, put + * - contentType: application/json(default), application/x-www-form-urlencoded + * - adapterTokenUseYn: Y, N(default) + * ex) + * {"type":"simpleRequest","extraPath":"v2.0/accout/balance","method":"post"} + * @formatter:on + */ + + String restOptionData = tempProp.getProperty(REST_OPTION); + + JSONObject restOptionObject = parseJson(restOptionData); + if (restOptionObject == null) { + throw new Exception("OptionData parsing result is NULL"); + } + + String rmethod = (String) restOptionObject.getOrDefault("method", inboundMethod); + String contentType = (String) restOptionObject.getOrDefault("contentType", "application/json"); + String adapterTokenUseYn = (String) restOptionObject.get("adapterTokenUseYn"); + if (StringUtils.isNotBlank(adapterTokenUseYn)) { // interface 에 설정된게 우선한다. + useAdapterToken = StringUtils.equalsIgnoreCase(adapterTokenUseYn, "Y"); + } + + HttpClient mclient = this.client; + String sendData = ""; + if (data instanceof String) { + sendData = (String) data; + } else if (data instanceof byte[]) { + sendData = new String((byte[]) data, vo.getEncode()); + } + + ////mclient.getParams().setContentCharset(vo.getEncode()); + + JSONObject dataObject = null; + + Object httpHeader = null; + Object dataContent = null; + + if (MessageType.JSON.equals(messageType)) { + + Object parsed = parseJsonGeneric(sendData); + + if(parsed instanceof JSONObject) { + dataObject = parseJson(sendData); + if (dataObject != null && StringUtils.isNotBlank(headerGroupName)) { + httpHeader = dataObject.get(headerGroupName); + dataObject.remove(headerGroupName); + } + + dataContent = dataObject; + if (dataObject.containsKey("innerList")) { + JSONArray innerList = (JSONArray) dataObject.get("innerList"); + dataContent = innerList; + } + } + } + + logger.debug("HttpClientAdapterServiceRest] dataObject1 = [" + dataObject + "]"); + logger.debug("HttpClientAdapterServiceRest] dataContent = [" + dataContent + "]"); + + // interface 에 설정된게 우선한다. + String uri = null; + if (dataObject == null) { + uri = changeUrl(messageType, vo.getUrl(), restOptionData, sendData); + } else { + uri = changeUrl(messageType, vo.getUrl(), restOptionData, dataObject); + } + +// ////if (vo.getConnectionTimeout() != 0) { +// mclient.getHttpConnectionManager().getParams().setConnectionTimeout(vo.getConnectionTimeout()); +// } + + if (logger.isDebug() && "N".equals(vo.getTestCallYn())) { + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") URL=[" + vo.getUrl() + "] "); + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") PARAMETER_NAME=[" + vo.getParameterName() + "] "); + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") ENCODE=[" + vo.getEncode() + "] "); + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") RESPONSE_TYPE=[" + vo.getResponseType() + "] "); + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") URL_ENCODE_YN=[" + vo.getUrlEncodeYn() + "] "); + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") CONNECTION_TIMEOUT=[" + vo.getConnectionTimeout() + "] "); + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") REST_OPTION=[" + restOptionData + "] "); + } + + HttpUriRequestBase method = null; + + // 메소드 타입에 따라 HttpRequestBase 인스턴스 생성 + switch (HttpMethodType.getValue(rmethod)) { + case GET: + method = new HttpGet(uri); + break; + case DELETE: + method = new HttpDelete(uri); + break; + case POST: + method = new HttpPost(uri); + break; + case PUT: + method = new HttpPut(uri); + break; + default: + method = new HttpGet(uri); + break; + } + + RequestConfig.Builder requestConfigBuilder = RequestConfig.custom(); + + if(useForwardProxy) { + java.net.URL url = new java.net.URL(forwardProxyUrl); + + // 프로토콜, 호스트, 포트 추출 + String protocol = url.getProtocol(); + String host = url.getHost(); + int port = url.getPort(); + HttpHost proxy = new HttpHost(protocol,host, port); + requestConfigBuilder.setProxy(proxy); + } + + configureHttpClient(requestConfigBuilder, vo, method); + + switch (HttpMethodType.getValue(rmethod)) { + case GET: + case DELETE: + HashMap h = getParameters(dataObject); + URIBuilder uriBuilder = new URIBuilder(uri); + for (Map.Entry entry : h.entrySet()) { + uriBuilder.addParameter(entry.getKey(), entry.getValue()); + } + URI fullUri = uriBuilder.build(); + if (method instanceof HttpGet) { + ((HttpGet) method).setUri(fullUri); + } else if (method instanceof HttpDelete) { + ((HttpDelete) method).setUri(fullUri); + } + if (logger.isDebug()) { + logger.debug("HttpClientAdapterServiceRest] (get Method) QueryString = [" + fullUri.getQuery() + "]"); + } + break; + case PUT: + case POST: + //assignPostBody(dataObject, contentType, vo.getEncode(), method); + assignPostBody(dataContent, contentType, vo.getEncode(), method); + break; + default: + break; + } + + if (logger.isDebugEnabled()) { + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") method.getParams()=[" + + method.getEntity() + "] "); + } + + OAuth2AccessTokenVO accessToken = null; + if (useAdapterToken) { + AccessTokenManagerByDB tokenManager = AccessTokenManagerByDB.getInstance(); + accessToken = (OAuth2AccessTokenVO) tokenManager.getAccessTokenVO(vo.getAdapterGroupName()); + + // 토큰이 없거나 만료 됐으면 재발급 + if (accessToken == null || accessToken.isExpired()) { + String oldToken = accessToken == null ? null : accessToken.getAccessToken(); + accessToken = (OAuth2AccessTokenVO) tokenManager.retryAccessTokenVO(vo.getAdapterGroupName(), prop, + oldToken); + } + + if (logger.isDebug()) { + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") TOKEN = [" + + accessToken + "]"); + } + + setAuthHeaders(method, accessToken); + + if (logger.isDebug()) { + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + + ") RequestHeader [Authorization=" + method.getHeader("Authorization") + "]"); + } + } + + // 2025.04.09 Authorization 헤더값을 프로퍼티(AUTH_TOKEN)에서 지정하여 설정할 수 있도록 수정 + if(!StringUtils.isBlank(authToken)) { + setAuthHeaders(method, authToken); + } + + // 전달 header 셋팅 + // Adapter 레벨 header 보다 data로 넘어온 httpHeader를 우선 적용(header명이 같다면 덮어씀) + assignRequestHeaders(method, httpHeader); + + int status = -1; + + try { + byte[] responseMessage = null; + Header[] responseHeaders; + + if (logger.isDebug() && "N".equals(vo.getTestCallYn())) { + if (dataContent != null) { //dataObject + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") = [" + + dataContent.toString() + "]"); //dataObject.toJSONString() + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") = " + + CommonLib.getDumpMessage(dataContent.toString())); //dataObject.toJSONString() + } else { + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") = [" + sendData + + "]"); + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") = " + + CommonLib.getDumpMessage(sendData)); + } + } + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + + if (vo.getTraceLevel() >= 3) { + HttpMemoryLogger.txlog(vo.getAdapterGroupName() + vo.getAdapterName(), + "SEND [" + sendData + "]" + CommonLib.getDumpMessage(sendData)); + } + + String uuid = tempProp.getProperty(TransactionContextKeys.TRANSACTION_UUID); + HttpContext context = new BasicHttpContext(); + context.setAttribute(TransactionContextKeys.TRANSACTION_UUID, uuid); + context.setAttribute(HttpClientAdapterServiceKey.ADAPTER_GROUP_NAME, vo.getAdapterGroupName()); + context.setAttribute(HttpClientAdapterServiceKey.ADAPTER_NAME, vo.getAdapterName()); + Integer logProcessNo = (Integer) tempProp.get(HttpClientAdapterServiceKey.LOG_PROCESS_NO); + if (logProcessNo != null && logProcessNo > 0) { + context.setAttribute(HttpClientAdapterServiceKey.LOG_PROCESS_NO, logProcessNo); + } + + try { + if (logger.isDebug()) { + logger.debug("[method getName]" + method.getMethod()); + logger.debug("[method getEntity]" + method.getEntity()); + logger.debug("[method getRequestHeaders]" + method.getHeaders().toString()); + logger.debug("[method getURI]" + method.getPath()); + } + try (CloseableHttpResponse response = (CloseableHttpResponse) mclient.execute(method, context)) { + status = response.getCode(); + + if (status >= 200 && status <= 207) { + status = 200; + } + +// if (status == 404) { +// status = 200; +// } + +// responseMessage = EntityUtils.toByteArray(response.getEntity()); + responseHeaders = response.getHeaders(); + // 2025/08/06 Response body를 Base64로 Encode 해서 Json으로 만들어서 전달하도록 수정 + HttpEntity entity = response.getEntity(); + if (entity != null) { + byte[] entityBytes = EntityUtils.toByteArray(response.getEntity()); + String b64 = Base64.encodeBase64String(entityBytes); + JSONObject resultJson = new JSONObject(); + resultJson.put("base64_file_data", b64); + responseMessage = resultJson.toString().getBytes(); + } else { + responseMessage = EntityUtils.toByteArray(response.getEntity()); + } + + + if (logger.isDebug()) { + logger.debug("[received status]" + status); + logger.debug("HttpClientAdapterServiceRest] RECV (" + vo.getAdapterGroupName() + ") = [" + responseMessage + "]"); + } + } + } catch (ConnectException e) { + if (logger.isDebug() && "N".equals(vo.getTestCallYn())) { + logger.debug("HttpClientAdapterServiceRest] responseType =" + vo.getResponseType()); + } + throw new Exception("excuteMethod java.net.ConnectException = " + e.getMessage()); + } + + stopWatch.stop(); + + /** + * @formatter:off + * OAuth 토큰 응답 체크(Adapter properties로 설정) + * 응답코드에 따라 유효한 토큰 확인(토큰 재발급 여부 확인) + * API 서비스 마다 정책이 다름(보통 400대에서 체크하나 200에서도 체크할 수 있음) + * + * TOKEN_ERROR_CODE_KEY: 응답 json error code key + * __ex) $.dataHeader.resultCode + * TOKEN_ERROR_CODE_VALUES: 토큰 재발급이 필요한 응답 error codes(ex: O0001,O0002,O0003) + * TOKEN_ERROR_HTTP_STATUS_CODE: 보통 400대에서 체크하나 API 제공자에 따라 200에서도 체클할수 있음 + * ex) TOKEN_ERROR_HTTP_STATUS_CODE = 200 + * @formatter:on + */ + boolean needReissue = false; + + Properties responseHeaderProp = assignRelayDataToInbound(tempProp, responseHeaders); + String responseString = new String(responseMessage, vo.getEncode()); + + Map map = new HashMap(); + tempProp.put(HttpAdapterServiceKey.OUTBOUND_PROPERTY_MAP, map); + map.put(HttpAdapterServiceKey.OUTBOUND_RESPONSE_HEADERS, responseHeaderProp); + map.put(HttpAdapterServiceKey.OUTBOUND_RESPONSE_MESSAGE, responseString); + map.put(HttpAdapterServiceKey.OUTBOUND_RESPONSE_STATUS, status); + + if (status >= 200 && status <= 207) { + if (useAdapterToken && StringUtils.equals(tokenErrorHttpStatusCode, "200")) { + needReissue = checkTokenRetry(responseMessage, tokenErrorCodeKey, tokenErrorCodeValues, + vo.getEncode()); + } + } else if (status == 302) { + if (!StringUtils.contains(relayResponseHeaderKeys, "Location")) { + relayResponseHeaderKeys += ",Location"; + } + } else { + String errMsg = String.format( + "http receive status fail value=%d adapterName=%s.%s uri=%s method=%s response Data=%s", + status, vo.getAdapterGroupName(), vo.getAdapterName(), uri, rmethod, responseString); +// logger.error(errMsg); + + if (status >= 400 && status < 500) { + if (useAdapterToken) { + needReissue = checkTokenRetry(responseMessage, tokenErrorCodeKey, tokenErrorCodeValues, + vo.getEncode()); + } + +// if (!needReissue) { +// throw new Exception(errMsg); +// } + } else { + + throw new Exception(errMsg); + } + } + + // ---------------------------------------------------------- + if (logger.isDebug() && "N".equals(vo.getTestCallYn())) { + logger.debug("HttpClientAdapterServiceRest] responseType =" + vo.getResponseType()); + logger.debug("HttpClientAdapterServiceRest] RECV " + vo.getEncode() + "(" + vo.getAdapterGroupName() + ") = [" + new String(responseMessage, vo.getEncode()) + "]"); +// logger.debug("HttpClientAdapterServiceRest] RECV MS949 (" + vo.getAdapterGroupName() + ") = [" + new String(responseMessage, "MS949") + "]"); +// logger.debug("HttpClientAdapterServiceRest] RECV euc-kr (" + vo.getAdapterGroupName() + ") = [" + new String(responseMessage, "euc-kr") + "]"); + logger.debug("HttpClientAdapterServiceRest] RECV (" + vo.getAdapterGroupName() + ") = " + CommonLib.getDumpMessage(responseMessage)); + } + + if (useAdapterToken) { + if (responseMessage == null) { + throw new Exception("responseMessage is NULL, HttpStatus=" + status); + } + + // OAuth 토큰 재요청 코드 확인 + if (needReissue) { + if (logger.isDebug() && "N".equals(vo.getTestCallYn())) { + logger.debug("HttpClientAdapterServiceRest] retry access token response code = [" + + vo.getResponseType() + "] message = [" + new String(responseMessage, vo.getEncode()) + + "]"); + } + + // 토큰 재발급 + AccessTokenManager tokenManager = AccessTokenManager.getInstance(); + String oldToken = accessToken == null ? null : accessToken.getAccessToken(); + OAuth2AccessTokenVO newaccessToken = (OAuth2AccessTokenVO) tokenManager + .retryAccessTokenVO(vo.getAdapterGroupName(), prop, oldToken); + method.setHeader("Authorization", newaccessToken.getAuthorization()); + + setAuthHeaders(method, newaccessToken); + + if (logger.isDebug() && "N".equals(vo.getTestCallYn())) { + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + + ") RETRY TOKEN = [" + newaccessToken + "]"); + } + + try (CloseableHttpResponse response = (CloseableHttpResponse) mclient.execute(method)) { + status = response.getCode(); + responseMessage = EntityUtils.toByteArray(response.getEntity()); + + if (logger.isDebug()) { + logger.debug("[received status]" + status); + logger.debug("HttpClientAdapterServiceRest] RECV (" + vo.getAdapterGroupName() + ") = [" + new String(responseMessage, vo.getEncode()) + "]"); + } + }catch (IOException e){ + throw new Exception("retry excuteMethod Exceptioin = " + e.getMessage()); + } + + + if (status == 302) { + if (!StringUtils.contains(relayResponseHeaderKeys, "Location")) { + relayResponseHeaderKeys += ",Location"; + } + } else { + String errMsg = String.format( + "http receive status fail value=%d adapterName=%s.%s uri=%s method=%s response Data=%s", + status, vo.getAdapterGroupName(), vo.getAdapterName(), uri, rmethod, responseMessage); + throw new Exception(errMsg); + } + + // ---------------------------------------------------------- + if (logger.isDebug() && "N".equals(vo.getTestCallYn())) { + logger.debug("HttpClientAdapterServiceRest] RETRY responseType =" + vo.getResponseType()); + logger.debug("HttpClientAdapterServiceRest] RETRY RECV " + vo.getEncode() + "(" + + vo.getAdapterGroupName() + ") = [" + new String(responseMessage, vo.getEncode()) + + "]"); + logger.debug("HttpClientAdapterServiceRest] RETRY RECV (" + vo.getAdapterGroupName() + ") = " + + CommonLib.getDumpMessage(responseMessage)); + } + // ---------------------------------------------------------- + } + } + + if (vo.getTraceLevel() >= 3) { + HttpMemoryLogger.txlog(vo.getAdapterGroupName() + vo.getAdapterName(), + "RECV [" + new String(responseMessage, vo.getEncode()) + "]" + + CommonLib.getDumpMessage(responseMessage)); + } + + + return assignResponseHeaders(method, responseMessage, headerGroupName, vo.getEncode(), messageType, + relayResponseHeaderKeys, status, responseHeaderProp); + } catch (SocketTimeoutException ste) { // Read Timeout :: HttpClient 3.1일 경우 + if (vo.getTraceLevel() >= 3) { + HttpMemoryLogger.error(vo.getAdapterGroupName() + vo.getAdapterName(), + "HttpClientAdapterServiceRest] SocketTimeoutException (" + vo.getAdapterGroupName() + ") : " + + ste.toString(), + ste); + } + logger.error("HttpClientAdapterServiceRest] SocketTimeoutException (" + vo.getAdapterGroupName() + ") : " + + ste.toString(), ste); + throw ste; + } catch (ConnectException ce) { + if (vo.getTraceLevel() >= 3) { + HttpMemoryLogger.error(vo.getAdapterGroupName() + vo.getAdapterName(), + "HttpClientAdapterServiceRest] Connection Exception (" + vo.getAdapterGroupName() + ") : " + + ce.toString(), + ce); + } + logger.error("HttpClientAdapterServiceRest] Connection Exception (" + vo.getAdapterGroupName() + ") : " + + ce.toString(), ce); + throw ce; + } catch (Exception e) { + if (vo.getTraceLevel() >= 3) { + HttpMemoryLogger.error(vo.getAdapterGroupName() + vo.getAdapterName(), e.toString(), e); + } + logger.error("HttpClientAdapterServiceRest] Exception (" + vo.getAdapterGroupName() + ") : " + e.toString(), + e); + throw e; + } finally { + if (status != HttpStatus.SC_OK) { + String[] msgArgs = new String[1]; + msgArgs[0] = String.valueOf(status); + String resMsg = ExceptionUtil.make("RDCEAIAHA013", msgArgs); + if (logger.isDebug()) { + logger.debug(resMsg); + } + } + } + } + + private Properties assignRelayDataToInbound(Properties prop, Header[] responseHeaders) { + Properties headerProp = new Properties(); + if (responseHeaders == null || responseHeaders.length == 0) { + return headerProp; + } + + for (Header header : responseHeaders) { + headerProp.put(header.getName(), header.getValue()); + } + + return headerProp; + } + + /** + * 현재 1단계 레이아웃에만 적용되도록 구현됨. + * + * @param method + * @param responseMessage + * @param headerGroupName + * @param encode + * @param messageType + * @param relayHeaderKeys + * @return + * @throws Exception + */ + @SuppressWarnings("unchecked") + private String assignResponseHeaders(HttpUriRequestBase method, byte[] responseMessage, String headerGroupName, + String encode, String messageType, String relayHeaderKeys, int status, Properties responseHeaderProp) throws Exception { + if (!MessageType.JSON.equals(messageType) || StringUtils.isBlank(headerGroupName) + || StringUtils.isBlank(relayHeaderKeys)) { + return new String(responseMessage, encode); + } + + String[] relayKeyArr = org.springframework.util.StringUtils.tokenizeToStringArray(relayHeaderKeys, ","); + JSONObject headerJson = new JSONObject(); + for (String key : relayKeyArr) { + String value = responseHeaderProp.getProperty(key); + if (StringUtils.isNotBlank(value)) { + headerJson.put(key, value); + } + } + + headerJson.put(HTTP_STATUS, String.valueOf(status)); + + if (headerJson.size() <= 0) { + return new String(responseMessage, encode); + } + + JSONObject message = null; + if (responseMessage == null || responseMessage.length == 0) { + message = new JSONObject(); + } else { + message = parseJson(new String(responseMessage, encode)); + if (message == null) { + message = new JSONObject(); + message.put("Malformed_Response_Message", new String(responseMessage, encode)); + } + } + + if (StringUtils.isNotBlank(headerGroupName) && message != null) + message.put(headerGroupName, headerJson); + + logger.info("------------- message --------------- : {} ", message); + + return message.toJSONString(); + } + + // 2025.04.09 Authorization 헤더값을 프로퍼티(AUTH_TOKEN)에서 지정하여 설정할 수 있도록 수정 + private void setAuthHeaders(HttpUriRequestBase method, String authorization) throws Exception { + method.setHeader("Authorization", + String.format("%s", authorization)); + } + + private void setAuthHeaders(HttpUriRequestBase method, OAuth2AccessTokenVO accessToken) throws Exception { + method.setHeader("Authorization", + String.format("%s %s", AccessTokenVO.BEARER_TYPE, accessToken.getAccessToken())); + } + + private boolean checkTokenRetry(byte[] responseMessage, String tokenErrorCodeKey, String tokenErrorCodeValues, + String encode) { + if (responseMessage == null) { + return false; + } + + if (StringUtils.isBlank(tokenErrorCodeKey)) { + return false; + } + + if (StringUtils.isBlank(tokenErrorCodeValues)) { + return false; + } + + try { + DocumentContext jsonContext = JsonPath.parse(new String(responseMessage, encode)); + String responseCode = jsonContext.read(tokenErrorCodeKey); + if (StringUtils.isBlank(responseCode)) { + return false; + } + + String[] arr = org.springframework.util.StringUtils.tokenizeToStringArray(tokenErrorCodeValues, ","); + return ArrayUtils.contains(arr, responseCode); + } catch (Exception e) { + logger.error("HttpClientAdapterServiceRest] checkTokenRetry error=" + e.getMessage()); + } + + return false; + } + + private void assignRequestHeaders(HttpUriRequestBase method, Object httpHeader) { + if (httpHeader == null) { + return; + } + + if (httpHeader instanceof JSONObject) { + JSONObject jsonObject = (JSONObject) httpHeader; + for (Object key : jsonObject.keySet()) { + Object obj = jsonObject.get(key); + if ((obj instanceof JSONObject) || (obj instanceof JSONArray)) { + continue; + } + + method.setHeader((String) key, (String) obj); + + if (logger.isDebugEnabled()) { + logger.debug("Request Header :" + (String) key + "=[" + (String) obj + "]"); + } + + } + } + } + + @SuppressWarnings("deprecation") + private void assignPostBody(Object eaiBody, String contentType, String charset, HttpUriRequestBase method) { + if (eaiBody == null) { + return; + } + + if (StringUtils.contains(contentType, "application/x-www-form-urlencoded")) { + if (eaiBody instanceof JSONObject) { + List params = new ArrayList<>(); + JSONObject jsonObject = (JSONObject) eaiBody; + for (Object key : jsonObject.keySet()) { + params.add(new BasicNameValuePair((String) key, (String) jsonObject.get(key))); + } + + UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, StandardCharsets.UTF_8); + method.setEntity(entity); + } + + } else { + ContentType contentTypeObj = ContentType.create(contentType, charset); + + // 요청 본문을 StringEntity 객체로 생성 + StringEntity entity = new StringEntity(eaiBody.toString(), contentTypeObj); + + // 요청에 본문 추가 + method.setEntity(entity); + } + } + + private HashMap getParameters(Object message) throws Exception { + + HashMap result = new HashMap(); + + if (message != null) { + if (message instanceof JSONObject) { + JSONObject jsonObject = (JSONObject) message; + for (Object key : jsonObject.keySet()) { + Object obj = jsonObject.get(key); + if ((obj instanceof JSONObject) || (obj instanceof JSONArray)) { + continue; + } + result.put((String) key, getStringValue(obj)); + } + } else { + String[] messages = ((String) message).split("&"); + String[] data = null; + + for (int i = 0; i < messages.length; i++) { + data = messages[i].split("=", 2); + if (data.length == 2) { + result.put(data[0], data[1]); + } + } + } + } + + return result; + } + + public String getStringValue(Object obj) { + if (obj == null) { + return null; + } + + if (obj instanceof String) { + return (String) obj; + } else if (obj instanceof Long) { + return Long.toString((Long) obj); + } else if (obj instanceof BigDecimal) { + return ((BigDecimal) obj).toPlainString(); + } else { + return (String) obj; + } + } + + public static Object parseJsonGeneric(String jsonData) { + if (StringUtils.isBlank(jsonData)) { + return null; + } + + try { + JSONParser parser = new JSONParser(); + Object parsedObj = parser.parse(jsonData); + return parsedObj; + } catch (ParseException e) { + e.printStackTrace(); + return null; + } + } + + @SuppressWarnings("deprecation") + private JSONObject parseJson(String message) throws Exception { + if (message == null) { + return null; + } + + return (JSONObject) JSONValue.parse(message); + } + + private Document convertXmlDocument(String message) throws Exception { + SAXReader builder = new SAXReader(); + Document document = builder.read(new StringReader(message)); + return document; + } + + @SuppressWarnings("deprecation") + private String changeUrl(String messageType, String url, String restOption, Object sendData) { + if ((MessageType.JSON.equals(messageType) || MessageType.XML.equals(messageType))) { + try { + if (StringUtils.isBlank(restOption)) { + return url; + } + List urlVariableList = new ArrayList(); + JSONObject restOptionObject = parseJson(restOption); + + if (restOptionObject == null) + throw new Exception("restOption is NULL"); + + String type = (String) restOptionObject.get("type"); + String requestExtraPath = (String) restOptionObject.get("extraPath"); + url = getUrl(url, requestExtraPath); + if (TYPE_VARIABLE_URL_REQUEST.equals(type)) { + Pattern p = Pattern.compile("\\{(.*?)\\}"); + Matcher m = p.matcher(requestExtraPath); + List uriVariables = new ArrayList<>(); + while(m.find()) { + String fieldName = m.group(1); + uriVariables.add(fieldName); + } + if (MessageType.JSON.equals(messageType)) { + JSONObject jsonObject; + if (sendData instanceof JSONObject) { + jsonObject = (JSONObject) sendData; + } else { + jsonObject = (JSONObject) JSONValue.parse((String) sendData); + } + for (Object tempObject : uriVariables) { + String urlVaribleId = (String) tempObject; + String uriVariable = (String) jsonObject.get(urlVaribleId); + urlVariableList.add(uriVariable); + jsonObject.remove(urlVaribleId); + } + } else if (MessageType.XML.equals(messageType)) { + Document doc; + if (sendData instanceof Document) { + doc = (Document) sendData; + } else { + doc = convertXmlDocument((String) sendData); + } + for (Object tempObject : uriVariables) { + String urlVaribleId = (String) tempObject; + Element element = (Element) doc.selectSingleNode("//" + urlVaribleId); + urlVariableList.add(element.getText()); + if (doc.getRootElement() == element) { + doc = null; + } else { + element.getParent().remove(element); + } + } + } + if (urlVariableList.size() > 0) { + UriComponents uriComponents = UriComponentsBuilder.fromUriString(url).build(); + Object[] urls = urlVariableList.toArray(); + url = uriComponents.expand(urls).toUriString(); + logger.debug("HttpClientAdapterServiceRest] after extand url=[" + url + "] "); + } + } + } catch (Exception e) { + logger.error("Failed to change url", e); + } + } + return url; + } + + private String getUrl(String baseUrl, String extraPath) { + if (StringUtils.isBlank(extraPath)) { + return baseUrl; + } + + if (StringUtils.contains(extraPath, "http://") || StringUtils.contains(extraPath, "https://")) { + return extraPath; + } + + String targetUrl = baseUrl; + if (!targetUrl.endsWith("/")) { + targetUrl += "/"; + } + if (extraPath.startsWith("/")) { + targetUrl += extraPath.substring(1); + } else { + targetUrl += extraPath; + } + + logger.debug("HttpClientAdapterServiceRest] after concatenate url=[" + targetUrl + "] "); + return targetUrl; + } + +} diff --git a/src/main/java/com/eactive/eai/adapter/http/client/impl/kjb/HttpClient5AdapterServiceBypass.java b/src/main/java/com/eactive/eai/adapter/http/client/impl/kjb/HttpClient5AdapterServiceBypass.java new file mode 100644 index 0000000..a89eeb7 --- /dev/null +++ b/src/main/java/com/eactive/eai/adapter/http/client/impl/kjb/HttpClient5AdapterServiceBypass.java @@ -0,0 +1,680 @@ +package com.eactive.eai.adapter.http.client.impl.kjb; + +import java.io.IOException; +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Formatter; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.StopWatch; +import org.apache.hc.client5.http.classic.HttpClient; +import org.apache.hc.client5.http.classic.methods.HttpDelete; +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.classic.methods.HttpPost; +import org.apache.hc.client5.http.classic.methods.HttpPut; +import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; +import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.HttpStatus; +import org.apache.hc.core5.http.io.entity.ByteArrayEntity; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.hc.core5.http.protocol.BasicHttpContext; +import org.apache.hc.core5.http.protocol.HttpContext; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.web.util.UriComponents; +import org.springframework.web.util.UriComponentsBuilder; + +import com.eactive.eai.adapter.http.HttpMemoryLogger; +import com.eactive.eai.adapter.http.HttpMethodType; +import com.eactive.eai.adapter.http.client.HttpClient5AdapterServiceSupport; +import com.eactive.eai.adapter.http.client.HttpClientAdapterServiceKey; +import com.eactive.eai.adapter.http.client.HttpClientAdapterVO; +import com.eactive.eai.adapter.http.client.impl.HttpClientAdapterServiceRest; +import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey; +import com.eactive.eai.adapter.http.dynamic.impl.HttpAdapterServiceBypass; +import com.eactive.eai.common.TransactionContextKeys; +import com.eactive.eai.common.exception.ExceptionUtil; +import com.eactive.eai.common.util.CommonLib; +import com.jayway.jsonpath.DocumentContext; +import com.jayway.jsonpath.JsonPath; +import com.openbanking.eai.common.token.AccessTokenManager; +import com.openbanking.eai.common.token.AccessTokenVO; +import com.openbanking.eai.common.token.OAuth2AccessTokenVO; + +public class HttpClient5AdapterServiceBypass extends HttpClient5AdapterServiceSupport + implements HttpClientAdapterServiceKey { + public static final String TYPE_BYPASS_REQUEST = "bypassRequest"; + public static final String REST_OPTION = "REST_OPTION"; + + protected boolean doSendUrlFragment = true; + protected boolean doHandleCompression = false; + protected boolean doForwardIP = false; + + @SuppressWarnings({ "unchecked" }) + public Object execute(Properties prop, Object data, Properties tempProp) throws Exception { + HttpClientAdapterVO vo = super.setting(prop, tempProp); + + boolean useAdapterToken = StringUtils.equalsIgnoreCase(prop.getProperty("ADAPTER_TOKEN_USE_YN", "N"), "Y"); + + // ex) $.dataHeader.GW_RSLT_CD + String tokenErrorCodeKey = prop.getProperty("ADAPTER_TOKEN_ERROR_CODE_KEY"); + String tokenErrorCodeValues = prop.getProperty("ADAPTER_TOKEN_ERROR_CODE_VALUES"); + String tokenErrorHttpStatusCode = prop.getProperty("ADAPTER_TOKEN_ERROR_HTTP_STATUS_CODE"); // 200 or 400번대 + + /** + * @formatter:off + * TSEAIHE02.RESTOPTION 정보 => JSON 형태로 구성 + * - type : simpleRequest, variableUrlRequest + * - extraPath : 어댑터 프로퍼티 URL에 추가될 HTTP REST URL + * - method : get, delete, post, put + * - contentType : application/json, application/x-www-form-urlencoded + * - adapterTokenUseYn: Y, N(default) + * ex) + * {"type":"variableUrlRequest","extraPath":"/aaa/{userId}","uriVariables":["userId"]} + * @formatter:on + */ + String restOptionData = tempProp.getProperty(REST_OPTION, "{}"); + + JSONObject restOptionObject = parseJson(restOptionData); + + String adapterTokenUseYn = (String) restOptionObject.get("adapterTokenUseYn"); + if (StringUtils.isNotBlank(adapterTokenUseYn)) { // interface 에 설정된게 우선한다. + useAdapterToken = StringUtils.equalsIgnoreCase(adapterTokenUseYn, "Y"); + } + + String inboundMethod = tempProp.getProperty(HttpAdapterServiceKey.INBOUND_METHOD, "POST"); + String inboundRewritePath = tempProp.getProperty(HttpAdapterServiceKey.INBOUND_REWRITE_PATH, ""); + String inboundQueryString = tempProp.getProperty(HttpAdapterServiceKey.INBOUND_QUERY_STRING, ""); + Properties inboundHeaders = (Properties) tempProp.get(HttpAdapterServiceKey.INBOUND_HEADER); + Map inboundPathVariables = (Map) tempProp + .get(HttpAdapterServiceKey.INBOUND_PATH_VARIABLES); + + String restMethod = (String) restOptionObject.get("method"); + if (StringUtils.isBlank(restMethod)) { + restMethod = inboundMethod; + } + + String url = getRewriteUrl(vo, inboundRewritePath, inboundQueryString, restOptionObject, inboundPathVariables); + if (logger.isDebug()) { + logger.debug("HttpClient5AdapterServiceBypass] url = [" + url + "]"); + } + + HttpClient mclient = this.client; + HttpUriRequestBase method = generateMethod(restMethod, url); + + assignRequestHeaders(method, inboundHeaders, tempProp); + + if (hasBody(data, method)) { + byte[] bodyBytes; + if (data instanceof byte[]) { + bodyBytes = (byte[]) data; + } else if (data instanceof String) { + bodyBytes = ((String) data).getBytes(vo.getEncode()); + } else { + bodyBytes = new byte[0]; + } + method.setEntity(new ByteArrayEntity(bodyBytes, null)); + } + + String contentType = (String) restOptionObject.get("contentType"); + if (StringUtils.isBlank(contentType) && inboundHeaders != null) { + contentType = getIgnoreCaseProp(inboundHeaders, HttpHeaders.CONTENT_TYPE); + contentType = StringUtils.substringBefore(contentType, ";"); + } + + RequestConfig.Builder requestConfigBuilder = RequestConfig.custom(); + configureHttpClient(requestConfigBuilder, vo, method); + + OAuth2AccessTokenVO accessToken = null; + if (useAdapterToken) { + AccessTokenManager tokenManager = AccessTokenManager.getInstance(); + accessToken = (OAuth2AccessTokenVO) tokenManager.getAccessTokenVO(vo.getAdapterGroupName()); + + // 토큰이 없거나 만료 됐으면 재발급 + if (accessToken == null || accessToken.isExpired()) { + String oldToken = accessToken == null ? null : accessToken.getAccessToken(); + accessToken = (OAuth2AccessTokenVO) tokenManager.retryAccessTokenVO(vo.getAdapterGroupName(), prop, + oldToken); + } + + if (logger.isDebug()) { + logger.debug("HttpClient5AdapterServiceBypass] SEND (" + vo.getAdapterGroupName() + ") TOKEN = [" + + accessToken + "]"); + } + + setAuthHeaders(method, accessToken); + + if (logger.isDebug()) { + logger.debug("HttpClient5AdapterServiceBypass] SEND (" + vo.getAdapterGroupName() + + ") RequestHeader [Authorization=" + method.getFirstHeader("Authorization") + "]"); + } + } + + // 로깅 인터셉터에 전달할 컨텍스트 정보 설정 + String uuid = tempProp.getProperty(TransactionContextKeys.TRANSACTION_UUID); + HttpContext context = new BasicHttpContext(); + context.setAttribute(TransactionContextKeys.TRANSACTION_UUID, uuid); + context.setAttribute(HttpClientAdapterServiceKey.ADAPTER_GROUP_NAME, vo.getAdapterGroupName()); + context.setAttribute(HttpClientAdapterServiceKey.ADAPTER_NAME, vo.getAdapterName()); + Integer logProcessNo = (Integer) tempProp.get(HttpClientAdapterServiceKey.LOG_PROCESS_NO); + if (logProcessNo != null && logProcessNo > 0) { + context.setAttribute(HttpClientAdapterServiceKey.LOG_PROCESS_NO, logProcessNo); + } + + int status = -1; + + try { + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + + byte[] responseMessage = null; + Header[] responseHeaders = null; + + if (vo.getTraceLevel() >= 3) { + HttpMemoryLogger.txlog(vo.getAdapterGroupName() + vo.getAdapterName(), + "SEND [Bypass Request..]" + CommonLib.getDumpMessage(data)); + } + try { + if (logger.isDebug()) { + logger.debug("[method getName]" + method.getMethod()); + logger.debug("[method getRequestHeaders]" + java.util.Arrays.toString(method.getHeaders())); + logger.debug("[method getURI]" + method.getPath()); + } + try (CloseableHttpResponse response = (CloseableHttpResponse) mclient.execute(method, context)) { + status = response.getCode(); + responseHeaders = response.getHeaders(); + responseMessage = EntityUtils.toByteArray(response.getEntity()); + if (logger.isDebug()) { + logger.debug("[received status]" + status); + } + } + } catch (ConnectException e) { + if (logger.isDebug() && "N".equals(vo.getTestCallYn())) { + logger.debug("HttpClient5AdapterServiceBypass] responseType =" + vo.getResponseType()); + logger.debug("HttpClient5AdapterServiceBypass] RECV (" + vo.getAdapterGroupName() + ") = " + + CommonLib.getDumpMessage(responseMessage)); + } + throw new Exception("excuteMethod java.net.ConnectException = " + e.getMessage()); + } + + stopWatch.stop(); + + /** + * @formatter:off + * OAuth 토큰 응답 체크(Adapter properties로 설정) + * 응답코드에 따라 유효한 토큰 확인(토큰 재발급 여부 확인) + * API 서비스 마다 정책이 다름(보통 400대에서 체크하나 200에서도 체크할 수 있음) + * + * TOKEN_ERROR_CODE_KEY: 응답 json error code key + * __ex) $.dataHeader.resultCode + * TOKEN_ERROR_CODE_VALUES: 토큰 재발급이 필요한 응답 error codes(ex: O0001,O0002,O0003) + * TOKEN_ERROR_HTTP_STATUS_CODE: 보통 400대에서 체크하나 API 제공자에 따라 200에서도 체클할수 있음 + * ex) TOKEN_ERROR_HTTP_STATUS_CODE = 200 + * @formatter:on + */ + boolean needReissue = false; + + if (status == 200) { + if (useAdapterToken && StringUtils.equals(tokenErrorHttpStatusCode, "200")) { + needReissue = checkTokenRetry(responseMessage, tokenErrorCodeKey, tokenErrorCodeValues, + vo.getEncode()); + } + } else if (status >= 400 && status < 500) { + if (useAdapterToken) { + needReissue = checkTokenRetry(responseMessage, tokenErrorCodeKey, tokenErrorCodeValues, + vo.getEncode()); + } + + if (!needReissue) { + // body 값이 없을때만 exception 처리하고, body가 있으면 bypass 한다. + if (responseMessage == null || responseMessage.length == 0) { + String errMsg = String.format( + "http receive status fail value=%d adapterName=%s.%s uri=%s method=%s response Data=%s", + status, vo.getAdapterGroupName(), vo.getAdapterName(), url, restMethod, responseMessage); + throw new Exception(errMsg); + } + } + } else if (status != 200) { + responseMessage = responseMessage != null ? responseMessage : new byte[0]; + // body 값이 없을때만 exception 처리하고, body가 있으면 bypass 한다. + if (responseMessage.length == 0) { + String errMsg = String.format( + "http receive status fail value=%d adapterName=%s.%s uri=%s method=%s response Data=%s", + status, vo.getAdapterGroupName(), vo.getAdapterName(), url, restMethod, responseMessage); + throw new Exception(errMsg); + } + } + + if (useAdapterToken) { + if (responseMessage == null) { + throw new Exception("responseMessage is NULL, HttpStatus=" + status); + } + + // OAuth 토큰 재요청 코드 확인 + if (needReissue) { + if (logger.isDebug() && "N".equals(vo.getTestCallYn())) { + logger.debug("HttpClient5AdapterServiceBypass] retry access token response code = [" + + vo.getResponseType() + "] message = [" + new String(responseMessage, vo.getEncode()) + + "]"); + } + + // 토큰 재발급 + AccessTokenManager tokenManager = AccessTokenManager.getInstance(); + String oldToken = accessToken == null ? null : accessToken.getAccessToken(); + OAuth2AccessTokenVO newAccessToken = (OAuth2AccessTokenVO) tokenManager + .retryAccessTokenVO(vo.getAdapterGroupName(), prop, oldToken); + method.setHeader("Authorization", newAccessToken.getAuthorization()); + setAuthHeaders(method, newAccessToken); + + if (logger.isDebug() && "N".equals(vo.getTestCallYn())) { + logger.debug("HttpClient5AdapterServiceBypass] SEND (" + vo.getAdapterGroupName() + + ") RETRY TOKEN = [" + newAccessToken + "]"); + } + + try (CloseableHttpResponse retryResponse = (CloseableHttpResponse) mclient.execute(method, context)) { + status = retryResponse.getCode(); + responseHeaders = retryResponse.getHeaders(); + responseMessage = EntityUtils.toByteArray(retryResponse.getEntity()); + } catch (IOException e) { + throw new Exception("retry excuteMethod Exception = " + e.getMessage()); + } + + if (status != 200) { + responseMessage = responseMessage != null ? responseMessage : new byte[0]; + if (responseMessage.length == 0) { + String errMsg = String.format( + "http receive status fail value=%d adapterName=%s.%s uri=%s method=%s response Data=%s", + status, vo.getAdapterGroupName(), vo.getAdapterName(), url, restMethod, + responseMessage); + throw new Exception(errMsg); + } + } + + if (logger.isDebug() && "N".equals(vo.getTestCallYn())) { + logger.debug("HttpClient5AdapterServiceBypass] RETRY responseType =" + vo.getResponseType()); + logger.debug("HttpClient5AdapterServiceBypass] RETRY RECV (" + vo.getAdapterGroupName() + ") = " + + CommonLib.getDumpMessage(responseMessage)); + } + } + } + + if (vo.getTraceLevel() >= 3) { + HttpMemoryLogger.txlog(vo.getAdapterGroupName() + vo.getAdapterName(), + "RECV [Bypass Request..]" + CommonLib.getDumpMessage(responseMessage)); + } + // 응답 Content-Type charset 기반 인코딩 결정, 없으면 어댑터 encoding 사용 + String responseEncode = vo.getEncode(); + if (responseHeaders != null) { + for (Header header : responseHeaders) { + if (StringUtils.equalsIgnoreCase(header.getName(), HttpHeaders.CONTENT_TYPE)) { + try { + MediaType mediaType = MediaType.parseMediaType(header.getValue()); + if (mediaType.getCharset() != null) { + responseEncode = mediaType.getCharset().name(); + } + } catch (Exception ignored) {} + break; + } + } + } + assignRelayDataToInbound(tempProp, responseHeaders, status); + return responseMessage != null ? new String(responseMessage, responseEncode) : ""; + } catch (SocketTimeoutException ste) { + if (vo.getTraceLevel() >= 3) { + HttpMemoryLogger.error(vo.getAdapterGroupName() + vo.getAdapterName(), + "HttpClient5AdapterServiceBypass] SocketTimeoutException (" + vo.getAdapterGroupName() + ") : " + + ste.toString(), + ste); + } + logger.error("HttpClient5AdapterServiceBypass] SocketTimeoutException (" + vo.getAdapterGroupName() + ") : " + + ste.toString(), ste); + throw ste; + } catch (ConnectException ce) { + if (vo.getTraceLevel() >= 3) { + HttpMemoryLogger.error(vo.getAdapterGroupName() + vo.getAdapterName(), + "HttpClient5AdapterServiceBypass] Connection Exception (" + vo.getAdapterGroupName() + ") : " + + ce.toString(), + ce); + } + logger.error("HttpClient5AdapterServiceBypass] Connection Exception (" + vo.getAdapterGroupName() + ") : " + + ce.toString(), ce); + throw ce; + } catch (Exception e) { + if (vo.getTraceLevel() >= 3) { + HttpMemoryLogger.error(vo.getAdapterGroupName() + vo.getAdapterName(), e.toString(), e); + } + logger.error( + "HttpClient5AdapterServiceBypass] Exception (" + vo.getAdapterGroupName() + ") : " + e.toString(), + e); + throw e; + } finally { + if (status != HttpStatus.SC_OK) { + String[] msgArgs = new String[1]; + msgArgs[0] = String.valueOf(status); + String resMsg = ExceptionUtil.make("RDCEAIAHA013", msgArgs); + if (logger.isDebug()) { + logger.debug(resMsg); + } + } + } + } + + private boolean hasBody(Object data, HttpUriRequestBase method) { + if (data == null) { + return false; + } + // GET, DELETE는 표준 HTTP에서 body를 지원하지 않음 + return method instanceof HttpPost || method instanceof HttpPut; + } + + private HttpUriRequestBase generateMethod(String restMethod, String url) { + switch (HttpMethodType.getValue(restMethod)) { + case GET: + return new HttpGet(url); + case DELETE: + return new HttpDelete(url); + case POST: + return new HttpPost(url); + case PUT: + return new HttpPut(url); + default: + return new HttpPost(url); + } + } + + private String getRewriteUrl(HttpClientAdapterVO vo, String inboundRewritePath, String queryString, + JSONObject restOptionObject, Map inboundPathVariables) { + String fragment = null; + if (StringUtils.isNotBlank(queryString)) { + int fragIdx = queryString.indexOf('#'); + if (fragIdx >= 0) { + fragment = queryString.substring(fragIdx + 1); + queryString = queryString.substring(0, fragIdx); + } + } + + String restExtraPath = (String) restOptionObject.get("extraPath"); + if (StringUtils.isBlank(restExtraPath)) { + StringBuilder uri = new StringBuilder(500); + String baseUrl = StringUtils.removeEnd(vo.getUrl(), "/"); + String rewritePath = inboundRewritePath != null && !inboundRewritePath.startsWith("/") + ? "/" + inboundRewritePath : inboundRewritePath; + uri.append(baseUrl).append(StringUtils.defaultString(rewritePath)); + + if (StringUtils.isNotBlank(queryString)) { + uri.append('?'); + uri.append(encodeUriQuery(queryString, false)); + } + + if (doSendUrlFragment && fragment != null) { + uri.append('#'); + uri.append(encodeUriQuery(fragment, false)); + } + + return uri.toString(); + } else { + String type = (String) restOptionObject.get("type"); + String url = getUrl(vo.getUrl(), restExtraPath); + + if (StringUtils.equalsIgnoreCase(type, HttpClientAdapterServiceRest.TYPE_VARIABLE_URL_REQUEST)) { + inboundPathVariables = mergeInboundPathVariables(inboundPathVariables, queryString); + List urlVariableValueList = new ArrayList<>(); + JSONArray uriVariables = (JSONArray) restOptionObject.get("uriVariables"); + if (uriVariables != null && !uriVariables.isEmpty() && inboundPathVariables != null + && !inboundPathVariables.isEmpty()) { + for (Object tempObject : uriVariables) { + String urlVaribleId = (String) tempObject; + String uriVariableValue = inboundPathVariables.get(urlVaribleId); + urlVariableValueList.add(uriVariableValue); + } + + if (!urlVariableValueList.isEmpty()) { + UriComponents uriComponents = UriComponentsBuilder.fromUriString(url).build(); + Object[] urls = urlVariableValueList.toArray(); + url = uriComponents.expand(urls).toUriString(); + } + } + } else { + if (StringUtils.isNotBlank(queryString)) { + url += "?" + encodeUriQuery(queryString, false); + } + + if (doSendUrlFragment && fragment != null) { + url += "#" + encodeUriQuery(fragment, false); + } + } + + return url; + } + } + + private Map mergeInboundPathVariables(Map inboundPathVariables, + String queryString) { + if (StringUtils.isBlank(queryString)) { + return inboundPathVariables; + } + + if (inboundPathVariables == null) { + inboundPathVariables = new LinkedHashMap<>(); + } + + String[] pairs = queryString.split("&"); + for (String pair : pairs) { + int idx = pair.indexOf("="); + inboundPathVariables.put(pair.substring(0, idx), pair.substring(idx + 1)); + } + + return inboundPathVariables; + } + + private String getUrl(String baseUrl, String extraPath) { + if (StringUtils.isBlank(extraPath)) { + return baseUrl; + } + + if (StringUtils.contains(extraPath, "http://") || StringUtils.contains(extraPath, "https://")) { + return extraPath; + } + + String targetUrl = baseUrl; + if (!targetUrl.endsWith("/")) { + targetUrl += "/"; + } + if (extraPath.startsWith("/")) { + targetUrl += extraPath.substring(1); + } else { + targetUrl += extraPath; + } + + return targetUrl; + } + + protected CharSequence encodeUriQuery(CharSequence in, boolean encodePercent) { + StringBuilder outBuf = null; + Formatter formatter = null; + for (int i = 0; i < in.length(); i++) { + char c = in.charAt(i); + boolean escape = true; + if (c < 128) { + if (asciiQueryChars.get(c) && !(encodePercent && c == '%')) { + escape = false; + } + } else if (!Character.isISOControl(c) && !Character.isSpaceChar(c)) { + escape = false; + } + if (!escape) { + if (outBuf != null) + outBuf.append(c); + } else { + if (outBuf == null) { + outBuf = new StringBuilder(in.length() + 5 * 3); + outBuf.append(in, 0, i); + formatter = new Formatter(outBuf); + } + formatter.format("%%%02X", (int) c); + } + } + return outBuf != null ? outBuf : in; + } + + protected static final BitSet asciiQueryChars; + static { + char[] c_unreserved = "_-!.~'()*".toCharArray(); + char[] c_punct = ",;:$&+=".toCharArray(); + char[] c_reserved = "/@".toCharArray(); + asciiQueryChars = new BitSet(128); + for (char c = 'a'; c <= 'z'; c++) + asciiQueryChars.set(c); + for (char c = 'A'; c <= 'Z'; c++) + asciiQueryChars.set(c); + for (char c = '0'; c <= '9'; c++) + asciiQueryChars.set(c); + for (char c : c_unreserved) + asciiQueryChars.set(c); + for (char c : c_punct) + asciiQueryChars.set(c); + for (char c : c_reserved) + asciiQueryChars.set(c); + asciiQueryChars.set('%'); + } + + private void assignRelayDataToInbound(Properties prop, Header[] responseHeaders, int status) { + Properties headerProp = new Properties(); + if (responseHeaders != null) { + for (Header header : responseHeaders) { + String name = header.getName(); + String value = header.getValue(); + String existing = headerProp.getProperty(name); + if (existing != null) { + // 동일 이름의 헤더가 여러 개인 경우 콤마로 합침 (RFC 7230) + headerProp.put(name, existing + ", " + value); + } else { + headerProp.put(name, value); + } + } + } + + headerProp.put(HttpAdapterServiceBypass.HTTP_STATUS, String.valueOf(status)); + + Map map = new HashMap(); + map.put(HttpAdapterServiceKey.OUTBOUND_RESPONSE_HEADERS, headerProp); + + prop.put(HttpAdapterServiceKey.OUTBOUND_PROPERTY_MAP, map); + } + + private void setAuthHeaders(HttpUriRequestBase method, OAuth2AccessTokenVO accessToken) throws Exception { + method.setHeader("Authorization", + String.format("%s %s", AccessTokenVO.BEARER_TYPE, accessToken.getAccessToken())); + } + + private boolean checkTokenRetry(byte[] responseMessage, String tokenErrorCodeKey, String tokenErrorCodeValues, + String encode) { + if (responseMessage == null) { + return false; + } + + if (StringUtils.isBlank(tokenErrorCodeKey)) { + return false; + } + + if (StringUtils.isBlank(tokenErrorCodeValues)) { + return false; + } + + try { + DocumentContext jsonContext = JsonPath.parse(new String(responseMessage, encode)); + String responseCode = jsonContext.read(tokenErrorCodeKey); + if (StringUtils.isBlank(responseCode)) { + return false; + } + + String[] arr = org.springframework.util.StringUtils.tokenizeToStringArray(tokenErrorCodeValues, ","); + return ArrayUtils.contains(arr, responseCode); + } catch (Exception e) { + logger.error("HttpClient5AdapterServiceBypass] checkTokenRetry error=" + e.getMessage()); + } + + return false; + } + + private void assignRequestHeaders(HttpUriRequestBase method, Properties headerProp, Properties inProp) { + if (headerProp == null) { + return; + } + + for (Entry e : headerProp.entrySet()) { + String key = (String) e.getKey(); + String value = (String) e.getValue(); + + if (StringUtils.equalsAnyIgnoreCase(key, HttpAdapterServiceBypass.HOP_BY_HOP_HEADERS) + || StringUtils.equalsIgnoreCase(key, HttpHeaders.CONTENT_LENGTH)) { + continue; + } + + if (doHandleCompression && StringUtils.equalsIgnoreCase(key, HttpHeaders.ACCEPT_ENCODING)) { + continue; + } + + method.addHeader(key, value); + + if (logger.isDebugEnabled()) { + logger.debug("Request Header :" + key + "=[" + value + "]"); + } + } + + if (doForwardIP) { + String forHeaderName = "X-Forwarded-For"; + String forHeader = inProp.getProperty(HttpAdapterServiceKey.INBOUND_REMOTE_ADDR); + if (StringUtils.isNotBlank(forHeader)) { + String existingForHeader = headerProp.getProperty(forHeaderName); + if (existingForHeader != null) { + forHeader = existingForHeader + ", " + forHeader; + } + method.addHeader(forHeaderName, forHeader); + } + + String protoHeaderName = "X-Forwarded-Proto"; + String protoHeader = inProp.getProperty(HttpAdapterServiceKey.INBOUND_SCHEME); + if (StringUtils.isNotBlank(protoHeader)) { + method.addHeader(protoHeaderName, protoHeader); + } + } + } + + @SuppressWarnings("deprecation") + private JSONObject parseJson(String message) throws Exception { + if (message == null) { + return null; + } + + return (JSONObject) JSONValue.parse(message); + } + + public static final String getIgnoreCaseProp(Properties prop, String key) { + if (prop == null) { + return null; + } + + for (Entry e : prop.entrySet()) { + if (StringUtils.equalsIgnoreCase(key, (String) e.getKey())) { + return (String) e.getValue(); + } + } + + return null; + } +} diff --git a/src/main/java/com/eactive/eai/adapter/http/client/impl/kjb/HttpClient5AdapterServiceKbank.java b/src/main/java/com/eactive/eai/adapter/http/client/impl/kjb/HttpClient5AdapterServiceKbank.java new file mode 100644 index 0000000..a213f05 --- /dev/null +++ b/src/main/java/com/eactive/eai/adapter/http/client/impl/kjb/HttpClient5AdapterServiceKbank.java @@ -0,0 +1,888 @@ +package com.eactive.eai.adapter.http.client.impl.kjb; + +import java.io.IOException; +import java.io.StringReader; +import java.math.BigDecimal; +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.StopWatch; +import org.apache.hc.client5.http.classic.HttpClient; +import org.apache.hc.client5.http.classic.methods.HttpDelete; +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.classic.methods.HttpPost; +import org.apache.hc.client5.http.classic.methods.HttpPut; +import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; +import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.entity.UrlEncodedFormEntity; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.HttpHost; +import org.apache.hc.core5.http.HttpStatus; +import org.apache.hc.core5.http.NameValuePair; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.hc.core5.http.io.entity.StringEntity; +import org.apache.hc.core5.http.message.BasicNameValuePair; +import org.apache.hc.core5.http.protocol.BasicHttpContext; +import org.apache.hc.core5.http.protocol.HttpContext; +import org.apache.hc.core5.net.URIBuilder; +import org.dom4j.Document; +import org.dom4j.Element; +import org.dom4j.io.SAXReader; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import org.springframework.web.util.UriComponents; +import org.springframework.web.util.UriComponentsBuilder; + +import com.eactive.eai.adapter.http.HttpMemoryLogger; +import com.eactive.eai.adapter.http.HttpMethodType; +import com.eactive.eai.adapter.http.client.HttpClient5AdapterServiceSupport; +import com.eactive.eai.adapter.http.client.HttpClientAdapterServiceKey; +import com.eactive.eai.adapter.http.client.HttpClientAdapterVO; +import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey; +import com.eactive.eai.common.TransactionContextKeys; +import com.eactive.eai.common.authoutbound.AccessTokenManagerByDB; +import com.eactive.eai.common.exception.ExceptionUtil; +import com.eactive.eai.common.message.MessageType; +import com.eactive.eai.common.util.CommonLib; +import com.jayway.jsonpath.DocumentContext; +import com.jayway.jsonpath.JsonPath; +import com.openbanking.eai.common.token.AccessTokenManager; +import com.openbanking.eai.common.token.AccessTokenVO; +import com.openbanking.eai.common.token.OAuth2AccessTokenVO; + +import com.eactive.eai.common.property.PropManager; + +public class HttpClient5AdapterServiceKbank extends HttpClient5AdapterServiceSupport + implements HttpClientAdapterServiceKey { + + public static final String TYPE_VARIABLE_URL_REQUEST = "variableUrlRequest"; + public static final String TYPE_SIMPLE_REQUEST = "simpleRequest"; + public static final String REST_OPTION = "REST_OPTION"; + //public static final String HEADER_CONTENT_TYPE = "Content-Type"; + public static final String HEADER_GROUP = "HEADER_GROUP"; + public static final String HTTP_STATUS = "HTTP_STATUS"; + public static final String HEADER_KEYS = "HEADER_KEYS"; + public static final String AUTH_TOKEN = "AUTH_TOKEN"; + + /** + * 1. 기능 : REST API 통신에 사용
+ * 2. 처리 개요 : - 속성 정보를 설정 하고 수동 시스템 서비스를 호출 한다.
+ * 3. 주의사항
+ * + * @param prop Http Adapter 속성 정보 + * @return 반환 된 Object + * @exception Exception 수동 시스템 간 통신 중 발생 + **/ + @SuppressWarnings({ "unchecked" }) + public Object execute(Properties prop, Object data, Properties tempProp) throws Exception { + HttpClientAdapterVO vo = super.setting(prop, tempProp); + + // 2025.04.09 Authorization 헤더값을 프로퍼티(AUTH_TOKEN)에서 지정하여 설정할 수 있도록 수정 + String bizCode = ""; + String authToken = ""; + try { + Properties authProp = PropManager.getInstance().getProperties(AUTH_TOKEN); //Authorization + bizCode = vo.getAdapterGroupName().substring(1, 4); + authToken = authProp.getProperty(bizCode, ""); + } catch (Exception e) { + logger.debug("Property Group AUTH_TOKEN is not Exist !!"); + } + + logger.debug("authToken :::::::::::::::::::::::::::::::::::" + authToken); + + String headerGroupName = prop.getProperty(HEADER_GROUP); + String relayResponseHeaderKeys = prop.getProperty(HEADER_KEYS, ""); + boolean useAdapterToken = StringUtils.equalsIgnoreCase(prop.getProperty("ADAPTER_TOKEN_USE_YN", "N"), "Y"); + + boolean useForwardProxy = StringUtils.equalsIgnoreCase(prop.getProperty("FORWARD_PROXY_USE_YN", "N"), "Y"); + String forwardProxyUrl = prop.getProperty("FORWARD_PROXY_URL"); + + // ex) $.dataHeader.GW_RSLT_CD + String tokenErrorCodeKey = prop.getProperty("ADAPTER_TOKEN_ERROR_CODE_KEY"); + String tokenErrorCodeValues = prop.getProperty("ADAPTER_TOKEN_ERROR_CODE_VALUES"); + String tokenErrorHttpStatusCode = prop.getProperty("ADAPTER_TOKEN_ERROR_HTTP_STATUS_CODE"); // 200 or 400번대 + + // 레이아웃 메시지 타입 REST URL 추출 시 활용. Default JSON + String messageType = prop.getProperty("MESSAGE_TYPE", MessageType.JSON); + String inboundMethod = tempProp.getProperty(HttpAdapterServiceKey.INBOUND_METHOD, "POST"); + + /** + * @formatter:off + * TSEAIHE02.RESTOPTION 정보 => JSON 형태로 구성 + * - type : simpleRequest, variableUrlRequest + * - extraPath : 어댑터 프로퍼티 URL에 추가될 HTTP REST URL + * - method : get, delete, post, put + * - contentType: application/json(default), application/x-www-form-urlencoded + * - adapterTokenUseYn: Y, N(default) + * ex) + * {"type":"simpleRequest","extraPath":"v2.0/accout/balance","method":"post"} + * @formatter:on + */ + + String restOptionData = tempProp.getProperty(REST_OPTION); + + JSONObject restOptionObject = parseJson(restOptionData); + if (restOptionObject == null) { + throw new Exception("OptionData parsing result is NULL"); + } + + String rmethod = (String) restOptionObject.getOrDefault("method", inboundMethod); + String contentType = (String) restOptionObject.getOrDefault("contentType", "application/json"); + String adapterTokenUseYn = (String) restOptionObject.get("adapterTokenUseYn"); + if (StringUtils.isNotBlank(adapterTokenUseYn)) { // interface 에 설정된게 우선한다. + useAdapterToken = StringUtils.equalsIgnoreCase(adapterTokenUseYn, "Y"); + } + + HttpClient mclient = this.client; + String sendData = ""; + if (data instanceof String) { + sendData = (String) data; + } else if (data instanceof byte[]) { + sendData = new String((byte[]) data, vo.getEncode()); + } + + ////mclient.getParams().setContentCharset(vo.getEncode()); + + JSONObject dataObject = null; + + Object httpHeader = null; + Object dataContent = null; + + if (MessageType.JSON.equals(messageType)) { + + Object parsed = parseJsonGeneric(sendData); + + if(parsed instanceof JSONObject) { + dataObject = parseJson(sendData); + if (dataObject != null && StringUtils.isNotBlank(headerGroupName)) { + httpHeader = dataObject.get(headerGroupName); + dataObject.remove(headerGroupName); + } + + dataContent = dataObject; + if (dataObject.containsKey("innerList")) { + JSONArray innerList = (JSONArray) dataObject.get("innerList"); + dataContent = innerList; + } + } + } + + logger.debug("HttpClientAdapterServiceRest] dataObject1 = [" + dataObject + "]"); + logger.debug("HttpClientAdapterServiceRest] dataContent = [" + dataContent + "]"); + + // interface 에 설정된게 우선한다. + String uri = null; + if (dataObject == null) { + uri = changeUrl(messageType, vo.getUrl(), restOptionData, sendData); + } else { + uri = changeUrl(messageType, vo.getUrl(), restOptionData, dataObject); + } + +// ////if (vo.getConnectionTimeout() != 0) { +// mclient.getHttpConnectionManager().getParams().setConnectionTimeout(vo.getConnectionTimeout()); +// } + + if (logger.isDebug() && "N".equals(vo.getTestCallYn())) { + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") URL=[" + vo.getUrl() + "] "); + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") PARAMETER_NAME=[" + vo.getParameterName() + "] "); + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") ENCODE=[" + vo.getEncode() + "] "); + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") RESPONSE_TYPE=[" + vo.getResponseType() + "] "); + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") URL_ENCODE_YN=[" + vo.getUrlEncodeYn() + "] "); + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") CONNECTION_TIMEOUT=[" + vo.getConnectionTimeout() + "] "); + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") REST_OPTION=[" + restOptionData + "] "); + } + + HttpUriRequestBase method = null; + + // 메소드 타입에 따라 HttpRequestBase 인스턴스 생성 + switch (HttpMethodType.getValue(rmethod)) { + case GET: + method = new HttpGet(uri); + break; + case DELETE: + method = new HttpDelete(uri); + break; + case POST: + method = new HttpPost(uri); + break; + case PUT: + method = new HttpPut(uri); + break; + default: + method = new HttpGet(uri); + break; + } + + RequestConfig.Builder requestConfigBuilder = RequestConfig.custom(); + + if(useForwardProxy) { + java.net.URL url = new java.net.URL(forwardProxyUrl); + + // 프로토콜, 호스트, 포트 추출 + String protocol = url.getProtocol(); + String host = url.getHost(); + int port = url.getPort(); + HttpHost proxy = new HttpHost(protocol,host, port); + requestConfigBuilder.setProxy(proxy); + } + + configureHttpClient(requestConfigBuilder, vo, method); + + switch (HttpMethodType.getValue(rmethod)) { + case GET: + case DELETE: + HashMap h = getParameters(dataObject); + URIBuilder uriBuilder = new URIBuilder(uri); + for (Map.Entry entry : h.entrySet()) { + uriBuilder.addParameter(entry.getKey(), entry.getValue()); + } + URI fullUri = uriBuilder.build(); + if (method instanceof HttpGet) { + ((HttpGet) method).setUri(fullUri); + } else if (method instanceof HttpDelete) { + ((HttpDelete) method).setUri(fullUri); + } + if (logger.isDebug()) { + logger.debug("HttpClientAdapterServiceRest] (get Method) QueryString = [" + fullUri.getQuery() + "]"); + } + break; + case PUT: + case POST: + //assignPostBody(dataObject, contentType, vo.getEncode(), method); + assignPostBody(dataContent, contentType, vo.getEncode(), method); + break; + default: + break; + } + + if (logger.isDebugEnabled()) { + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") method.getParams()=[" + + method.getEntity() + "] "); + } + + OAuth2AccessTokenVO accessToken = null; + if (useAdapterToken) { + AccessTokenManagerByDB tokenManager = AccessTokenManagerByDB.getInstance(); + accessToken = (OAuth2AccessTokenVO) tokenManager.getAccessTokenVO(vo.getAdapterGroupName()); + + // 토큰이 없거나 만료 됐으면 재발급 + if (accessToken == null || accessToken.isExpired()) { + String oldToken = accessToken == null ? null : accessToken.getAccessToken(); + accessToken = (OAuth2AccessTokenVO) tokenManager.retryAccessTokenVO(vo.getAdapterGroupName(), prop, + oldToken); + } + + if (logger.isDebug()) { + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") TOKEN = [" + + accessToken + "]"); + } + + setAuthHeaders(method, accessToken); + + if (logger.isDebug()) { + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + + ") RequestHeader [Authorization=" + method.getHeader("Authorization") + "]"); + } + } + + // 2025.04.09 Authorization 헤더값을 프로퍼티(AUTH_TOKEN)에서 지정하여 설정할 수 있도록 수정 + if(!StringUtils.isBlank(authToken)) { + setAuthHeaders(method, authToken); + } + + // 전달 header 셋팅 + // Adapter 레벨 header 보다 data로 넘어온 httpHeader를 우선 적용(header명이 같다면 덮어씀) + assignRequestHeaders(method, httpHeader); + + int status = -1; + + try { + byte[] responseMessage = null; + Header[] responseHeaders; + + if (logger.isDebug() && "N".equals(vo.getTestCallYn())) { + if (dataContent != null) { //dataObject + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") = [" + + dataContent.toString() + "]"); //dataObject.toJSONString() + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") = " + + CommonLib.getDumpMessage(dataContent.toString())); //dataObject.toJSONString() + } else { + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") = [" + sendData + + "]"); + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") = " + + CommonLib.getDumpMessage(sendData)); + } + } + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + + if (vo.getTraceLevel() >= 3) { + HttpMemoryLogger.txlog(vo.getAdapterGroupName() + vo.getAdapterName(), + "SEND [" + sendData + "]" + CommonLib.getDumpMessage(sendData)); + } + + String uuid = tempProp.getProperty(TransactionContextKeys.TRANSACTION_UUID); + HttpContext context = new BasicHttpContext(); + context.setAttribute(TransactionContextKeys.TRANSACTION_UUID, uuid); + context.setAttribute(HttpClientAdapterServiceKey.ADAPTER_GROUP_NAME, vo.getAdapterGroupName()); + context.setAttribute(HttpClientAdapterServiceKey.ADAPTER_NAME, vo.getAdapterName()); + Integer logProcessNo = (Integer) tempProp.get(HttpClientAdapterServiceKey.LOG_PROCESS_NO); + if (logProcessNo != null && logProcessNo > 0) { + context.setAttribute(HttpClientAdapterServiceKey.LOG_PROCESS_NO, logProcessNo); + } + + try { + if (logger.isDebug()) { + logger.debug("[method getName]" + method.getMethod()); + logger.debug("[method getEntity]" + method.getEntity()); + logger.debug("[method getRequestHeaders]" + method.getHeaders().toString()); + logger.debug("[method getURI]" + method.getPath()); + } + try (CloseableHttpResponse response = (CloseableHttpResponse) mclient.execute(method, context)) { + status = response.getCode(); + + if (status >= 200 && status <= 207) { + status = 200; + } + +// if (status == 404) { +// status = 200; +// } + + responseMessage = EntityUtils.toByteArray(response.getEntity()); + responseHeaders = response.getHeaders(); + + if (logger.isDebug()) { + logger.debug("[received status]" + status); + logger.debug("HttpClientAdapterServiceRest] RECV (" + vo.getAdapterGroupName() + ") = [" + responseMessage + "]"); + } + } + } catch (ConnectException e) { + if (logger.isDebug() && "N".equals(vo.getTestCallYn())) { + logger.debug("HttpClientAdapterServiceRest] responseType =" + vo.getResponseType()); + } + throw new Exception("excuteMethod java.net.ConnectException = " + e.getMessage()); + } + + stopWatch.stop(); + + /** + * @formatter:off + * OAuth 토큰 응답 체크(Adapter properties로 설정) + * 응답코드에 따라 유효한 토큰 확인(토큰 재발급 여부 확인) + * API 서비스 마다 정책이 다름(보통 400대에서 체크하나 200에서도 체크할 수 있음) + * + * TOKEN_ERROR_CODE_KEY: 응답 json error code key + * __ex) $.dataHeader.resultCode + * TOKEN_ERROR_CODE_VALUES: 토큰 재발급이 필요한 응답 error codes(ex: O0001,O0002,O0003) + * TOKEN_ERROR_HTTP_STATUS_CODE: 보통 400대에서 체크하나 API 제공자에 따라 200에서도 체클할수 있음 + * ex) TOKEN_ERROR_HTTP_STATUS_CODE = 200 + * @formatter:on + */ + boolean needReissue = false; + + Properties responseHeaderProp = assignRelayDataToInbound(tempProp, responseHeaders); + String responseString = new String(responseMessage, vo.getEncode()); + + Map map = new HashMap(); + tempProp.put(HttpAdapterServiceKey.OUTBOUND_PROPERTY_MAP, map); + map.put(HttpAdapterServiceKey.OUTBOUND_RESPONSE_HEADERS, responseHeaderProp); + map.put(HttpAdapterServiceKey.OUTBOUND_RESPONSE_MESSAGE, responseString); + map.put(HttpAdapterServiceKey.OUTBOUND_RESPONSE_STATUS, status); + + if (status >= 200 && status <= 207) { + if (useAdapterToken && StringUtils.equals(tokenErrorHttpStatusCode, "200")) { + needReissue = checkTokenRetry(responseMessage, tokenErrorCodeKey, tokenErrorCodeValues, + vo.getEncode()); + } + } else if (status == 302) { + if (!StringUtils.contains(relayResponseHeaderKeys, "Location")) { + relayResponseHeaderKeys += ",Location"; + } + } else { + String errMsg = String.format( + "http receive status fail value=%d adapterName=%s.%s uri=%s method=%s response Data=%s", + status, vo.getAdapterGroupName(), vo.getAdapterName(), uri, rmethod, responseString); +// logger.error(errMsg); + + if (status >= 400 && status < 500) { + if (useAdapterToken) { + needReissue = checkTokenRetry(responseMessage, tokenErrorCodeKey, tokenErrorCodeValues, + vo.getEncode()); + } + +// if (!needReissue) { +// throw new Exception(errMsg); +// } + } else { + + throw new Exception(errMsg); + } + } + + // ---------------------------------------------------------- + if (logger.isDebug() && "N".equals(vo.getTestCallYn())) { + logger.debug("HttpClientAdapterServiceRest] responseType =" + vo.getResponseType()); + logger.debug("HttpClientAdapterServiceRest] RECV " + vo.getEncode() + "(" + vo.getAdapterGroupName() + ") = [" + new String(responseMessage, vo.getEncode()) + "]"); +// logger.debug("HttpClientAdapterServiceRest] RECV MS949 (" + vo.getAdapterGroupName() + ") = [" + new String(responseMessage, "MS949") + "]"); +// logger.debug("HttpClientAdapterServiceRest] RECV euc-kr (" + vo.getAdapterGroupName() + ") = [" + new String(responseMessage, "euc-kr") + "]"); + logger.debug("HttpClientAdapterServiceRest] RECV (" + vo.getAdapterGroupName() + ") = " + CommonLib.getDumpMessage(responseMessage)); + } + + if (useAdapterToken) { + if (responseMessage == null) { + throw new Exception("responseMessage is NULL, HttpStatus=" + status); + } + + // OAuth 토큰 재요청 코드 확인 + if (needReissue) { + if (logger.isDebug() && "N".equals(vo.getTestCallYn())) { + logger.debug("HttpClientAdapterServiceRest] retry access token response code = [" + + vo.getResponseType() + "] message = [" + new String(responseMessage, vo.getEncode()) + + "]"); + } + + // 토큰 재발급 + AccessTokenManager tokenManager = AccessTokenManager.getInstance(); + String oldToken = accessToken == null ? null : accessToken.getAccessToken(); + OAuth2AccessTokenVO newaccessToken = (OAuth2AccessTokenVO) tokenManager + .retryAccessTokenVO(vo.getAdapterGroupName(), prop, oldToken); + method.setHeader("Authorization", newaccessToken.getAuthorization()); + + setAuthHeaders(method, newaccessToken); + + if (logger.isDebug() && "N".equals(vo.getTestCallYn())) { + logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + + ") RETRY TOKEN = [" + newaccessToken + "]"); + } + + try (CloseableHttpResponse response = (CloseableHttpResponse) mclient.execute(method)) { + status = response.getCode(); + responseMessage = EntityUtils.toByteArray(response.getEntity()); + + if (logger.isDebug()) { + logger.debug("[received status]" + status); + logger.debug("HttpClientAdapterServiceRest] RECV (" + vo.getAdapterGroupName() + ") = [" + new String(responseMessage, vo.getEncode()) + "]"); + } + }catch (IOException e){ + throw new Exception("retry excuteMethod Exceptioin = " + e.getMessage()); + } + + + if (status == 302) { + if (!StringUtils.contains(relayResponseHeaderKeys, "Location")) { + relayResponseHeaderKeys += ",Location"; + } + } else { + String errMsg = String.format( + "http receive status fail value=%d adapterName=%s.%s uri=%s method=%s response Data=%s", + status, vo.getAdapterGroupName(), vo.getAdapterName(), uri, rmethod, responseMessage); + throw new Exception(errMsg); + } + + // ---------------------------------------------------------- + if (logger.isDebug() && "N".equals(vo.getTestCallYn())) { + logger.debug("HttpClientAdapterServiceRest] RETRY responseType =" + vo.getResponseType()); + logger.debug("HttpClientAdapterServiceRest] RETRY RECV " + vo.getEncode() + "(" + + vo.getAdapterGroupName() + ") = [" + new String(responseMessage, vo.getEncode()) + + "]"); + logger.debug("HttpClientAdapterServiceRest] RETRY RECV (" + vo.getAdapterGroupName() + ") = " + + CommonLib.getDumpMessage(responseMessage)); + } + // ---------------------------------------------------------- + } + } + + if (vo.getTraceLevel() >= 3) { + HttpMemoryLogger.txlog(vo.getAdapterGroupName() + vo.getAdapterName(), + "RECV [" + new String(responseMessage, vo.getEncode()) + "]" + + CommonLib.getDumpMessage(responseMessage)); + } + + + return assignResponseHeaders(method, responseMessage, headerGroupName, vo.getEncode(), messageType, + relayResponseHeaderKeys, status, responseHeaderProp); + } catch (SocketTimeoutException ste) { // Read Timeout :: HttpClient 3.1일 경우 + if (vo.getTraceLevel() >= 3) { + HttpMemoryLogger.error(vo.getAdapterGroupName() + vo.getAdapterName(), + "HttpClientAdapterServiceRest] SocketTimeoutException (" + vo.getAdapterGroupName() + ") : " + + ste.toString(), + ste); + } + logger.error("HttpClientAdapterServiceRest] SocketTimeoutException (" + vo.getAdapterGroupName() + ") : " + + ste.toString(), ste); + throw ste; + } catch (ConnectException ce) { + if (vo.getTraceLevel() >= 3) { + HttpMemoryLogger.error(vo.getAdapterGroupName() + vo.getAdapterName(), + "HttpClientAdapterServiceRest] Connection Exception (" + vo.getAdapterGroupName() + ") : " + + ce.toString(), + ce); + } + logger.error("HttpClientAdapterServiceRest] Connection Exception (" + vo.getAdapterGroupName() + ") : " + + ce.toString(), ce); + throw ce; + } catch (Exception e) { + if (vo.getTraceLevel() >= 3) { + HttpMemoryLogger.error(vo.getAdapterGroupName() + vo.getAdapterName(), e.toString(), e); + } + logger.error("HttpClientAdapterServiceRest] Exception (" + vo.getAdapterGroupName() + ") : " + e.toString(), + e); + throw e; + } finally { + if (status != HttpStatus.SC_OK) { + String[] msgArgs = new String[1]; + msgArgs[0] = String.valueOf(status); + String resMsg = ExceptionUtil.make("RDCEAIAHA013", msgArgs); + if (logger.isDebug()) { + logger.debug(resMsg); + } + } + } + } + + private Properties assignRelayDataToInbound(Properties prop, Header[] responseHeaders) { + Properties headerProp = new Properties(); + if (responseHeaders == null || responseHeaders.length == 0) { + return headerProp; + } + + for (Header header : responseHeaders) { + headerProp.put(header.getName(), header.getValue()); + } + + return headerProp; + } + + /** + * 현재 1단계 레이아웃에만 적용되도록 구현됨. + * + * @param method + * @param responseMessage + * @param headerGroupName + * @param encode + * @param messageType + * @param relayHeaderKeys + * @return + * @throws Exception + */ + @SuppressWarnings("unchecked") + private String assignResponseHeaders(HttpUriRequestBase method, byte[] responseMessage, String headerGroupName, + String encode, String messageType, String relayHeaderKeys, int status, Properties responseHeaderProp) throws Exception { + if (!MessageType.JSON.equals(messageType) || StringUtils.isBlank(headerGroupName) + || StringUtils.isBlank(relayHeaderKeys)) { + return new String(responseMessage, encode); + } + + String[] relayKeyArr = org.springframework.util.StringUtils.tokenizeToStringArray(relayHeaderKeys, ","); + JSONObject headerJson = new JSONObject(); + for (String key : relayKeyArr) { + String value = responseHeaderProp.getProperty(key); + if (StringUtils.isNotBlank(value)) { + headerJson.put(key, value); + } + } + + headerJson.put(HTTP_STATUS, String.valueOf(status)); + + if (headerJson.size() <= 0) { + return new String(responseMessage, encode); + } + + JSONObject message = null; + if (responseMessage == null || responseMessage.length == 0) { + message = new JSONObject(); + } else { + message = parseJson(new String(responseMessage, encode)); + if (message == null) { + message = new JSONObject(); + message.put("Malformed_Response_Message", new String(responseMessage, encode)); + } + } + + if (StringUtils.isNotBlank(headerGroupName) && message != null) + message.put(headerGroupName, headerJson); + + logger.info("------------- message --------------- : {} ", message); + + return message.toJSONString(); + } + + // 2025.04.09 Authorization 헤더값을 프로퍼티(AUTH_TOKEN)에서 지정하여 설정할 수 있도록 수정 + private void setAuthHeaders(HttpUriRequestBase method, String authorization) throws Exception { + method.setHeader("Authorization", + String.format("%s", authorization)); + } + + private void setAuthHeaders(HttpUriRequestBase method, OAuth2AccessTokenVO accessToken) throws Exception { + method.setHeader("Authorization", + String.format("%s %s", AccessTokenVO.BEARER_TYPE, accessToken.getAccessToken())); + } + + private boolean checkTokenRetry(byte[] responseMessage, String tokenErrorCodeKey, String tokenErrorCodeValues, + String encode) { + if (responseMessage == null) { + return false; + } + + if (StringUtils.isBlank(tokenErrorCodeKey)) { + return false; + } + + if (StringUtils.isBlank(tokenErrorCodeValues)) { + return false; + } + + try { + DocumentContext jsonContext = JsonPath.parse(new String(responseMessage, encode)); + String responseCode = jsonContext.read(tokenErrorCodeKey); + if (StringUtils.isBlank(responseCode)) { + return false; + } + + String[] arr = org.springframework.util.StringUtils.tokenizeToStringArray(tokenErrorCodeValues, ","); + return ArrayUtils.contains(arr, responseCode); + } catch (Exception e) { + logger.error("HttpClientAdapterServiceRest] checkTokenRetry error=" + e.getMessage()); + } + + return false; + } + + private void assignRequestHeaders(HttpUriRequestBase method, Object httpHeader) { + if (httpHeader == null) { + return; + } + + if (httpHeader instanceof JSONObject) { + JSONObject jsonObject = (JSONObject) httpHeader; + for (Object key : jsonObject.keySet()) { + Object obj = jsonObject.get(key); + if ((obj instanceof JSONObject) || (obj instanceof JSONArray)) { + continue; + } + + method.setHeader((String) key, (String) obj); + + if (logger.isDebugEnabled()) { + logger.debug("Request Header :" + (String) key + "=[" + (String) obj + "]"); + } + + } + } + } + + @SuppressWarnings("deprecation") + private void assignPostBody(Object eaiBody, String contentType, String charset, HttpUriRequestBase method) { + if (eaiBody == null) { + return; + } + + if (StringUtils.contains(contentType, "application/x-www-form-urlencoded")) { + if (eaiBody instanceof JSONObject) { + List params = new ArrayList<>(); + JSONObject jsonObject = (JSONObject) eaiBody; + for (Object key : jsonObject.keySet()) { + params.add(new BasicNameValuePair((String) key, (String) jsonObject.get(key))); + } + + UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, StandardCharsets.UTF_8); + method.setEntity(entity); + } + + } else { + ContentType contentTypeObj = ContentType.create(contentType, charset); + + // 요청 본문을 StringEntity 객체로 생성 + StringEntity entity = new StringEntity(eaiBody.toString(), contentTypeObj); + + // 요청에 본문 추가 + method.setEntity(entity); + } + } + + private HashMap getParameters(Object message) throws Exception { + + HashMap result = new HashMap(); + + if (message != null) { + if (message instanceof JSONObject) { + JSONObject jsonObject = (JSONObject) message; + for (Object key : jsonObject.keySet()) { + Object obj = jsonObject.get(key); + if ((obj instanceof JSONObject) || (obj instanceof JSONArray)) { + continue; + } + result.put((String) key, getStringValue(obj)); + } + } else { + String[] messages = ((String) message).split("&"); + String[] data = null; + + for (int i = 0; i < messages.length; i++) { + data = messages[i].split("=", 2); + if (data.length == 2) { + result.put(data[0], data[1]); + } + } + } + } + + return result; + } + + public String getStringValue(Object obj) { + if (obj == null) { + return null; + } + + if (obj instanceof String) { + return (String) obj; + } else if (obj instanceof Long) { + return Long.toString((Long) obj); + } else if (obj instanceof BigDecimal) { + return ((BigDecimal) obj).toPlainString(); + } else { + return (String) obj; + } + } + + public static Object parseJsonGeneric(String jsonData) { + if (StringUtils.isBlank(jsonData)) { + return null; + } + + try { + JSONParser parser = new JSONParser(); + Object parsedObj = parser.parse(jsonData); + return parsedObj; + } catch (ParseException e) { + e.printStackTrace(); + return null; + } + } + + @SuppressWarnings("deprecation") + private JSONObject parseJson(String message) throws Exception { + if (message == null) { + return null; + } + + return (JSONObject) JSONValue.parse(message); + } + + private Document convertXmlDocument(String message) throws Exception { + SAXReader builder = new SAXReader(); + Document document = builder.read(new StringReader(message)); + return document; + } + + @SuppressWarnings("deprecation") + private String changeUrl(String messageType, String url, String restOption, Object sendData) { + if ((MessageType.JSON.equals(messageType) || MessageType.XML.equals(messageType))) { + try { + if (StringUtils.isBlank(restOption)) { + return url; + } + List urlVariableList = new ArrayList(); + JSONObject restOptionObject = parseJson(restOption); + + if (restOptionObject == null) + throw new Exception("restOption is NULL"); + + String type = (String) restOptionObject.get("type"); + String requestExtraPath = (String) restOptionObject.get("extraPath"); + url = getUrl(url, requestExtraPath); + if (TYPE_VARIABLE_URL_REQUEST.equals(type)) { + Pattern p = Pattern.compile("\\{(.*?)\\}"); + Matcher m = p.matcher(requestExtraPath); + List uriVariables = new ArrayList<>(); + while(m.find()) { + String fieldName = m.group(1); + uriVariables.add(fieldName); + } + if (MessageType.JSON.equals(messageType)) { + JSONObject jsonObject; + if (sendData instanceof JSONObject) { + jsonObject = (JSONObject) sendData; + } else { + jsonObject = (JSONObject) JSONValue.parse((String) sendData); + } + for (Object tempObject : uriVariables) { + String urlVaribleId = (String) tempObject; + String uriVariable = (String) jsonObject.get(urlVaribleId); + urlVariableList.add(uriVariable); + jsonObject.remove(urlVaribleId); + } + } else if (MessageType.XML.equals(messageType)) { + Document doc; + if (sendData instanceof Document) { + doc = (Document) sendData; + } else { + doc = convertXmlDocument((String) sendData); + } + for (Object tempObject : uriVariables) { + String urlVaribleId = (String) tempObject; + Element element = (Element) doc.selectSingleNode("//" + urlVaribleId); + urlVariableList.add(element.getText()); + if (doc.getRootElement() == element) { + doc = null; + } else { + element.getParent().remove(element); + } + } + } + if (urlVariableList.size() > 0) { + UriComponents uriComponents = UriComponentsBuilder.fromUriString(url).build(); + Object[] urls = urlVariableList.toArray(); + url = uriComponents.expand(urls).toUriString(); + logger.debug("HttpClientAdapterServiceRest] after extand url=[" + url + "] "); + } + } + } catch (Exception e) { + logger.error("Failed to change url", e); + } + } + return url; + } + + private String getUrl(String baseUrl, String extraPath) { + if (StringUtils.isBlank(extraPath)) { + return baseUrl; + } + + if (StringUtils.contains(extraPath, "http://") || StringUtils.contains(extraPath, "https://")) { + return extraPath; + } + + String targetUrl = baseUrl; + if (!targetUrl.endsWith("/")) { + targetUrl += "/"; + } + if (extraPath.startsWith("/")) { + targetUrl += extraPath.substring(1); + } else { + targetUrl += extraPath; + } + + logger.debug("HttpClientAdapterServiceRest] after concatenate url=[" + targetUrl + "] "); + return targetUrl; + } + +} diff --git a/src/main/java/com/eactive/eai/adapter/http/dynamic/filter/FilterCryptoException.java b/src/main/java/com/eactive/eai/adapter/http/dynamic/filter/FilterCryptoException.java new file mode 100644 index 0000000..594ef08 --- /dev/null +++ b/src/main/java/com/eactive/eai/adapter/http/dynamic/filter/FilterCryptoException.java @@ -0,0 +1,18 @@ +package com.eactive.eai.adapter.http.dynamic.filter; + +@SuppressWarnings("serial") +public class FilterCryptoException extends FilterException { + public FilterCryptoException(String msg) { + super(msg); + } + public FilterCryptoException(String msg, int status) { + super(msg, status); + } + public FilterCryptoException(String msg, String code, int status) { + super(msg, code, status); + } + + public FilterCryptoException(String msg, String code, int status, Throwable cause) { + super(msg, code, status, cause); + } +} diff --git a/src/main/java/com/eactive/eai/adapter/http/dynamic/filter/FilterException.java b/src/main/java/com/eactive/eai/adapter/http/dynamic/filter/FilterException.java new file mode 100644 index 0000000..aebd843 --- /dev/null +++ b/src/main/java/com/eactive/eai/adapter/http/dynamic/filter/FilterException.java @@ -0,0 +1,21 @@ +package com.eactive.eai.adapter.http.dynamic.filter; + +import com.eactive.eai.adapter.http.HttpStatusException; + +@SuppressWarnings("serial") +public class FilterException extends HttpStatusException { + public FilterException(String msg) { + super(msg); + } + public FilterException(String msg, int status) { + super(msg, status); + } + public FilterException(String msg, String code, int status) { + super(msg, code, status); + } + + public FilterException(String msg, String code, int status, Throwable cause) { + super(msg, code, status, cause); + } + +}