Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9eea585b52 | |||
| 66e51d1755 | |||
| 5e07100c3d | |||
| 1bf2c78f03 | |||
| 1941292079 | |||
| abf0de7757 | |||
| a7fde3f81e | |||
| bfec1edea2 |
+27
-4
@@ -75,7 +75,11 @@ dependencies {
|
|||||||
api 'io.micrometer:micrometer-core:1.5.17'
|
api 'io.micrometer:micrometer-core:1.5.17'
|
||||||
api 'io.micrometer:micrometer-registry-prometheus:1.5.17'
|
api 'io.micrometer:micrometer-registry-prometheus:1.5.17'
|
||||||
|
|
||||||
implementation "com.google.code.gson:gson:2.3.1"
|
// gson 2.3.1 -> 2.8.9 (CVE-2022-25647: 악의적 데이터 역직렬화 시 메모리 고갈/DoS)
|
||||||
|
// 사용처는 Jsons(new Gson), AlarmService(fromJson), TemplateAdapterErrorMsgHandler(JsonParser),
|
||||||
|
// alarm/ums/payload/*(@SerializedName) 4곳뿐이고 사용 API 는 그대로 유지된다.
|
||||||
|
// (new JsonParser().parse() 는 2.8.9 에서 deprecated 이지만 제거되지 않아 동작에 영향 없음)
|
||||||
|
implementation "com.google.code.gson:gson:2.8.9"
|
||||||
|
|
||||||
//api "com.eactive:mina-core-1.0.10-custom:1.0:custom@jar"
|
//api "com.eactive:mina-core-1.0.10-custom:1.0:custom@jar"
|
||||||
api ("org.apache.mina:mina-filter-ssl:1.0.10") {
|
api ("org.apache.mina:mina-filter-ssl:1.0.10") {
|
||||||
@@ -91,10 +95,23 @@ dependencies {
|
|||||||
|
|
||||||
api 'com.nimbusds:nimbus-jose-jwt:9.24.3'
|
api 'com.nimbusds:nimbus-jose-jwt:9.24.3'
|
||||||
|
|
||||||
api 'org.bouncycastle:bcprov-jdk15on:1.70'
|
// BouncyCastle: bcprov-jdk15on 라인은 1.70에서 종료되어 후속 패치가 없다.
|
||||||
|
// jdk18on 라인으로 전환한다(패키지명·프로바이더명("BC") 동일 → 소스 변경 없음,
|
||||||
|
// 클래스파일 major 52 / Bundle-RequiredExecutionEnvironment: JavaSE-1.8 이라 JDK 8 유지 가능).
|
||||||
|
// 사용처: AESCryptoModuleExtension, ARIACryptoModuleExtension
|
||||||
|
// (BouncyCastleProvider 등록, ARIA 알고리즘, FPE 모드의 FPEParameterSpec)
|
||||||
|
// 1.70 대비 CVE-2023-33201 / CVE-2024-29857 / CVE-2024-30171 / CVE-2024-30172 해소.
|
||||||
|
// bcpkix 는 spring-security-jwt 가 전이로 끌고 오던 1.64 를 대체한다(아래 exclude 참조).
|
||||||
|
api 'org.bouncycastle:bcprov-jdk18on:1.78.1'
|
||||||
|
api 'org.bouncycastle:bcpkix-jdk18on:1.78.1'
|
||||||
|
|
||||||
api "io.undertow:undertow-servlet:${undertowVersion}"
|
api "io.undertow:undertow-servlet:${undertowVersion}"
|
||||||
api 'org.java-websocket:Java-WebSocket:1.3.9'
|
// Java-WebSocket 1.3.9 -> 1.5.7 (CVE-2020-11050: 구형 암호화 통신/인증서 검증 결함에 의한 MitM)
|
||||||
|
// 사용 API 는 WebSocketServer 상속(onOpen/onClose/onMessage/onError/onStart)과
|
||||||
|
// WebSocket 의 send/close/getRemoteSocketAddress/isOpen, start()/stop(timeout) 뿐이라
|
||||||
|
// 1.4.0 의 파괴적 변경(WebSocketImpl.DEBUG 제거, Draft_10/17 제거, connections() -> getConnections())
|
||||||
|
// 에 해당하는 사용처가 없다. 1.5.x 는 로깅을 SLF4J 로 하는데 이미 클래스패스에 있다.
|
||||||
|
api 'org.java-websocket:Java-WebSocket:1.5.7'
|
||||||
api 'javax.cache:cache-api:1.1.1'
|
api 'javax.cache:cache-api:1.1.1'
|
||||||
|
|
||||||
api 'org.apache.ignite:ignite-slf4j:2.14.0'
|
api 'org.apache.ignite:ignite-slf4j:2.14.0'
|
||||||
@@ -146,7 +163,13 @@ dependencies {
|
|||||||
// XmlMapper/JacksonXml* 사용처가 전 소스에 0건이고, 선언 버전(2.13.1)이
|
// XmlMapper/JacksonXml* 사용처가 전 소스에 0건이고, 선언 버전(2.13.1)이
|
||||||
// 실제 해석되는 jackson-core/databind(2.12.7)와 마이너 불일치라 승격 시 위험했다.
|
// 실제 해석되는 jackson-core/databind(2.12.7)와 마이너 불일치라 승격 시 위험했다.
|
||||||
|
|
||||||
api "org.springframework.security:spring-security-jwt:1.1.1.RELEASE"
|
// bcpkix-jdk15on:1.64 -> bcprov-jdk15on:1.64 를 전이로 끌고 온다.
|
||||||
|
// 위에서 jdk18on 으로 전환했으므로 함께 두면 org.bouncycastle.* 클래스가 중복되고
|
||||||
|
// 로딩 순서에 따라 구버전이 선택될 수 있다. 전이를 끊고 jdk18on 만 사용한다.
|
||||||
|
// (이 exclude 는 발행 POM 에도 기록되어 이 모듈을 참조하는 타 사이트에도 동일 적용된다)
|
||||||
|
api ("org.springframework.security:spring-security-jwt:1.1.1.RELEASE") {
|
||||||
|
exclude group: 'org.bouncycastle'
|
||||||
|
}
|
||||||
|
|
||||||
testRuntimeOnly 'com.h2database:h2:2.1.214'
|
testRuntimeOnly 'com.h2database:h2:2.1.214'
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
|
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
|
||||||
|
|||||||
+35
-12
@@ -108,6 +108,8 @@ public class HttpClient5AdapterServiceRest extends HttpClient5AdapterServiceSupp
|
|||||||
public static final String AUTH_TOKEN = "AUTH_TOKEN";
|
public static final String AUTH_TOKEN = "AUTH_TOKEN";
|
||||||
public static final String HTTP_HEADER_SETTING = "HTTP_HEADER_SETTING";
|
public static final String HTTP_HEADER_SETTING = "HTTP_HEADER_SETTING";
|
||||||
|
|
||||||
|
public static final String NON_DELIVERY_GROUP = "NON_DELIVERY_GROUP";
|
||||||
|
|
||||||
public static final String PAYLOAD_PARAM_NAME_CLIENT_ID = "client_id"; // jwhong
|
public static final String PAYLOAD_PARAM_NAME_CLIENT_ID = "client_id"; // jwhong
|
||||||
|
|
||||||
static final String DJB_ROOTLESS_ARRAY = "{ \"DJB_ROOTLESS_ARRAY\" : ";
|
static final String DJB_ROOTLESS_ARRAY = "{ \"DJB_ROOTLESS_ARRAY\" : ";
|
||||||
@@ -135,6 +137,12 @@ public class HttpClient5AdapterServiceRest extends HttpClient5AdapterServiceSupp
|
|||||||
String bizCode = "";
|
String bizCode = "";
|
||||||
String headerGroupName = prop.getProperty(HEADER_GROUP);
|
String headerGroupName = prop.getProperty(HEADER_GROUP);
|
||||||
String relayResponseHeaderKeys = prop.getProperty(HEADER_KEYS, "");
|
String relayResponseHeaderKeys = prop.getProperty(HEADER_KEYS, "");
|
||||||
|
|
||||||
|
// 전송 대상에서 제외할 그룹명(콤마로 다건 지정 가능)
|
||||||
|
String[] nonDeliveryGroups = StringUtils.split(
|
||||||
|
StringUtils.trimToEmpty(prop.getProperty(NON_DELIVERY_GROUP)), ",");
|
||||||
|
if (nonDeliveryGroups == null) nonDeliveryGroups = new String[0];
|
||||||
|
|
||||||
useAdapterToken = StringUtils.equalsIgnoreCase(prop.getProperty("ADAPTER_TOKEN_USE_YN", "N"), "Y");
|
useAdapterToken = StringUtils.equalsIgnoreCase(prop.getProperty("ADAPTER_TOKEN_USE_YN", "N"), "Y");
|
||||||
String authorizationHeaderName = prop.getProperty("ADAPTER_TOKEN_HEADER_NAME", "Authorization");
|
String authorizationHeaderName = prop.getProperty("ADAPTER_TOKEN_HEADER_NAME", "Authorization");
|
||||||
|
|
||||||
@@ -253,24 +261,39 @@ public class HttpClient5AdapterServiceRest extends HttpClient5AdapterServiceSupp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(dataObject instanceof ObjectNode) {
|
if (dataObject instanceof ObjectNode) {
|
||||||
if (dataObject != null && StringUtils.isNotBlank(headerGroupName)) {
|
ObjectNode node = (ObjectNode) dataObject;
|
||||||
httpHeader = ((ObjectNode)dataObject).get(headerGroupName);
|
if (StringUtils.isNotBlank(headerGroupName)) {
|
||||||
((ObjectNode)dataObject).remove(headerGroupName);
|
httpHeader = node.get(headerGroupName);
|
||||||
|
node.remove(headerGroupName);
|
||||||
}
|
}
|
||||||
} else if(dataObject instanceof JSONObject) {
|
for (String nonDeliveryGroup : nonDeliveryGroups) {
|
||||||
if (dataObject != null && StringUtils.isNotBlank(headerGroupName)) {
|
if (StringUtils.isNotBlank(nonDeliveryGroup)) node.remove(nonDeliveryGroup.trim());
|
||||||
httpHeader = ((JSONObject)dataObject).get(headerGroupName);
|
}
|
||||||
((JSONObject)dataObject).remove(headerGroupName);
|
} else if (dataObject instanceof JSONObject) {
|
||||||
|
JSONObject json = (JSONObject) dataObject;
|
||||||
|
if (StringUtils.isNotBlank(headerGroupName)) {
|
||||||
|
httpHeader = json.get(headerGroupName);
|
||||||
|
json.remove(headerGroupName);
|
||||||
|
}
|
||||||
|
for (String nonDeliveryGroup : nonDeliveryGroups) {
|
||||||
|
if (StringUtils.isNotBlank(nonDeliveryGroup)) json.remove(nonDeliveryGroup.trim());
|
||||||
}
|
}
|
||||||
} else if (dataObject instanceof Document) {
|
} else if (dataObject instanceof Document) {
|
||||||
Document doc = (Document)dataObject;
|
Element root = ((Document) dataObject).getRootElement();
|
||||||
if (doc != null && StringUtils.isNotBlank(headerGroupName)) {
|
if (root != null) {
|
||||||
Element root = doc.getRootElement();
|
if (StringUtils.isNotBlank(headerGroupName)) {
|
||||||
Element httpHeaderElement = root.element(headerGroupName);
|
Element httpHeaderElement = root.element(headerGroupName);
|
||||||
root.remove(httpHeaderElement);
|
if (httpHeaderElement != null) root.remove(httpHeaderElement);
|
||||||
httpHeader = httpHeaderElement;
|
httpHeader = httpHeaderElement;
|
||||||
}
|
}
|
||||||
|
// element(null) 은 dom4j 내부에서 NPE 가 발생하므로 반드시 blank 검사 후 조회한다
|
||||||
|
for (String nonDeliveryGroup : nonDeliveryGroups) {
|
||||||
|
if (StringUtils.isBlank(nonDeliveryGroup)) continue;
|
||||||
|
Element nonDeliveryGroupElement = root.element(nonDeliveryGroup.trim());
|
||||||
|
if (nonDeliveryGroupElement != null) root.remove(nonDeliveryGroupElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug("HttpClientAdapterServiceRest] dataObject2 = [" + dataObject + "]");
|
logger.debug("HttpClientAdapterServiceRest] dataObject2 = [" + dataObject + "]");
|
||||||
|
|||||||
+6
-1
@@ -173,7 +173,12 @@ public class HttpClientAccessTokenServiceWithBase64Header implements HttpClientA
|
|||||||
cmBuilder.setSSLSocketFactory(sslSocketFactory);
|
cmBuilder.setSSLSocketFactory(sslSocketFactory);
|
||||||
} else {
|
} else {
|
||||||
sslContext = SSLContextBuilder.create().loadTrustMaterial(new TrustAllStrategy()).build();
|
sslContext = SSLContextBuilder.create().loadTrustMaterial(new TrustAllStrategy()).build();
|
||||||
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
|
SSLConnectionSocketFactory csf = null;
|
||||||
|
if (testMode) {
|
||||||
|
csf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
|
||||||
|
} else {
|
||||||
|
csf = new SSLConnectionSocketFactory(sslContext);
|
||||||
|
}
|
||||||
cmBuilder.setSSLSocketFactory(csf);
|
cmBuilder.setSSLSocketFactory(csf);
|
||||||
}
|
}
|
||||||
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException | CertificateException
|
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException | CertificateException
|
||||||
|
|||||||
+6
-1
@@ -181,7 +181,12 @@ public class HttpClientAccessTokenServiceWithBase64NiceOn implements HttpClientA
|
|||||||
cmBuilder.setSSLSocketFactory(sslSocketFactory);
|
cmBuilder.setSSLSocketFactory(sslSocketFactory);
|
||||||
} else {
|
} else {
|
||||||
sslContext = SSLContextBuilder.create().loadTrustMaterial(new TrustAllStrategy()).build();
|
sslContext = SSLContextBuilder.create().loadTrustMaterial(new TrustAllStrategy()).build();
|
||||||
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
|
SSLConnectionSocketFactory csf = null;
|
||||||
|
if (testMode) {
|
||||||
|
csf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
|
||||||
|
} else {
|
||||||
|
csf = new SSLConnectionSocketFactory(sslContext);
|
||||||
|
}
|
||||||
cmBuilder.setSSLSocketFactory(csf);
|
cmBuilder.setSSLSocketFactory(csf);
|
||||||
}
|
}
|
||||||
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException | CertificateException
|
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException | CertificateException
|
||||||
|
|||||||
+6
-1
@@ -180,7 +180,12 @@ public class HttpClientAccessTokenServiceWithDefault implements HttpClientAccess
|
|||||||
cmBuilder.setSSLSocketFactory(sslSocketFactory);
|
cmBuilder.setSSLSocketFactory(sslSocketFactory);
|
||||||
} else {
|
} else {
|
||||||
sslContext = SSLContextBuilder.create().loadTrustMaterial(new TrustAllStrategy()).build();
|
sslContext = SSLContextBuilder.create().loadTrustMaterial(new TrustAllStrategy()).build();
|
||||||
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
|
SSLConnectionSocketFactory csf = null;
|
||||||
|
if (testMode) {
|
||||||
|
csf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
|
||||||
|
} else {
|
||||||
|
csf = new SSLConnectionSocketFactory(sslContext);
|
||||||
|
}
|
||||||
cmBuilder.setSSLSocketFactory(csf);
|
cmBuilder.setSSLSocketFactory(csf);
|
||||||
}
|
}
|
||||||
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException | CertificateException
|
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException | CertificateException
|
||||||
|
|||||||
+6
-1
@@ -179,7 +179,12 @@ public class HttpClientAccessTokenServiceWithParam implements HttpClientAccessTo
|
|||||||
cmBuilder.setSSLSocketFactory(sslSocketFactory);
|
cmBuilder.setSSLSocketFactory(sslSocketFactory);
|
||||||
} else {
|
} else {
|
||||||
sslContext = SSLContextBuilder.create().loadTrustMaterial(new TrustAllStrategy()).build();
|
sslContext = SSLContextBuilder.create().loadTrustMaterial(new TrustAllStrategy()).build();
|
||||||
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
|
SSLConnectionSocketFactory csf = null;
|
||||||
|
if (testMode) {
|
||||||
|
csf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
|
||||||
|
} else {
|
||||||
|
csf = new SSLConnectionSocketFactory(sslContext);
|
||||||
|
}
|
||||||
cmBuilder.setSSLSocketFactory(csf);
|
cmBuilder.setSSLSocketFactory(csf);
|
||||||
}
|
}
|
||||||
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException | CertificateException
|
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException | CertificateException
|
||||||
|
|||||||
+6
-1
@@ -196,7 +196,12 @@ public class HttpClientAccessTokenServiceWithParamAddBody implements HttpClientA
|
|||||||
cmBuilder.setSSLSocketFactory(sslSocketFactory);
|
cmBuilder.setSSLSocketFactory(sslSocketFactory);
|
||||||
} else {
|
} else {
|
||||||
sslContext = SSLContextBuilder.create().loadTrustMaterial(new TrustAllStrategy()).build();
|
sslContext = SSLContextBuilder.create().loadTrustMaterial(new TrustAllStrategy()).build();
|
||||||
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
|
SSLConnectionSocketFactory csf = null;
|
||||||
|
if (testMode) {
|
||||||
|
csf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
|
||||||
|
} else {
|
||||||
|
csf = new SSLConnectionSocketFactory(sslContext);
|
||||||
|
}
|
||||||
cmBuilder.setSSLSocketFactory(csf);
|
cmBuilder.setSSLSocketFactory(csf);
|
||||||
}
|
}
|
||||||
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException | CertificateException
|
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException | CertificateException
|
||||||
|
|||||||
@@ -295,6 +295,58 @@ public class StandardItem implements Serializable, Cloneable {
|
|||||||
this.isHidden = isHidden;
|
this.isHidden = isHidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 조건부 블록의 refValue 와 실제 필드값을 비교한다.
|
||||||
|
* FlatReader / StandardMessageCoordinator 와 동일한 기준을 쓰기 위한 공용 판정 로직이다.
|
||||||
|
* - '!' 접두사 : NOT 조건 (예: !S → S가 아닌 경우)
|
||||||
|
* - '|' 구분자 : OR 조건 (예: NM|EM → NM 또는 EM인 경우)
|
||||||
|
* - '!' 와 '|' 조합 가능 (예: !NM|EM → NM도 EM도 아닌 경우)
|
||||||
|
*/
|
||||||
|
public static boolean matchRefCondition(String refValue, String actualValue) {
|
||||||
|
if(refValue == null) return false;
|
||||||
|
String expect = refValue;
|
||||||
|
boolean negate = expect.startsWith("!");
|
||||||
|
if(negate) {
|
||||||
|
expect = expect.substring(1);
|
||||||
|
}
|
||||||
|
String actual = (actualValue == null) ? null : actualValue.trim();
|
||||||
|
boolean matched = false;
|
||||||
|
String[] expectValues = expect.split("\\|");
|
||||||
|
for(int i=0; i<expectValues.length; i++) {
|
||||||
|
if(expectValues[i].equals(actual)) {
|
||||||
|
matched = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return negate ? !matched : matched;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FLAT 직렬화 시 이 GROUP 블록의 바이트를 출력할지 판단한다.
|
||||||
|
* FlatReader.traverse() 의 GROUP 처리와 같은 기준을 쓰기 위한 것으로,
|
||||||
|
* ref 조건이 성립하면 size==0(전문에 블록이 없어 미활성) 이어도 레이아웃 기본값으로 출력한다.
|
||||||
|
* 조건이 성립하는데 출력하지 않으면, 파서는 블록이 있다고 보고 읽어 오프셋이 어긋난다.
|
||||||
|
*
|
||||||
|
* @param root 조건 평가 기준이 되는 최상위 메시지. null 이면 조건 평가 없이 기존 규칙만 적용
|
||||||
|
* @param parentActivated 조건 성립으로 활성화된 상위 GROUP 하위인지 여부
|
||||||
|
*/
|
||||||
|
protected boolean isFlatGroupActive(StandardMessage root, boolean parentActivated) {
|
||||||
|
if(isHidden) return false;
|
||||||
|
if(getSize() > 0) return true;
|
||||||
|
if(root != null
|
||||||
|
&& StringUtils.isNotBlank(getRefPath())
|
||||||
|
&& StringUtils.isNotBlank(getRefValue())
|
||||||
|
&& matchRefCondition(getRefValue(), root.findItemValue(getRefPath())) ) {
|
||||||
|
if(logger.isDebugEnabled()) {
|
||||||
|
logger.debug("@GROUP name={} ACTIVATED by ref condition({}=[{}]). size=0 이지만 FLAT 출력 대상"
|
||||||
|
, getName(), getRefPath(), getRefValue());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 조건 성립으로 활성화된 상위 GROUP 하위의 무조건 블록은 함께 출력한다.
|
||||||
|
return parentActivated;
|
||||||
|
}
|
||||||
|
|
||||||
protected String toTypeValue(String svalue) {
|
protected String toTypeValue(String svalue) {
|
||||||
if(svalue == null) return svalue;
|
if(svalue == null) return svalue;
|
||||||
|
|
||||||
@@ -1007,6 +1059,15 @@ public class StandardItem implements Serializable, Cloneable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getBytesDataLength(String charset) {
|
public int getBytesDataLength(String charset) {
|
||||||
|
return getBytesDataLength(charset, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param root 조건부 블록(refPath/refValue) 평가 기준이 되는 최상위 메시지
|
||||||
|
* @param parentActivated 조건 성립으로 활성화된 상위 GROUP 하위인지 여부
|
||||||
|
* @see #isFlatGroupActive(StandardMessage, boolean)
|
||||||
|
*/
|
||||||
|
public int getBytesDataLength(String charset, StandardMessage root, boolean parentActivated) {
|
||||||
int totalSize = 0;
|
int totalSize = 0;
|
||||||
Iterator<String> keyIter = null;
|
Iterator<String> keyIter = null;
|
||||||
try {
|
try {
|
||||||
@@ -1016,23 +1077,25 @@ public class StandardItem implements Serializable, Cloneable {
|
|||||||
while(keyIter.hasNext()) {
|
while(keyIter.hasNext()) {
|
||||||
String key = keyIter.next();
|
String key = keyIter.next();
|
||||||
StandardItem item = childs.get(key);
|
StandardItem item = childs.get(key);
|
||||||
totalSize +=item.getBytesDataLength(charset);
|
totalSize +=item.getBytesDataLength(charset, root, false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case StandardType.FIELD :
|
case StandardType.FIELD :
|
||||||
totalSize += getLength();
|
totalSize += getLength();
|
||||||
break;
|
break;
|
||||||
case StandardType.GROUP :
|
case StandardType.GROUP :
|
||||||
// skip inactive/conditional group (size==0 means not activated)
|
// ref 조건이 성립하면 size==0 이어도 출력한다(FlatReader 와 동일 기준)
|
||||||
if( getSize() == 0 ) {
|
if( !isFlatGroupActive(root, parentActivated) ) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (isHidden) break;
|
// 출력하는 GROUP 의 하위 무조건 블록은 함께 출력한다.
|
||||||
|
// FlatReader 는 이 블록을 조건 없이 읽으므로 빼면 오프셋이 어긋난다.
|
||||||
|
boolean childActivated = true;
|
||||||
keyIter = childs.keySet().iterator();
|
keyIter = childs.keySet().iterator();
|
||||||
while(keyIter.hasNext()) {
|
while(keyIter.hasNext()) {
|
||||||
String key = keyIter.next();
|
String key = keyIter.next();
|
||||||
StandardItem item = childs.get(key);
|
StandardItem item = childs.get(key);
|
||||||
totalSize +=item.getBytesDataLength(charset);
|
totalSize +=item.getBytesDataLength(charset, root, childActivated);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case StandardType.GRID :
|
case StandardType.GRID :
|
||||||
@@ -1050,7 +1113,7 @@ public class StandardItem implements Serializable, Cloneable {
|
|||||||
while(keyIter.hasNext()) {
|
while(keyIter.hasNext()) {
|
||||||
String key = keyIter.next();
|
String key = keyIter.next();
|
||||||
StandardItem item = group.get(key);
|
StandardItem item = group.get(key);
|
||||||
totalSize +=item.getBytesDataLength(charset);
|
totalSize +=item.getBytesDataLength(charset, root, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1086,6 +1149,15 @@ public class StandardItem implements Serializable, Cloneable {
|
|||||||
return toByteArray(true, charset);
|
return toByteArray(true, charset);
|
||||||
}
|
}
|
||||||
public byte[] toByteArray(boolean withBizData, String charset) {
|
public byte[] toByteArray(boolean withBizData, String charset) {
|
||||||
|
return toByteArray(withBizData, charset, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param root 조건부 블록(refPath/refValue) 평가 기준이 되는 최상위 메시지
|
||||||
|
* @param parentActivated 조건 성립으로 활성화된 상위 GROUP 하위인지 여부
|
||||||
|
* @see #isFlatGroupActive(StandardMessage, boolean)
|
||||||
|
*/
|
||||||
|
public byte[] toByteArray(boolean withBizData, String charset, StandardMessage root, boolean parentActivated) {
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
Iterator<String> keyIter = null;
|
Iterator<String> keyIter = null;
|
||||||
// logger.debug("Encoding : {}", charset);
|
// logger.debug("Encoding : {}", charset);
|
||||||
@@ -1096,7 +1168,7 @@ public class StandardItem implements Serializable, Cloneable {
|
|||||||
while(keyIter.hasNext()) {
|
while(keyIter.hasNext()) {
|
||||||
String key = keyIter.next();
|
String key = keyIter.next();
|
||||||
StandardItem item = childs.get(key);
|
StandardItem item = childs.get(key);
|
||||||
bos.write(item.toByteArray(withBizData,charset)); //하위로 내려갈때 withBizDat 값이 없어져서 추가 jun's 20231101
|
bos.write(item.toByteArray(withBizData, charset, root, false)); //하위로 내려갈때 withBizDat 값이 없어져서 추가 jun's 20231101
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case StandardType.FIELD :
|
case StandardType.FIELD :
|
||||||
@@ -1118,16 +1190,18 @@ public class StandardItem implements Serializable, Cloneable {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case StandardType.GROUP :
|
case StandardType.GROUP :
|
||||||
// skip inactive/conditional group (size==0 means not activated)
|
// ref 조건이 성립하면 size==0 이어도 출력한다(FlatReader 와 동일 기준)
|
||||||
if( getSize() == 0 ) {
|
if( !isFlatGroupActive(root, parentActivated) ) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (isHidden) break;
|
// 출력하는 GROUP 의 하위 무조건 블록은 함께 출력한다.
|
||||||
|
// FlatReader 는 이 블록을 조건 없이 읽으므로 빼면 오프셋이 어긋난다.
|
||||||
|
boolean childActivated = true;
|
||||||
keyIter = childs.keySet().iterator();
|
keyIter = childs.keySet().iterator();
|
||||||
while(keyIter.hasNext()) {
|
while(keyIter.hasNext()) {
|
||||||
String key = keyIter.next();
|
String key = keyIter.next();
|
||||||
StandardItem item = childs.get(key);
|
StandardItem item = childs.get(key);
|
||||||
bos.write(item.toByteArray(withBizData,charset)); //하위로 내려갈때 withBizDat 값이 없어져서 추가 jun's 20231101
|
bos.write(item.toByteArray(withBizData, charset, root, childActivated)); //하위로 내려갈때 withBizDat 값이 없어져서 추가 jun's 20231101
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case StandardType.GRID :
|
case StandardType.GRID :
|
||||||
@@ -1145,7 +1219,7 @@ public class StandardItem implements Serializable, Cloneable {
|
|||||||
while(keyIter.hasNext()) {
|
while(keyIter.hasNext()) {
|
||||||
String key = keyIter.next();
|
String key = keyIter.next();
|
||||||
StandardItem item = group.get(key);
|
StandardItem item = group.get(key);
|
||||||
bos.write(item.toByteArray(withBizData, charset)); //하위로 내려갈때 withBizDat 값이 없어져서 추가 jun's 20231101
|
bos.write(item.toByteArray(withBizData, charset, root, false)); //하위로 내려갈때 withBizDat 값이 없어져서 추가 jun's 20231101
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -461,7 +461,8 @@ public class StandardMessage extends StandardItem {
|
|||||||
while(keyIter.hasNext()) {
|
while(keyIter.hasNext()) {
|
||||||
String key = keyIter.next();
|
String key = keyIter.next();
|
||||||
StandardItem item = childs.get(key);
|
StandardItem item = childs.get(key);
|
||||||
bos.write(item.toByteArray(withBizData, charset));
|
// this 를 root 로 넘겨 조건부 블록(refPath/refValue)을 평가하게 한다
|
||||||
|
bos.write(item.toByteArray(withBizData, charset, this, false));
|
||||||
}
|
}
|
||||||
return bos.toByteArray();
|
return bos.toByteArray();
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
@@ -490,7 +491,7 @@ public class StandardMessage extends StandardItem {
|
|||||||
while(keyIter.hasNext()) {
|
while(keyIter.hasNext()) {
|
||||||
String key = keyIter.next();
|
String key = keyIter.next();
|
||||||
StandardItem item = childs.get(key);
|
StandardItem item = childs.get(key);
|
||||||
totalSize +=item.getBytesDataLength(charset);
|
totalSize +=item.getBytesDataLength(charset, this, false);
|
||||||
}
|
}
|
||||||
return totalSize;
|
return totalSize;
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
@@ -516,7 +517,7 @@ public class StandardMessage extends StandardItem {
|
|||||||
while (keyIter.hasNext()) {
|
while (keyIter.hasNext()) {
|
||||||
String key = keyIter.next();
|
String key = keyIter.next();
|
||||||
StandardItem item = find.childs.get(key);
|
StandardItem item = find.childs.get(key);
|
||||||
totalSize += item.getBytesDataLength(this.bizDataCharset);
|
totalSize += item.getBytesDataLength(this.bizDataCharset, this, false);
|
||||||
}
|
}
|
||||||
return totalSize;
|
return totalSize;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -121,24 +121,15 @@ public class FlatReader implements StandardReader {
|
|||||||
logger.debug("@GROUP name={}, getRefPath={}, refItemValue=[{}], getRefValue=[{}]"
|
logger.debug("@GROUP name={}, getRefPath={}, refItemValue=[{}], getRefValue=[{}]"
|
||||||
, currentItem.getName(), currentItem.getRefPath(), refItemValue, currentItem.getRefValue());
|
, currentItem.getName(), currentItem.getRefPath(), refItemValue, currentItem.getRefValue());
|
||||||
|
|
||||||
if(refItemValue != null) refItemValue = refItemValue.trim();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 값의 경우의수 NM,EM,""(blank)
|
* 값의 경우의수 NM,EM,""(blank)
|
||||||
* ! : NOT 조건 (예: !EM → EM이 아닌 경우)
|
* ! : NOT 조건 (예: !EM → EM이 아닌 경우)
|
||||||
* | : OR 조건 (예: NM|EM → NM 또는 EM인 경우)
|
* | : OR 조건 (예: NM|EM → NM 또는 EM인 경우)
|
||||||
* !와 | 조합 가능 (예: !NM|EM → NM도 아니고 EM도 아닌 경우)
|
* !와 | 조합 가능 (예: !NM|EM → NM도 아니고 EM도 아닌 경우)
|
||||||
|
* 판정 기준은 StandardItem.matchRefCondition 하나로 통일한다.
|
||||||
|
* (FLAT 직렬화 StandardItem.isFlatGroupActive 와 같은 기준이어야 오프셋이 어긋나지 않음)
|
||||||
*/
|
*/
|
||||||
String refValue = currentItem.getRefValue();
|
boolean matched = StandardItem.matchRefCondition(currentItem.getRefValue(), refItemValue);
|
||||||
boolean isNot = refValue.startsWith("!");
|
|
||||||
if (isNot) {
|
|
||||||
refValue = refValue.substring(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean matched = matchRefValue(refValue, refItemValue);
|
|
||||||
if (isNot) {
|
|
||||||
matched = !matched;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!matched) {
|
if (!matched) {
|
||||||
logger.warn("@GROUP name={} SKIPPED.", currentItem.getName());
|
logger.warn("@GROUP name={} SKIPPED.", currentItem.getName());
|
||||||
@@ -307,21 +298,4 @@ public class FlatReader implements StandardReader {
|
|||||||
}
|
}
|
||||||
return refItemValue;
|
return refItemValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* refValue와 refItemValue를 비교하여 일치 여부를 반환
|
|
||||||
* | 구분자로 OR 조건 지원 (예: "NM|EM" → "NM" 또는 "EM"과 일치하면 true)
|
|
||||||
*/
|
|
||||||
private boolean matchRefValue(String refValue, String refItemValue) {
|
|
||||||
if (refValue.contains("|")) {
|
|
||||||
String[] values = refValue.split("\\|");
|
|
||||||
for (String val : values) {
|
|
||||||
if (val.equals(refItemValue)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return refValue.equals(refItemValue);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -119,6 +119,19 @@ public class JsonReader implements StandardReader {
|
|||||||
|
|
||||||
switch(currentNode.getNodeType()) {
|
switch(currentNode.getNodeType()) {
|
||||||
case OBJECT:
|
case OBJECT:
|
||||||
|
// refPath/refValue 는 "이 블록이 전문에 들어있는가" 를 판단하기 위한 조건이다.
|
||||||
|
// 고정길이(FLAT)는 블록의 존재를 알려주는 키가 없어 이 조건이 유일한 근거지만,
|
||||||
|
// JSON 은 키의 존재 자체가 답이다. 여기에 도달했다는 것은 수신 전문에 이 블록이
|
||||||
|
// 실제로 들어있다는 뜻이므로, 조건으로 다시 걸러내면 받은 데이터를 잃는다.
|
||||||
|
//
|
||||||
|
// 실제 사고(2026-09): 상대가 오류응답을 procs_rslt_dvcd=F 로 보내면서 요청응답구분
|
||||||
|
// dman_rspn_dvcd 는 요청값 S 를 그대로 에코백했다(표준은 응답 시 R). MSG 블록의
|
||||||
|
// 조건이 !S 라 MSG 이하가 통째로 버려졌고, 그 결과
|
||||||
|
// - MAIN_MSG 가 레이아웃 기본값으로 남아 오류가 "정상처리되었습니다." 로 보고되고
|
||||||
|
// - 거래로그는 StandardMessage 재직렬화본이라 MSG 부 이후가 통째로 누락됐다.
|
||||||
|
//
|
||||||
|
// 조건 불일치는 상대 헤더의 오류로 보고, 경고만 남기고 파싱은 계속한다.
|
||||||
|
// (FlatReader 는 조건이 존재 판단에 반드시 필요하므로 그대로 둔다.)
|
||||||
if( currentItem != null && currentItem.getSize() == 0
|
if( currentItem != null && currentItem.getSize() == 0
|
||||||
&& StringUtils.isNotBlank(currentItem.getRefPath())
|
&& StringUtils.isNotBlank(currentItem.getRefPath())
|
||||||
&& StringUtils.isNotBlank(currentItem.getRefValue()) ) {
|
&& StringUtils.isNotBlank(currentItem.getRefValue()) ) {
|
||||||
@@ -131,26 +144,21 @@ public class JsonReader implements StandardReader {
|
|||||||
|
|
||||||
if (refItemValue != null) refItemValue = refItemValue.trim();
|
if (refItemValue != null) refItemValue = refItemValue.trim();
|
||||||
String refValue = currentItem.getRefValue();
|
String refValue = currentItem.getRefValue();
|
||||||
if (refValue.startsWith("!")) { // 느낌표가 들어가면 다음에 나오는게 아닌경우
|
boolean negate = refValue.startsWith("!"); // 느낌표가 들어가면 다음에 나오는게 아닌경우
|
||||||
|
if (negate) {
|
||||||
refValue = refValue.substring(1);
|
refValue = refValue.substring(1);
|
||||||
if (refValue.equals(refItemValue)) {
|
}
|
||||||
logger.warn("@GROUP name={} SKIPPED.", currentItem.getName());
|
boolean matched = refValue.equals(refItemValue);
|
||||||
currentItem.setHidden(true);
|
if (negate) {
|
||||||
break;
|
matched = !matched;
|
||||||
} else {
|
}
|
||||||
|
if (!matched) {
|
||||||
|
logger.warn("@GROUP name={} 조건 불일치({}=[{}], 기대=[{}]). 수신 전문에 존재하므로 파싱은 계속한다.",
|
||||||
|
currentItem.getName(), currentItem.getRefPath(), refItemValue,
|
||||||
|
currentItem.getRefValue());
|
||||||
|
}
|
||||||
currentItem.setHidden(false);
|
currentItem.setHidden(false);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (!currentItem.getRefValue().equals(refItemValue)) {
|
|
||||||
if (logger.isDebugEnabled())
|
|
||||||
logger.warn("@GROUP name={} SKIPPED.", currentItem.getName());
|
|
||||||
currentItem.setHidden(true);
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
currentItem.setHidden(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Iterator<String> fieldNames = currentNode.fieldNames();
|
Iterator<String> fieldNames = currentNode.fieldNames();
|
||||||
while(fieldNames.hasNext()) {
|
while(fieldNames.hasNext()) {
|
||||||
fieldName = fieldNames.next();
|
fieldName = fieldNames.next();
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import com.eactive.eai.common.header.HeaderActionKeys;
|
|||||||
import com.eactive.eai.common.message.EAIMessage;
|
import com.eactive.eai.common.message.EAIMessage;
|
||||||
import com.eactive.eai.common.message.EAIMessageKeys;
|
import com.eactive.eai.common.message.EAIMessageKeys;
|
||||||
import com.eactive.eai.common.message.MessageType;
|
import com.eactive.eai.common.message.MessageType;
|
||||||
|
import com.eactive.eai.common.property.PropManager;
|
||||||
import com.eactive.eai.common.routing.Process;
|
import com.eactive.eai.common.routing.Process;
|
||||||
import com.eactive.eai.common.server.EAIServerManager;
|
import com.eactive.eai.common.server.EAIServerManager;
|
||||||
import com.eactive.eai.common.submessage.SubMessageManager;
|
import com.eactive.eai.common.submessage.SubMessageManager;
|
||||||
@@ -43,6 +44,12 @@ public abstract class DefaultProcess extends Process {
|
|||||||
protected static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
|
protected static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
|
||||||
protected static Logger esbLogger = Logger.getLogger(Logger.LOGGER_ESBFW);
|
protected static Logger esbLogger = Logger.getLogger(Logger.LOGGER_ESBFW);
|
||||||
|
|
||||||
|
/** 표준 오류응답 메시지 형식을 조회할 프로퍼티 그룹/키. 없으면 기본형식을 쓴다. */
|
||||||
|
public static final String PROP_GROUP = "DefaultProcess";
|
||||||
|
public static final String PROP_STD_ERR_MSG_FORMAT = "std.error.msg.format";
|
||||||
|
/** 인자 순서: 1=오류코드, 2=오류메시지, 3=오류상세 */
|
||||||
|
public static final String DEFAULT_STD_ERR_MSG_FORMAT = "[%s] %s";
|
||||||
|
|
||||||
protected String guidLogPrefix = this.getClass().getSimpleName() + "] ";
|
protected String guidLogPrefix = this.getClass().getSimpleName() + "] ";
|
||||||
|
|
||||||
protected static ThreadLocal local = new ThreadLocal(); // 경과시간 동기화를 위한 ThreadLocal
|
protected static ThreadLocal local = new ThreadLocal(); // 경과시간 동기화를 위한 ThreadLocal
|
||||||
@@ -689,7 +696,7 @@ public abstract class DefaultProcess extends Process {
|
|||||||
String errorCode = mapper.getErrorCode(resStandardMessage);
|
String errorCode = mapper.getErrorCode(resStandardMessage);
|
||||||
String errorMsg = StringUtils.trim(mapper.getErrorMsg(resStandardMessage));
|
String errorMsg = StringUtils.trim(mapper.getErrorMsg(resStandardMessage));
|
||||||
String errorDesc = StringUtils.trim(resStandardMessage.findItemValue("MSG.MAIN_MSG.outp_msg_desc"));
|
String errorDesc = StringUtils.trim(resStandardMessage.findItemValue("MSG.MAIN_MSG.outp_msg_desc"));
|
||||||
this.resEaiMsg.setRspErr("RECEAIINA001", String.format("[%s] %s (%s)", errorCode, errorMsg, errorDesc));
|
this.resEaiMsg.setRspErr("RECEAIINA001", formatStdErrorMessage(errorCode, errorMsg, errorDesc));
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
this.resEaiMsg.setRspErr("RECEAIINA001", "비표준 오류 응답 수신");
|
this.resEaiMsg.setRspErr("RECEAIINA001", "비표준 오류 응답 수신");
|
||||||
@@ -707,7 +714,7 @@ public abstract class DefaultProcess extends Process {
|
|||||||
String errorCode = mapper.getErrorCode(resStandardMessage);
|
String errorCode = mapper.getErrorCode(resStandardMessage);
|
||||||
String errorMsg = StringUtils.trim(mapper.getErrorMsg(resStandardMessage));
|
String errorMsg = StringUtils.trim(mapper.getErrorMsg(resStandardMessage));
|
||||||
String errorDesc = StringUtils.trim(resStandardMessage.findItemValue("MSG.MAIN_MSG.outp_msg_desc"));
|
String errorDesc = StringUtils.trim(resStandardMessage.findItemValue("MSG.MAIN_MSG.outp_msg_desc"));
|
||||||
this.resEaiMsg.setRspErr("RECEAIINA001", String.format("[%s] %s (%s)", errorCode, errorMsg, errorDesc));
|
this.resEaiMsg.setRspErr("RECEAIINA001", formatStdErrorMessage(errorCode, errorMsg, errorDesc));
|
||||||
} else {
|
} else {
|
||||||
this.resEaiMsg.setRspErr("RECEAIINA001", "비표준 오류 응답 수신");
|
this.resEaiMsg.setRspErr("RECEAIINA001", "비표준 오류 응답 수신");
|
||||||
}
|
}
|
||||||
@@ -720,6 +727,37 @@ public abstract class DefaultProcess extends Process {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 표준 오류응답 메시지를 조립한다.
|
||||||
|
*
|
||||||
|
* 형식은 PropManager 의 {@code DefaultProcess / std.error.msg.format} 에서 가져온다.
|
||||||
|
* 프로퍼티는 에이전트의 ReloadPropertyCommand 로 무중단 갱신될 수 있으므로,
|
||||||
|
* 캐시하지 않고 호출할 때마다 조회한다. 그룹이나 키가 없으면
|
||||||
|
* {@link #DEFAULT_STD_ERR_MSG_FORMAT} 를 쓴다.
|
||||||
|
*
|
||||||
|
* 형식 문자열은 운영에서 편집 가능하므로 잘못된 형식(예: %s 개수 불일치)이 들어올 수 있다.
|
||||||
|
* 이 메서드는 오류응답 경로에서 호출되므로 여기서 예외가 나면 오류 자체를 못 내려보낸다.
|
||||||
|
* 따라서 조립에 실패하면 경고만 남기고 기본형식으로 되돌린다.
|
||||||
|
*
|
||||||
|
* @param errorCode 오류코드 (형식 인자 1)
|
||||||
|
* @param errorMsg 오류메시지 (형식 인자 2)
|
||||||
|
* @param errorDesc 오류상세 (형식 인자 3)
|
||||||
|
*/
|
||||||
|
protected String formatStdErrorMessage(String errorCode, String errorMsg, String errorDesc) {
|
||||||
|
String format = PropManager.getInstance().getProperty(
|
||||||
|
PROP_GROUP, PROP_STD_ERR_MSG_FORMAT, DEFAULT_STD_ERR_MSG_FORMAT);
|
||||||
|
if (StringUtils.isBlank(format)) {
|
||||||
|
format = DEFAULT_STD_ERR_MSG_FORMAT;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return String.format(format, errorCode, errorMsg, errorDesc);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn(guidLogPrefix + " 표준 오류응답 형식이 잘못되어 기본형식으로 대체한다. ["
|
||||||
|
+ PROP_GROUP + "/" + PROP_STD_ERR_MSG_FORMAT + "=" + format + "] - " + e.getMessage());
|
||||||
|
return String.format(DEFAULT_STD_ERR_MSG_FORMAT, errorCode, errorMsg, errorDesc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isTgtTranCall() {
|
public boolean isTgtTranCall() {
|
||||||
return this.isTgtTran;
|
return this.isTgtTran;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user