feat: StandardMessageCoordinatorDJB에 callProp refValue 값 주입 기능 추가
- coordinateAfterCreateMessageByRule에서 StandardItem의 refValue가
\${callProp.키} 패턴이면 Properties에서 값을 꺼내 setValue로 설정한다.
- \${callProp.키:기본값} 구문으로 기본값 지정 가능
- 점(.) 구분으로 Properties/Map 중첩 구조 탐색 지원
- GROUP 타입 재귀 순회, GRID/FARRAY는 건너뜀
This commit is contained in:
@@ -5,7 +5,10 @@ import java.net.NetworkInterface;
|
|||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
@@ -15,6 +18,7 @@ import com.eactive.eai.common.util.MessageUtil;
|
|||||||
import com.eactive.eai.inbound.processor.ProcessVO;
|
import com.eactive.eai.inbound.processor.ProcessVO;
|
||||||
import com.eactive.eai.message.StandardItem;
|
import com.eactive.eai.message.StandardItem;
|
||||||
import com.eactive.eai.message.StandardMessage;
|
import com.eactive.eai.message.StandardMessage;
|
||||||
|
import com.eactive.eai.message.StandardType;
|
||||||
import com.eactive.eai.message.manager.StandardMessageManager;
|
import com.eactive.eai.message.manager.StandardMessageManager;
|
||||||
import com.eactive.eai.message.service.DefaultStandardMessageCoordinator;
|
import com.eactive.eai.message.service.DefaultStandardMessageCoordinator;
|
||||||
import com.eactive.eai.message.service.InterfaceMapper;
|
import com.eactive.eai.message.service.InterfaceMapper;
|
||||||
@@ -36,6 +40,10 @@ public class StandardMessageCoordinatorDJB extends DefaultStandardMessageCoordin
|
|||||||
private static final DateTimeFormatter FMT_DATE = DateTimeFormatter.ofPattern("yyyyMMdd");
|
private static final DateTimeFormatter FMT_DATE = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||||||
private static final DateTimeFormatter FMT_TIME_MILLIS = DateTimeFormatter.ofPattern("HHmmssSSS");
|
private static final DateTimeFormatter FMT_TIME_MILLIS = DateTimeFormatter.ofPattern("HHmmssSSS");
|
||||||
|
|
||||||
|
// ${callProp.key} 또는 ${callProp.key:기본값} 패턴
|
||||||
|
private static final Pattern CALL_PROP_PATTERN =
|
||||||
|
Pattern.compile("\\$\\{callProp\\.([^}:]+)(?::([^}]*))?\\}");
|
||||||
|
|
||||||
// HEAD 필드 경로
|
// HEAD 필드 경로
|
||||||
private static final String HEAD_FRST_MESG_DMAN_DT = "HEAD.frst_mesg_dman_dt";
|
private static final String HEAD_FRST_MESG_DMAN_DT = "HEAD.frst_mesg_dman_dt";
|
||||||
private static final String HEAD_FRST_MESG_DMAN_TIME = "HEAD.frst_mesg_dman_time";
|
private static final String HEAD_FRST_MESG_DMAN_TIME = "HEAD.frst_mesg_dman_time";
|
||||||
@@ -114,6 +122,9 @@ public class StandardMessageCoordinatorDJB extends DefaultStandardMessageCoordin
|
|||||||
if (ezdataItem != null && evaluateBlockCondition(standardMessage, ezdataItem)) {
|
if (ezdataItem != null && evaluateBlockCondition(standardMessage, ezdataItem)) {
|
||||||
setEzdataFields(standardMessage, ezdataItem, ipv4, mac);
|
setEzdataFields(standardMessage, ezdataItem, ipv4, mac);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// refValue="${callProp.키}" 필드에 Properties 값 주입
|
||||||
|
resolveCallPropRefValues(standardMessage, prop);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -232,6 +243,67 @@ public class StandardMessageCoordinatorDJB extends DefaultStandardMessageCoordin
|
|||||||
// private helpers
|
// private helpers
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
|
|
||||||
|
private void resolveCallPropRefValues(StandardMessage standardMessage, Properties prop) {
|
||||||
|
if (prop == null) return;
|
||||||
|
resolveCallPropInChilds(standardMessage.getChilds(), prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resolveCallPropInChilds(LinkedHashMap<String, StandardItem> childs, Properties prop) {
|
||||||
|
if (childs == null) return;
|
||||||
|
for (StandardItem item : childs.values()) {
|
||||||
|
int type = item.getType();
|
||||||
|
if (type == StandardType.FIELD) {
|
||||||
|
String refValue = item.getRefValue();
|
||||||
|
if (refValue == null) continue;
|
||||||
|
Matcher m = CALL_PROP_PATTERN.matcher(refValue);
|
||||||
|
if (m.matches()) {
|
||||||
|
String key = m.group(1);
|
||||||
|
String defaultVal = m.group(2) != null ? m.group(2) : "";
|
||||||
|
String resolved = resolveCallPropValue(prop, key);
|
||||||
|
item.setValue(resolved != null ? resolved : defaultVal);
|
||||||
|
}
|
||||||
|
} else if (type == StandardType.GROUP) {
|
||||||
|
resolveCallPropInChilds(item.getChilds(), prop);
|
||||||
|
}
|
||||||
|
// GRID, FARRAY, BIZDATA: 배열/업무데이터 영역은 건너뜀
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Properties/Map 중첩 구조에서 점(.) 구분 keyPath로 값을 탐색한다.
|
||||||
|
* Properties는 Map을 구현하므로 Map<?,?> 단일 메서드로 통일한다.
|
||||||
|
* - "aaa.bbb" : map.get("aaa")가 Map(Properties 포함)이면 그 안에서 "bbb" 재귀 조회
|
||||||
|
* - 중간 값이 Map이 아니면 전체 keyPath로 폴백 조회
|
||||||
|
* - 값을 찾지 못하면 null 반환
|
||||||
|
*/
|
||||||
|
private String resolveCallPropValue(Properties props, String keyPath) {
|
||||||
|
return resolveNestedValue(props, keyPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private String resolveNestedValue(Map<?, ?> map, String keyPath) {
|
||||||
|
int dotIdx = keyPath.indexOf('.');
|
||||||
|
if (dotIdx < 0) {
|
||||||
|
Object val = map.get(keyPath);
|
||||||
|
if (val == null) return null;
|
||||||
|
if (val instanceof String) return (String) val;
|
||||||
|
return val.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
String first = keyPath.substring(0, dotIdx);
|
||||||
|
String rest = keyPath.substring(dotIdx + 1);
|
||||||
|
Object val = map.get(first);
|
||||||
|
if (val instanceof Map) {
|
||||||
|
return resolveNestedValue((Map<?, ?>) val, rest);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 폴백: 전체 keyPath를 단일 키로 조회
|
||||||
|
Object direct = map.get(keyPath);
|
||||||
|
if (direct == null) return null;
|
||||||
|
if (direct instanceof String) return (String) direct;
|
||||||
|
return direct.toString();
|
||||||
|
}
|
||||||
|
|
||||||
private String getSysEnvDvcd(EAIServerManager server) {
|
private String getSysEnvDvcd(EAIServerManager server) {
|
||||||
if (server.isPEAIServer()) return "P"; // 운영
|
if (server.isPEAIServer()) return "P"; // 운영
|
||||||
if (server.isSEAIServer()) return "T"; // 검증/테스트
|
if (server.isSEAIServer()) return "T"; // 검증/테스트
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import static org.mockito.ArgumentMatchers.anyString;
|
|||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
@@ -20,6 +23,7 @@ import com.eactive.eai.common.monitor.EAIServiceMonitor;
|
|||||||
import com.eactive.eai.common.property.PropManager;
|
import com.eactive.eai.common.property.PropManager;
|
||||||
import com.eactive.eai.common.server.EAIServerManager;
|
import com.eactive.eai.common.server.EAIServerManager;
|
||||||
import com.eactive.eai.common.util.ApplicationContextProvider;
|
import com.eactive.eai.common.util.ApplicationContextProvider;
|
||||||
|
import com.eactive.eai.message.StandardItem;
|
||||||
import com.eactive.eai.message.StandardMessage;
|
import com.eactive.eai.message.StandardMessage;
|
||||||
import com.eactive.eai.message.manager.StandardMessageManager;
|
import com.eactive.eai.message.manager.StandardMessageManager;
|
||||||
import com.eactive.eai.message.service.InterfaceMapper;
|
import com.eactive.eai.message.service.InterfaceMapper;
|
||||||
@@ -354,4 +358,142 @@ class StandardMessageCoordinatorDJBTest {
|
|||||||
assertNotNull(scopLen, "msg_scop_len이 null");
|
assertNotNull(scopLen, "msg_scop_len이 null");
|
||||||
assertTrue(Integer.parseInt(scopLen.trim()) >= 0, "msg_scop_len은 0 이상이어야 함: " + scopLen);
|
assertTrue(Integer.parseInt(scopLen.trim()) >= 0, "msg_scop_len은 0 이상이어야 함: " + scopLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ================================================================
|
||||||
|
// resolveCallPropRefValues — ${callProp.키} 치환
|
||||||
|
// ================================================================
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void callProp_refValue_단순키_값_주입() throws Exception {
|
||||||
|
// chnl_dtls_clcd: CSV 기본값 없음, refValue를 ${callProp.CHNL_CD}로 주입
|
||||||
|
setRefValue(message.findItem("HEAD.chnl_dtls_clcd"), "${callProp.CHNL_CD}");
|
||||||
|
|
||||||
|
Properties prop = new Properties();
|
||||||
|
prop.setProperty("CHNL_CD", "MOB");
|
||||||
|
coordinator.coordinateAfterCreateMessageByRule(message, null, prop);
|
||||||
|
|
||||||
|
assertEquals("MOB", message.findItemValue("HEAD.chnl_dtls_clcd"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void callProp_refValue_기본값_키_없을때_기본값_사용() throws Exception {
|
||||||
|
setRefValue(message.findItem("HEAD.chnl_dtls_clcd"), "${callProp.MISSING:IB1}");
|
||||||
|
|
||||||
|
coordinator.coordinateAfterCreateMessageByRule(message, null, new Properties());
|
||||||
|
|
||||||
|
assertEquals("IB1", message.findItemValue("HEAD.chnl_dtls_clcd"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void callProp_refValue_기본값_키_있을때_prop값으로_덮어씀() throws Exception {
|
||||||
|
setRefValue(message.findItem("HEAD.chnl_dtls_clcd"), "${callProp.CHNL_CD:IB1}");
|
||||||
|
|
||||||
|
Properties prop = new Properties();
|
||||||
|
prop.setProperty("CHNL_CD", "ATM");
|
||||||
|
coordinator.coordinateAfterCreateMessageByRule(message, null, prop);
|
||||||
|
|
||||||
|
assertEquals("ATM", message.findItemValue("HEAD.chnl_dtls_clcd"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void callProp_refValue_prop_null이면_setValue_호출_안함() throws Exception {
|
||||||
|
setRefValue(message.findItem("HEAD.chnl_dtls_clcd"), "${callProp.CHNL_CD}");
|
||||||
|
|
||||||
|
coordinator.coordinateAfterCreateMessageByRule(message, null, null);
|
||||||
|
|
||||||
|
String val = message.findItemValue("HEAD.chnl_dtls_clcd");
|
||||||
|
assertTrue(val == null || val.trim().isEmpty(),
|
||||||
|
"prop=null이면 callProp refValue 치환하지 않아야 함: " + val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void callProp_refValue_패턴_불일치_필드는_skip() throws Exception {
|
||||||
|
setRefValue(message.findItem("HEAD.chnl_dtls_clcd"), "PLAIN");
|
||||||
|
|
||||||
|
Properties prop = new Properties();
|
||||||
|
prop.setProperty("chnl_dtls_clcd", "SHOULD_NOT_SET");
|
||||||
|
coordinator.coordinateAfterCreateMessageByRule(message, null, prop);
|
||||||
|
|
||||||
|
assertNotEquals("SHOULD_NOT_SET", message.findItemValue("HEAD.chnl_dtls_clcd"),
|
||||||
|
"패턴 불일치 refValue는 치환하지 않아야 함");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void callProp_refValue_여러_필드_동시_주입() throws Exception {
|
||||||
|
setRefValue(message.findItem("HEAD.chnl_dtls_clcd"), "${callProp.CHNL_CD}");
|
||||||
|
setRefValue(message.findItem("HEAD.xtis_cd"), "${callProp.XTIS_CD}");
|
||||||
|
|
||||||
|
Properties prop = new Properties();
|
||||||
|
prop.setProperty("CHNL_CD", "IB1");
|
||||||
|
prop.setProperty("XTIS_CD", "OBK");
|
||||||
|
coordinator.coordinateAfterCreateMessageByRule(message, null, prop);
|
||||||
|
|
||||||
|
assertEquals("IB1", message.findItemValue("HEAD.chnl_dtls_clcd"));
|
||||||
|
assertEquals("OBK", message.findItemValue("HEAD.xtis_cd"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void callProp_중첩_Properties_점경로_조회() throws Exception {
|
||||||
|
// prop.get("OUTBOUND") = subProps, subProps.getProperty("IF_ID") = "SVC001"
|
||||||
|
// → ${callProp.OUTBOUND.IF_ID} → "SVC001"
|
||||||
|
setRefValue(message.findItem("HEAD.chnl_dtls_clcd"), "${callProp.OUTBOUND.IF_CD}");
|
||||||
|
|
||||||
|
Properties subProps = new Properties();
|
||||||
|
subProps.setProperty("IF_CD", "IB1");
|
||||||
|
Properties prop = new Properties();
|
||||||
|
prop.put("OUTBOUND", subProps);
|
||||||
|
coordinator.coordinateAfterCreateMessageByRule(message, null, prop);
|
||||||
|
|
||||||
|
assertEquals("IB1", message.findItemValue("HEAD.chnl_dtls_clcd"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void callProp_중첩_Map_점경로_조회() throws Exception {
|
||||||
|
// prop.get("CTX") = Map{"CHNL":"MOB"}
|
||||||
|
// → ${callProp.CTX.CHNL} → "MOB"
|
||||||
|
setRefValue(message.findItem("HEAD.chnl_dtls_clcd"), "${callProp.CTX.CHNL}");
|
||||||
|
|
||||||
|
Map<String, Object> ctxMap = new HashMap<>();
|
||||||
|
ctxMap.put("CHNL", "MOB");
|
||||||
|
Properties prop = new Properties();
|
||||||
|
prop.put("CTX", ctxMap);
|
||||||
|
coordinator.coordinateAfterCreateMessageByRule(message, null, prop);
|
||||||
|
|
||||||
|
assertEquals("MOB", message.findItemValue("HEAD.chnl_dtls_clcd"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void callProp_중간값_비Properties_폴백_전체키_조회() throws Exception {
|
||||||
|
// prop.get("aaa") = "plain" (Properties가 아님) → 전체 키 "aaa.bbb" 폴백
|
||||||
|
setRefValue(message.findItem("HEAD.chnl_dtls_clcd"), "${callProp.aaa.bbb}");
|
||||||
|
|
||||||
|
Properties prop = new Properties();
|
||||||
|
prop.setProperty("aaa", "plain");
|
||||||
|
prop.setProperty("aaa.bbb", "ATM");
|
||||||
|
coordinator.coordinateAfterCreateMessageByRule(message, null, prop);
|
||||||
|
|
||||||
|
assertEquals("ATM", message.findItemValue("HEAD.chnl_dtls_clcd"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void callProp_중첩_키_없을때_기본값_사용() throws Exception {
|
||||||
|
setRefValue(message.findItem("HEAD.chnl_dtls_clcd"), "${callProp.OUTBOUND.MISSING:IB1}");
|
||||||
|
|
||||||
|
Properties subProps = new Properties(); // MISSING 키 없음
|
||||||
|
Properties prop = new Properties();
|
||||||
|
prop.put("OUTBOUND", subProps);
|
||||||
|
coordinator.coordinateAfterCreateMessageByRule(message, null, prop);
|
||||||
|
|
||||||
|
assertEquals("IB1", message.findItemValue("HEAD.chnl_dtls_clcd"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------
|
||||||
|
// 헬퍼
|
||||||
|
// ----------------------------------------------------------------
|
||||||
|
|
||||||
|
private static void setRefValue(StandardItem item, String refValue) throws Exception {
|
||||||
|
Field f = StandardItem.class.getDeclaredField("refValue");
|
||||||
|
f.setAccessible(true);
|
||||||
|
f.set(item, refValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user