Merge branch '알람기능_및_UMS_연동' into jenkins_with_weblogic

This commit is contained in:
cho
2026-02-11 13:25:08 +09:00
25 changed files with 1067 additions and 11 deletions
@@ -6,6 +6,11 @@ import com.eactive.eai.common.lifecycle.LifecycleListener;
import com.eactive.eai.common.lifecycle.LifecycleSupport;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.custom.alarm.AlarmStateManager;
import com.eactive.eai.custom.alarm.condition.AlarmCondition;
import com.eactive.eai.custom.alarm.event.AlarmEvent;
import com.eactive.eai.custom.alarm.key.CoreAlarmKey;
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
@@ -71,6 +76,28 @@ public class CircuitBreakerManager implements Lifecycle {
circuitBreakerRegistry = CircuitBreakerRegistry.of(config);
TaggedCircuitBreakerMetrics.ofCircuitBreakerRegistry(circuitBreakerRegistry).bindTo(meterRegistry);
circuitBreakerRegistry.getEventPublisher().onEntryAdded(event -> {
CircuitBreaker cb = event.getAddedEntry();
String apiId = cb.getName();
// 여기서 상태 변경 리스너를 한 번만 등록
cb.getEventPublisher().onStateTransition(stateEvent -> {
CircuitBreaker.State from = stateEvent.getStateTransition().getFromState();
CircuitBreaker.State to = stateEvent.getStateTransition().getToState();
AlarmEvent alarmEvent = AlarmEvent.builder().key(CoreAlarmKey.CircuitBreaker)
.condition(AlarmCondition.builder().build())
.message(String.format("CircuitBreaker state changed. apiId=%s, %s -> %s", apiId, from, to))
.build();
AlarmStateManager.getAlarmStateManager().onEvnet(alarmEvent);
logger.warn("CircuitBreaker state changed. apiId={}, {} -> {}", apiId, from, to);
});
logger.info("Registered StateTransition listener for: {}", apiId);
});
logger.info("init CircuitBreaker - "+config);
}
@@ -0,0 +1,10 @@
package com.eactive.eai.common.util;
import com.google.gson.Gson;
public final class Jsons {
private Jsons() {}
public static final Gson GSON = new Gson();
}
@@ -0,0 +1,132 @@
package com.eactive.eai.custom.alarm;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import org.springframework.stereotype.Service;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.Jsons;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.custom.alarm.key.AlarmKey;
import com.eactive.eai.custom.alarm.key.DynamicAlarmKey;
import com.eactive.eai.custom.alarm.policy.AlarmPolicy;
import com.eactive.eai.custom.alarm.policy.CounterPolicy;
import com.eactive.eai.custom.alarm.policy.SendPolicy;
import com.eactive.eai.custom.alarm.policy.TimerPolicy;
import com.google.gson.JsonObject;
@Service
public class AlarmService {
protected static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
final static String ALARM_GROUP = "Alarm";
final static String ALARM_KEY = "{ALARM}";
final static String ALARM_RECIVER = "{RECIVER}";
final static String ALARM_EABLE = "enable.alarm";
public Map<AlarmKey, AlarmPolicy> getAlarmPolicy() {
Map<AlarmKey, AlarmPolicy> policyMap = new HashMap<AlarmKey, AlarmPolicy>();
try {
Properties props = PropManager.getInstance().getProperties(ALARM_GROUP);
props.forEach((key, value) -> {
String keyName = (String) key;
String configStr = (String) value;
if (keyName.contains(ALARM_KEY)) {
AlarmPolicy newPolicy = buildAlarmPolicy(keyName, configStr);
policyMap.put(newPolicy.getAlarmKey(), newPolicy);
}
});
} catch (Exception e) {
logger.warn("getAlarmPolicy fail.", e);
return Collections.EMPTY_MAP;
}
return policyMap;
}
public boolean isAlarmEnabled() {
return Optional.ofNullable(PropManager.getInstance().getProperty(ALARM_GROUP, ALARM_EABLE))
.map(Boolean::valueOf).orElse(false);
}
private AlarmPolicy buildAlarmPolicy(String key, String configJsonStr) {
JsonObject alarmConfig = Jsons.GSON.fromJson(configJsonStr, JsonObject.class);
String policyType = alarmConfig.get("type").getAsString();
int thresholdCount = Optional.ofNullable(alarmConfig.get("thresholdCount")).map(i -> i.getAsInt()).orElse(5);
int timeWindowSec = Optional.ofNullable(alarmConfig.get("timeWindowSec")).map(i -> i.getAsInt()).orElse(60);
AlarmPolicy alarmPolicy;
if ("count".equals(policyType)) {
alarmPolicy = CounterPolicy.builder().alarmKey(new DynamicAlarmKey(key)).thresholdCount(thresholdCount)
.build();
} else {
alarmPolicy = TimerPolicy.builder().alarmKey(new DynamicAlarmKey(key)).timeWindowMs(timeWindowSec * 1000)
.build();
}
return alarmPolicy;
}
public Map<AlarmKey, SendPolicy> getSendPolicy() {
Map<AlarmKey, SendPolicy> policyMap = new HashMap<AlarmKey, SendPolicy>();
try {
Properties props = PropManager.getInstance().getProperties(ALARM_GROUP);
props.forEach((key, value) -> {
String keyName = (String) key;
String configStr = (String) value;
if (!keyName.contains(ALARM_RECIVER))
return;
SendPolicy newPolicy = buildSendPolicy(keyName, configStr);
policyMap.put(newPolicy.getAlarmKey(), newPolicy);
});
} catch (Exception e) {
logger.warn("getSendPolicy fail.", e);
return Collections.EMPTY_MAP;
}
return policyMap;
}
private SendPolicy buildSendPolicy(String key, String configStr) {
List<String> reciverList = Optional.ofNullable(configStr).map(t -> Arrays.asList(t.split(",")))
.orElse(Collections.EMPTY_LIST);
key = key.replace(ALARM_RECIVER, "").trim() + "{ALARM}";
SendPolicy sendPolicy = new SendPolicy(key, reciverList);
return sendPolicy;
}
}
@@ -0,0 +1,145 @@
package com.eactive.eai.custom.alarm;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.eactive.eai.common.util.ApplicationContextProvider;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.custom.alarm.event.AlarmEvent;
import com.eactive.eai.custom.alarm.key.AlarmKey;
import com.eactive.eai.custom.alarm.policy.AlarmPolicy;
import com.eactive.eai.custom.alarm.policy.SendPolicy;
import com.eactive.eai.custom.alarm.ums.UmsService;
import com.eactive.eai.custom.alarm.ums.payload.ObpMonitoringUmsTemplate;
import com.eactive.eai.custom.alarm.ums.payload.UmsRequestPayload;
@Component
public class AlarmStateManager {
List<AlarmEvent> registerAlarmList = new ArrayList<AlarmEvent>();
private final ScheduledExecutorService scheduler;
protected static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
private static AlarmStateManager alarmStateManager;
@Autowired
AlarmService alarmService;
@Autowired
UmsService usmService;
public static AlarmStateManager getAlarmStateManager() {
if (alarmStateManager == null) {
alarmStateManager = ApplicationContextProvider.getContext().getBean(AlarmStateManager.class);
}
return alarmStateManager;
}
public AlarmStateManager() {
this.scheduler = Executors.newSingleThreadScheduledExecutor(r -> {
Thread t = new Thread(r);
t.setName("AlarmStateManager-worker");
t.setDaemon(false);
return t;
});
scheduler.scheduleWithFixedDelay(() -> {
try {
// === 여기에 지속 실행할 로직 ===
fireAlarm();
} catch (Throwable e) {
// 반드시 예외 잡기 (안 잡으면 스케줄 자체가 죽음)
logger.error("fireAlarm fail", e);
}
}, 0, 5, TimeUnit.SECONDS); // 최초 0초, 이후 5초 간격
}
public void onEvnet(AlarmEvent alaramEvent) {
Optional<AlarmEvent> existEvent = registerAlarmList.stream().filter(e -> e.equals(alaramEvent)).findFirst();
if (existEvent.isPresent()) {
AlarmEvent event = existEvent.get();
event.onOcurr();
} else {
registerAlarmList.add(alaramEvent);
}
}
private void fireAlarm() {
Map<AlarmKey, AlarmPolicy> alarmPolicyMap = alarmService.getAlarmPolicy();
Map<AlarmKey, SendPolicy> sendPolicyMap = alarmService.getSendPolicy();
Iterator<AlarmEvent> iterator = registerAlarmList.iterator();
while (iterator.hasNext()) {
AlarmEvent event = iterator.next();
AlarmPolicy alarmPolicy = alarmPolicyMap.get(event.getKey());
if (alarmPolicy != null && alarmPolicy.isAlarmTriggered(event)) {
// 알람 발송 로직
SendPolicy sendPolicy = sendPolicyMap.get(event.getKey());
if (alarmService.isAlarmEnabled()) {
send(sendPolicy, event);
} else {
logger.info("Alarm is disabled, event = {}, policy = {}", event, sendPolicy);
}
// 리스트에서 안전하게 제거
iterator.remove();
}else { //트리거 조건이 안되더라도, 이벤트발생한지 오래 되었으면 제거하여 OOM 예방.
long firstOcurredAt = event.getCondition().getOcurredAt();
long diffInMs = System.currentTimeMillis() - firstOcurredAt;
if ( diffInMs >= TimeUnit.MINUTES.toMillis(60) ) { //1시간 지난거면 삭제
iterator.remove();
}
}
}
}
private void send(SendPolicy sendPolicy, AlarmEvent event) {
UmsRequestPayload payload = null;
try {
List<String> cellPhoneList = sendPolicy.getCellPhoneList();
for (String cellPhone : cellPhoneList) {
payload = UmsRequestPayload.builder()
.content(ObpMonitoringUmsTemplate.builder().message(event.getMessage()).build())
.cellphone(cellPhone).build();
usmService.send(payload);
}
} catch (Exception e) {
logger.error(String.format("Alarm message delivery failed (UMS), payload=%s", payload), e);
}
}
}
@@ -0,0 +1,21 @@
package com.eactive.eai.custom.alarm.condition;
import lombok.Builder;
import lombok.Getter;
import lombok.ToString;
@Builder
@Getter
@ToString
public class AlarmCondition {
final long ocurredAt = System.currentTimeMillis();
long lastOcurredAt; // 일단, 미사용
@Builder.Default
long count = 1;
public void onOcurr() {
this.lastOcurredAt = System.currentTimeMillis();
this.count++;
}
}
@@ -0,0 +1,5 @@
package com.eactive.eai.custom.alarm.condition;
public enum AlarmLevel {
ERROR, WARN, FATAL
}
@@ -0,0 +1,44 @@
package com.eactive.eai.custom.alarm.event;
import java.util.Objects;
import com.eactive.eai.custom.alarm.condition.AlarmCondition;
import com.eactive.eai.custom.alarm.key.AlarmKey;
import com.eactive.eai.custom.alarm.policy.AlarmPolicy;
import com.eactive.eai.custom.alarm.policy.CounterPolicy;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Setter
@Getter
@Builder
@ToString
public class AlarmEvent {
AlarmKey key;
AlarmCondition condition;
String message;
public void onOcurr() {
condition.onOcurr();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof AlarmEvent)) return false;
AlarmEvent that = (AlarmEvent) o;
return Objects.equals(this.key, that.key)
&& Objects.equals(this.message, that.message);
}
@Override
public int hashCode() {
return Objects.hash(key, message);
}
}
@@ -0,0 +1,6 @@
package com.eactive.eai.custom.alarm.key;
public interface AlarmKey {
String code();
}
@@ -0,0 +1,12 @@
package com.eactive.eai.custom.alarm.key;
public final class CoreAlarmKey {
private CoreAlarmKey() { }
public static final AlarmKey CircuitBreaker = new DynamicAlarmKey("CircuitBreaker{ALARM}");
public static final AlarmKey Timeout = new DynamicAlarmKey("Timeout{ALARM}");
}
@@ -0,0 +1,33 @@
package com.eactive.eai.custom.alarm.key;
import lombok.ToString;
@ToString
public final class DynamicAlarmKey implements AlarmKey {
private final String code;
public DynamicAlarmKey(String code) {
if (code == null || code.isEmpty()) {
throw new IllegalArgumentException("AlarmKey code must not be empty");
}
this.code = code;
}
@Override
public String code() {
return code;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof AlarmKey)) return false;
return code.equals(((AlarmKey) o).code());
}
@Override
public int hashCode() {
return code.hashCode();
}
}
@@ -0,0 +1,71 @@
package com.eactive.eai.custom.alarm.policy;
import com.eactive.eai.custom.alarm.event.AlarmEvent;
import com.eactive.eai.custom.alarm.key.AlarmKey;
public abstract class AlarmPolicy {
private AlarmKey alarmKey;
private int thresholdCount;
private long timeWindowMs;
protected AlarmPolicy(Builder<?> builder) {
this.alarmKey = builder.alarmKey;
this.thresholdCount = builder.thresholdCount;
this.timeWindowMs = builder.timeWindowMs;
}
public AlarmKey getAlarmKey() {
return alarmKey;
}
public int getThresholdCount() {
return thresholdCount;
}
public long getTimeWindowMs() {
return timeWindowMs;
}
public abstract boolean isAlarmTriggered(AlarmEvent event);
public boolean hasElapsed(long baseTimeMs) {
return System.currentTimeMillis() - baseTimeMs >= timeWindowMs;
}
public boolean hasExceededLimit(long count) {
return count >= thresholdCount;
}
public abstract static class Builder<T extends Builder<T>> {
private AlarmKey alarmKey;
private int thresholdCount;
private long timeWindowMs;
public T alarmKey(AlarmKey alarmKey) {
this.alarmKey = alarmKey;
return self();
}
public T thresholdCount(int thresholdCount) {
this.thresholdCount = thresholdCount;
return self();
}
public T timeWindowMs(long timeWindowMs) {
this.timeWindowMs = timeWindowMs;
return self();
}
protected abstract T self();
abstract AlarmPolicy build();
}
}
@@ -0,0 +1,5 @@
package com.eactive.eai.custom.alarm.policy;
public enum AlarmType {
COUNT, TIMER
}
@@ -0,0 +1,39 @@
package com.eactive.eai.custom.alarm.policy;
import com.eactive.eai.custom.alarm.event.AlarmEvent;
public class CounterPolicy extends AlarmPolicy {
private CounterPolicy(Builder builder) {
super(builder);
}
@Override
public boolean isAlarmTriggered(AlarmEvent event) {
if (!event.getKey().equals(getAlarmKey())) {
return false;
}
long count = event.getCondition().getCount();
return hasExceededLimit(count);
}
public static Builder builder() {
return new Builder();
}
public static class Builder extends AlarmPolicy.Builder<Builder> {
@Override
protected Builder self() {
return this;
}
@Override
public CounterPolicy build() {
return new CounterPolicy(this);
}
}
}
@@ -0,0 +1,23 @@
package com.eactive.eai.custom.alarm.policy;
import java.util.List;
import com.eactive.eai.custom.alarm.key.AlarmKey;
import com.eactive.eai.custom.alarm.key.DynamicAlarmKey;
import lombok.Getter;
import lombok.ToString;
@ToString
@Getter
public class SendPolicy {
final AlarmKey alarmKey;
final List<String> cellPhoneList;
public SendPolicy(String alarmKey, List<String> cellPhoneList) {
this.alarmKey = new DynamicAlarmKey(alarmKey);
this.cellPhoneList = cellPhoneList;
}
}
@@ -0,0 +1,51 @@
package com.eactive.eai.custom.alarm.policy;
import com.eactive.eai.custom.alarm.condition.AlarmCondition;
import com.eactive.eai.custom.alarm.event.AlarmEvent;
import com.eactive.eai.custom.alarm.policy.CounterPolicy.Builder;
public class TimerPolicy extends AlarmPolicy {
private TimerPolicy(Builder builder) {
super(builder);
}
@Override
public boolean isAlarmTriggered(AlarmEvent event) {
if (event.getKey().equals(getAlarmKey())) {
AlarmCondition condition = event.getCondition();
long baseTimeMs = condition.getOcurredAt();
if (hasElapsed(baseTimeMs)) {
return true;
} else {
return false;
}
}
return false;
}
public static Builder builder() {
return new Builder();
}
public static class Builder extends AlarmPolicy.Builder<Builder> {
@Override
protected Builder self() {
return this;
}
@Override
public TimerPolicy build() {
return new TimerPolicy(this);
}
}
}
@@ -0,0 +1,18 @@
package com.eactive.eai.custom.alarm.ums;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class UmsCallResult {
private String umsMessageId;
private boolean isSuccess;
private String returnCode;
private String returnMessage;
private String returnPayload;
private Throwable throwable;
}
@@ -0,0 +1,62 @@
package com.eactive.eai.custom.alarm.ums;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.custom.alarm.ums.payload.UmsRequestPayload;
@Component
public class UmsSendManager {
private final ScheduledExecutorService scheduler;
protected static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
Queue<UmsRequestPayload> queue = new ConcurrentLinkedQueue<UmsRequestPayload>();
@Autowired
UmsService umsService;
public UmsSendManager() {
this.scheduler = Executors.newSingleThreadScheduledExecutor(r -> {
Thread t = new Thread(r);
t.setName("AlarmStateManager-worker");
t.setDaemon(false);
return t;
});
scheduler.scheduleWithFixedDelay(() -> {
try {
// === 여기에 지속 실행할 로직 ===
sendMessage();
} catch (Throwable e) {
// 반드시 예외 잡기 (안 잡으면 스케줄 자체가 죽음)
logger.error("fireAlarm fail", e);
}
}, 0, 5, TimeUnit.SECONDS); // 최초 0초, 이후 5초 간격
}
void sendMessage() throws Exception {
UmsRequestPayload payload;
while ((payload = queue.poll()) != null) {
umsService.send(payload);
}
}
public void enqueue(UmsRequestPayload payload) {
queue.offer(payload);
}
}
@@ -0,0 +1,135 @@
package com.eactive.eai.custom.alarm.ums;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.entity.EntityBuilder;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.ProtocolException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.util.Timeout;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.Jsons;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.custom.alarm.ums.payload.UmsRequestPayload;
import com.eactive.eai.custom.alarm.ums.payload.ValidationUtil;
import com.google.gson.JsonSyntaxException;
@Component
public class UmsService {
protected static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
private final static String UMS_GROUP_PROP_NAME = "UMS";
private final static String UMS_PROP_HOST = "host";
private final static String UMS_PROP_URL = "url";
private final static String UMS_PROP_API_KEY = "apiKey";
private final static String UMS_URL = "http://172.31.35.144:9021";
private final static String API_KEY = "7cca6d58-e4f7-474d-9edd-525806bc99ff";
private final static String SEND_URL = "/ums/openapi/send";
private final static String CONTENT_TYPE = "application/json; charset=utf-8";
private final static String AUTHORIZATION_FMT = "Bearer %s";
public Future<UmsCallResult> send(UmsRequestPayload payload) throws Exception {
if (payload.getContent() == null) {
throw new IllegalArgumentException("content is null");
}
try {
ValidationUtil.validateOrThrow(payload);
} catch (Exception ex) {
logger.debug(String.format("UMS Request payload validation failed - %s", payload));
throw ex;
}
return call(payload);
}
private CloseableHttpClient createHttpClient() {
return HttpClients.createDefault();
}
private Future<UmsCallResult> call(UmsRequestPayload requestPayload) throws Exception {
ExecutorService executor = Executors.newSingleThreadExecutor();
String json = Jsons.GSON.toJson(requestPayload);
String host = PropManager.getInstance().getProperty(UMS_GROUP_PROP_NAME, UMS_PROP_HOST, UMS_URL);
String url = PropManager.getInstance().getProperty(UMS_GROUP_PROP_NAME, UMS_PROP_URL, SEND_URL);
String apiKey = PropManager.getInstance().getProperty(UMS_GROUP_PROP_NAME, UMS_PROP_API_KEY, API_KEY);
HttpPost httpPost = new HttpPost(host + url);
httpPost.setHeader("Content-Type", CONTENT_TYPE);
httpPost.setHeader("Authorization", String.format(AUTHORIZATION_FMT, apiKey));
httpPost.setEntity(EntityBuilder.create().setText(json).setContentType(ContentType.APPLICATION_JSON).build());
if (logger.isDebugEnabled()) {
try {
logger.debug("Header - {}", httpPost.getHeader("Content-Type"));
logger.debug("Header - {}", httpPost.getHeader("Authorization"));
logger.debug("Request Body : {}",
StreamUtils.copyToString(httpPost.getEntity().getContent(), Charset.defaultCharset()));
} catch (ProtocolException | IOException e) {
throw new RuntimeException(e);
}
}
httpPost.setConfig(RequestConfig.custom().setConnectTimeout(Timeout.ofSeconds(3))
.setResponseTimeout(Timeout.ofSeconds(3)).build());
Future<UmsCallResult> future;
try (CloseableHttpClient httpClient = createHttpClient()) {
future = executor.submit(() -> {
return httpClient.execute(httpPost, res -> {
try {
UmsCallResult result;
String body = EntityUtils.toString(res.getEntity());
if (logger.isDebugEnabled()) {
logger.debug("Response - {}", body);
}
result = Jsons.GSON.fromJson(body, UmsCallResult.class);
result.setUmsMessageId(requestPayload.getMessageIdentityNo());
result.setSuccess(true);
return result;
} catch (JsonSyntaxException e) {
logger.error("UMS Call error(JSON Syntax)", e);
return UmsCallResult.builder().umsMessageId(requestPayload.getMessageIdentityNo())
.isSuccess(false).throwable(e).build();
} catch (Exception e) {
logger.error("UMS Call error", e);
return UmsCallResult.builder().umsMessageId(requestPayload.getMessageIdentityNo())
.isSuccess(false).throwable(e).build();
} finally {
logger.debug("Response status - {}", res.getCode());
if (res.getCode() != 200) {
Arrays.asList(res.getHeaders()).forEach(o -> {
logger.warn("[Header] {}: {}", o.getName(), o.getValue());
});
}
}
});
});
return future;
} catch (Exception e) {
logger.error("UMS call exception", e);
throw new Exception("UMS call failed", e);
} finally {
executor.shutdown();
}
}
}
@@ -0,0 +1,7 @@
package com.eactive.eai.custom.alarm.ums.payload;
/**
* GSON Serialize 용 Object
*/
public interface IUmsGsonObject {
}
@@ -0,0 +1,12 @@
package com.eactive.eai.custom.alarm.ums.payload;
import com.google.gson.annotations.SerializedName;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class ObpMonitoringUmsTemplate implements IUmsGsonObject {
@SerializedName("MOTR_EVAL_CNTN")
private String message;
}
@@ -0,0 +1,19 @@
package com.eactive.eai.custom.alarm.ums.payload;
import lombok.Getter;
public enum UmsBizWorkCode {
MONITORING("B_ELFN_09510", "OBP 모니터링"),
VERIFY_PHONE("S55_ICT_0006", "API Portal 인증번호"),
;
@Getter
private String code;
@Getter
private String displayName;
UmsBizWorkCode(String code, String displayName) {
this.code = code;
this.displayName = displayName;
}
}
@@ -0,0 +1,105 @@
package com.eactive.eai.custom.alarm.ums.payload;
import com.google.gson.annotations.SerializedName;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Data
@Builder
@Getter
public class UmsRequestPayload {
// MSG_IDNT_NO string 메시지식별번호 50 O "발송 구분을 위한 거래 번호 (GUID 권고)
//GUID로 입력이 어려울시
//어플리케이션코드(3자리)+난수 로 유니크한 ID로 채번 권고"
@Length(min = 1, max = 50)
@NotEmpty
@SerializedName("MSG_IDNT_NO")
@Builder.Default
private String messageIdentityNo = String.format("APM-%s",DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS").format(LocalDateTime.now()));
// MSG_BZWK_CD string 메시지업무코드 30 O UMS에 등록된 메시지업무코드(AS-IS 템플릿ID)
@NotEmpty
@Length(min = 1, max = 30)
@SerializedName("MSG_BZWK_CD")
@Builder.Default
private String msgBizWorkCode = UmsBizWorkCode.MONITORING.getCode();
// 메시지업무파라미터내용 / 4000 / JSONObject
// MSG_BZWK_PARAM_CTNT object 메시지업무파라미터내용 4000 메시지업무코드의 템플릿에서 사용하는 변수와 변수값
@NotNull
@SerializedName("MSG_BZWK_PARAM_CTNT")
private IUmsGsonObject content;
// 발송예약일시 / 14 / 실 발송 일자(yyyyMMddHHmmss)
// SND_RESR_DTTM string 발송예약일시 14 20240418111500 O 실 발송 일자(현재시간)
@Pattern(regexp = "^[0-9]{14}$")
@NotEmpty
@SerializedName("SND_RESR_DTTM")
@Builder.Default
private String sendReservationDatetime = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now());
// 휴대전화번호 / 44 / 휴대폰번호(평문, 암호화 둘다가능)
// CPNO string 휴대전화번호 44 "암호화 또는 평문 (safedb not rrno)
//문자일 시 암호문
//숫자일 시 전화번호
//고객번호 또는 휴대전화번호 필수"
@NotEmpty
@Pattern(regexp = "^[0-9]{10,11}$")
@SerializedName("CPNO")
private String cellphone;
// 발송부점코드 / 4 /
// SND_BRCD string 발송부점코드 4 O 0333
@NotEmpty
@SerializedName("SND_BRCD")
@Builder.Default
private String sendBranchCode = "0990";
// 발송직원번호 / 7 / ECEB057(텔러번호?)
// SND_EMPNO string 발송직원번호 7 O ? 넣긴해야함
@NotEmpty
@SerializedName("SND_EMPNO")
@Builder.Default
private String sendEmployeeNo = "ECEB057";
// SRVC_ID string 서비스ID 11 요청단 측 서비스ID
@NotEmpty
@SerializedName("SRVC_ID")
@Builder.Default
private String serviceId = "eAPIM";
// MSG_SND_CHNL_CD string 메시지발송채널코드 2
@SerializedName("MSG_SND_CHNL_CD")
@NotNull
@Builder.Default
private ChannelCode msgSndChnlCd = ChannelCode.KM;
// VALDTN_RSLT_CD string 검증결과코드 2 00으로 고정
@SerializedName("VALDTN_RSLT_CD")
@NotEmpty
@Builder.Default
private String validationResultCode = "00";
public enum ChannelCode {
SM, // SMS
LM, // LMS
MM, // MMS
KM, // 카카오-알림톡
FM, // 카카오-친구톡
SR, // RCS-SMS
LR, // RCS-LMS
MR, // RCS-MMS
TR, // RCS-TEMPLATE
IR, // RCS-IMAGE Template
PM // Push
}
}
@@ -0,0 +1,53 @@
package com.eactive.eai.custom.alarm.ums.payload;
import java.util.Set;
import java.util.stream.Collectors;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator;
public class ValidationUtil {
private static final ValidatorFactory factory;
private static final Validator validator;
static {
factory = Validation.byDefaultProvider()
.configure()
.messageInterpolator(new ParameterMessageInterpolator())
.buildValidatorFactory();
validator = factory.getValidator();
}
private ValidationUtil() {
}
public static <T> Set<ConstraintViolation<T>> validate(T target) {
return validator.validate(target);
}
public static <T> void validateOrThrow(T target) {
Set<ConstraintViolation<T>> violations = validate(target);
if (!violations.isEmpty()) {
String message = violations.stream()
.map(v -> v.getPropertyPath() + " : " + v.getMessage())
.collect(Collectors.joining(", "));
throw new ConstraintViolationException("Validation failed: " + message, violations);
}
}
public static <T> String getViolationMessage(T target) {
Set<ConstraintViolation<T>> violations = validate(target);
if (violations.isEmpty()) return null;
return violations.stream()
.map(v -> v.getPropertyPath() + " : " + v.getMessage())
.collect(Collectors.joining(", "));
}
}
@@ -0,0 +1,12 @@
package com.eactive.eai.custom.alarm.ums.payload;
import com.google.gson.annotations.SerializedName;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class VerifyPhoneUmsTemplate implements IUmsGsonObject {
@SerializedName("ATHN_NO")
private String authNumber;
}
@@ -42,6 +42,11 @@ import com.eactive.eai.common.worker.ResponseMap;
import com.eactive.eai.control.HttpSender;
import com.eactive.eai.control.LogSender;
import com.eactive.eai.control.Transform;
import com.eactive.eai.custom.alarm.AlarmStateManager;
import com.eactive.eai.custom.alarm.condition.AlarmCondition;
import com.eactive.eai.custom.alarm.event.AlarmEvent;
import com.eactive.eai.custom.alarm.key.AlarmKey;
import com.eactive.eai.custom.alarm.key.CoreAlarmKey;
import com.eactive.eai.env.ElinkConfig;
import com.eactive.eai.inbound.processor.Processor;
import com.ext.eai.common.stdmessage.STDMessageKeys;
@@ -96,20 +101,24 @@ public class RESTProcess extends HTTPProcess {
String circuitBreakerUseYn = PropManager.getInstance().getProperty("CircuitBreaker", "useYn");
try {
if("Y".equals(circuitBreakerUseYn)){
String apiId = reqEaiMsg.getEAISvcCd();
CircuitBreaker circuitBreaker = CircuitBreakerManager.getInstance().getCircuitBreaker(apiId);
if (true) {
String apiId = reqEaiMsg.getEAISvcCd();
CircuitBreaker circuitBreaker = CircuitBreakerManager.getInstance().getCircuitBreaker(apiId);
// ObjectMapper 인스턴스 생성
ObjectMapper objectMapper = new ObjectMapper();
// ObjectMapper 인스턴스 생성
ObjectMapper objectMapper = new ObjectMapper();
// 서킷 브레이커의 메트릭 정보와 상태 정보를 직접 Node로 변환
ObjectNode combinedNode = objectMapper.createObjectNode();
combinedNode.put("state", circuitBreaker.getState().toString());
combinedNode.set("metrics", objectMapper.valueToTree(circuitBreaker.getMetrics()));
// 서킷 브레이커의 메트릭 정보와 상태 정보를 직접 Node로 변환
ObjectNode combinedNode = objectMapper.createObjectNode();
combinedNode.put("state", circuitBreaker.getState().toString());
combinedNode.set("metrics", objectMapper.valueToTree(circuitBreaker.getMetrics()));
logger.info("CircuitBreaker state " + combinedNode.toString());
this.resObject = circuitBreaker.executeCallable(() -> HttpSender
.callService(this.adapterGroupName, this.outboundProp, this.reqObject, this.tempProp));
logger.info("CircuitBreaker state "+combinedNode.toString());
this.resObject = circuitBreaker.executeCallable(() -> HttpSender.callService(this.adapterGroupName, this.outboundProp, this.reqObject, this.tempProp));
}else{
this.resObject = HttpSender.callService(this.adapterGroupName, this.outboundProp, this.reqObject, this.tempProp);
}