findByTimesliceGreaterThanEqual(LocalDateTime timeslice) {
+ return repository.findByTimesliceGreaterThanEqual(timeslice);
+ }
+
/**
* μν°ν° μ μ₯ (INSERT λλ UPDATE)
*/
diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricAfterDTO.java b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricAfterDTO.java
new file mode 100644
index 0000000..2852cf6
--- /dev/null
+++ b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricAfterDTO.java
@@ -0,0 +1,96 @@
+package com.eactive.eai.rms.onl.apim.obp;
+
+import com.eactive.apim.portal.obp.entity.ObpGwMetric;
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * ObpGwMetric JSON μλ΅ DTO (after-timeslice μλν¬μΈνΈμ©)
+ *
+ * μμ μΆλ ₯ νμμ λ§μΆ€:
+ *
+ * {
+ * "apiId": "1ba56b82-a9c9-41e2-b3ab-ff9377667d56",
+ * "apiName": "[ν΄μΈμ‘κΈ] κ°μκ³μ’ μ
κΈμ€ν¨ μ 보쑰ν",
+ * "apiRoutingUri": "/api/obs/remittance/remittancefail*",
+ * "orgId": "ORG001",
+ * "partnerCode": "000008-01",
+ * "appId": "APP001",
+ * "appKey": "l7xx2fd7efc523794be5a1b126873a5de613",
+ * "requestUri": "-1",
+ * "timeslice": "2025-12-02T03:00:00.000+0000",
+ * "attemptedCount": 3,
+ * "completedCount": 3
+ * }
+ *
+ */
+@Data
+public class ObpGwMetricAfterDTO {
+
+ private static final DateTimeFormatter ISO8601_MILLIS_FORMATTER =
+ DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'+0000'");
+
+ @SerializedName("apiId")
+ private String apiId;
+
+ @SerializedName("apiName")
+ private String apiName;
+
+ @SerializedName("apiRoutingUri")
+ private String apiRoutingUri;
+
+ @SerializedName("orgId")
+ private String orgId;
+
+ @SerializedName("partnerCode")
+ private String partnerCode;
+
+ @SerializedName("appId")
+ private String appId;
+
+ @SerializedName("appKey")
+ private String appKey;
+
+ @SerializedName("requestUri")
+ private String requestUri;
+
+ @SerializedName("timeslice")
+ private String timeslice;
+
+ @SerializedName("attemptedCount")
+ private Long attemptedCount;
+
+ @SerializedName("completedCount")
+ private Long completedCount;
+
+ /**
+ * Entity β DTO λ³ν
+ */
+ public static ObpGwMetricAfterDTO from(ObpGwMetric entity) {
+ ObpGwMetricAfterDTO dto = new ObpGwMetricAfterDTO();
+ dto.setApiId(entity.getApiId());
+ dto.setApiName(entity.getApiName());
+ dto.setApiRoutingUri(entity.getUri());
+ dto.setOrgId(entity.getOrgId());
+ dto.setPartnerCode(entity.getPartnerCode());
+ dto.setAppId(entity.getAppId());
+ dto.setAppKey(entity.getClientId());
+ dto.setRequestUri(entity.getRequestUri());
+ dto.setTimeslice(formatTimeslice(entity.getTimeslice()));
+ dto.setAttemptedCount(entity.getAttemptedCount());
+ dto.setCompletedCount(entity.getCompletedCount());
+ return dto;
+ }
+
+ /**
+ * timesliceλ₯Ό ISO8601 νμ (λ°λ¦¬μ΄ ν¬ν¨) + UTC μκ°λλ‘ λ³ν
+ * μ: 2025-12-02T03:00:00.000+0000
+ */
+ private static String formatTimeslice(LocalDateTime timeslice) {
+ if (timeslice == null) return null;
+ return timeslice.format(ISO8601_MILLIS_FORMATTER);
+ }
+}
diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricController.java b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricController.java
index fb3168b..164bc3f 100644
--- a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricController.java
+++ b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricController.java
@@ -8,6 +8,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.nio.charset.StandardCharsets;
@@ -27,7 +28,7 @@ public class ObpGwMetricController implements InterceptorSkipController {
private static final Logger log = LoggerFactory.getLogger(ObpGwMetricController.class);
private static final DateTimeFormatter PATH_FORMATTER =
- DateTimeFormatter.ofPattern("yyyy-MM-dd_HH:mm:ss");
+ DateTimeFormatter.ofPattern("yyyy-MM-dd_HH:mm:ss");
private final ObpGwMetricManService obpGwMetricManService;
@@ -39,15 +40,18 @@ public class ObpGwMetricController implements InterceptorSkipController {
* νΉμ μκ°λμ GwMetric λ°μ΄ν° μ‘°ν
*
* @param datetime μ‘°ν μκ°λ (νμ: yyyy-MM-dd_HH:mm:ss)
+ * @param pretty trueλ©΄ JSON λ€μ¬μ°κΈ° μ μ©
* @return JSON Array
*/
@RequestMapping(
- value = "/kjb/gw-metrics/{datetime}",
+ value = "/kjb/gw-metrics/{datetime}.json",
produces = "application/json;charset=utf-8"
)
@ResponseBody
- public String getGwMetrics(@PathVariable("datetime") String datetime) {
- log.debug("GwMetric μ‘°ν μμ²: datetime={}", datetime);
+ public String getGwMetrics(
+ @PathVariable("datetime") String datetime,
+ @RequestParam(value = "pretty", required = false) Boolean pretty) {
+ log.debug("GwMetric μ‘°ν μμ²: datetime={}, pretty={}", datetime, pretty);
long startTime = System.currentTimeMillis();
try {
@@ -59,11 +63,7 @@ public class ObpGwMetricController implements InterceptorSkipController {
List metrics = obpGwMetricManService.findByTimeslice(timeslice);
// JSON λ³ν (GSON)
- Gson gson = new GsonBuilder()
- .serializeNulls()
- .create();
-
- String json = gson.toJson(metrics);
+ String json = toJson(metrics, pretty);
// μλ΅ ν¬κΈ° κ³μ° (UTF-8 κΈ°μ€)
int jsonBytes = json.getBytes(StandardCharsets.UTF_8).length;
@@ -81,6 +81,66 @@ public class ObpGwMetricController implements InterceptorSkipController {
}
}
+ /**
+ * νΉμ μκ°λ μ΄νμ GwMetric λ°μ΄ν° μ‘°ν
+ *
+ * @param datetime μ‘°ν μμ μκ°λ (νμ: yyyy-MM-dd_HH:mm:ss)
+ * @param pretty trueλ©΄ JSON λ€μ¬μ°κΈ° μ μ©
+ * @return JSON Array (ν΄λΉ μκ°λ μ΄νμ λͺ¨λ μ§ν)
+ */
+ @RequestMapping(
+ value = "/kjb/gw-metrics/after-timeslice/{datetime}.json",
+ produces = "application/json;charset=utf-8"
+ )
+ @ResponseBody
+ public String getGwMetricsAfterTimeslice(
+ @PathVariable("datetime") String datetime,
+ @RequestParam(value = "pretty", required = false) Boolean pretty) {
+ log.debug("GwMetric after-timeslice μ‘°ν μμ²: datetime={}, pretty={}", datetime, pretty);
+ long startTime = System.currentTimeMillis();
+
+ try {
+ // Path νλΌλ―Έν° νμ±
+ LocalDateTime targetTime = LocalDateTime.parse(datetime, PATH_FORMATTER);
+ LocalDateTime timeslice = targetTime.withMinute(0).withSecond(0).withNano(0);
+
+ // μ‘°ν
+ List metrics = obpGwMetricManService.findByTimesliceAfter(timeslice);
+
+ // JSON λ³ν (GSON)
+ String json = toJson(metrics, pretty);
+
+ // μλ΅ ν¬κΈ° κ³μ° (UTF-8 κΈ°μ€)
+ int jsonBytes = json.getBytes(StandardCharsets.UTF_8).length;
+ String jsonSize = formatByteSize(jsonBytes);
+
+ long elapsed = System.currentTimeMillis() - startTime;
+ log.info("GwMetric after-timeslice μ‘°ν μλ£: datetime={}, count={}, size={}, elapsed={}ms",
+ datetime, metrics.size(), jsonSize, elapsed);
+
+ return json;
+
+ } catch (Exception e) {
+ log.error("GwMetric after-timeslice μ‘°ν μ€ν¨: datetime={}", datetime, e);
+ throw e;
+ }
+ }
+
+ /**
+ * κ°μ²΄λ₯Ό JSON λ¬Έμμ΄λ‘ λ³ν
+ *
+ * @param object λ³νν κ°μ²΄
+ * @param pretty trueλ©΄ λ€μ¬μ°κΈ° μ μ©
+ * @return JSON λ¬Έμμ΄
+ */
+ private String toJson(Object object, Boolean pretty) {
+ GsonBuilder builder = new GsonBuilder().serializeNulls();
+ if (Boolean.TRUE.equals(pretty)) {
+ builder.setPrettyPrinting();
+ }
+ return builder.create().toJson(object);
+ }
+
/**
* λ°μ΄νΈ ν¬κΈ°λ₯Ό μ½κΈ° μ¬μ΄ νμμΌλ‘ λ³ν
* μ: 1024 -> "1.0 KB", 1048576 -> "1.0 MB"
diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricManService.java b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricManService.java
index 0675162..86c6167 100644
--- a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricManService.java
+++ b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricManService.java
@@ -279,4 +279,18 @@ public class ObpGwMetricManService extends BaseService {
.map(ObpGwMetricDTO::from)
.collect(Collectors.toList());
}
+
+ /**
+ * νΉμ μκ°λ μ΄νμ GwMetric λ°μ΄ν° μ‘°ν (REST APIμ©)
+ * after-timeslice μλν¬μΈνΈμμ μ¬μ©
+ */
+ public List findByTimesliceAfter(LocalDateTime timeslice) {
+ DataSourceContextHolder.setDataSourceType(
+ DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.MONITORING));
+
+ List entities = obpGwMetricService.findByTimesliceGreaterThanEqual(timeslice);
+ return entities.stream()
+ .map(ObpGwMetricAfterDTO::from)
+ .collect(Collectors.toList());
+ }
}
diff --git a/src/main/resources/hotswap-agent.properties b/src/main/resources/hotswap-agent.properties
index 3371a8b..cc2d7ce 100644
--- a/src/main/resources/hotswap-agent.properties
+++ b/src/main/resources/hotswap-agent.properties
@@ -1,9 +1,81 @@
-# HotSwapAgent μ€μ
-# Spring κ΄λ ¨
-Spring.reload=true
-# Hibernate κ΄λ ¨
-Hibernate.reload=true
-# λ‘κ·Έ λ 벨
-logLevel=INFO
-# μλ Hot Swap νμ±ν
+# ============================================================
+# HotswapAgent Configuration for eapim-admin (Spring Legacy)
+# ============================================================
+
+# \uC790\uB3D9 \uD56B\uC2A4\uC651 \uD65C\uC131\uD654
autoHotswap=true
+
+# \uB85C\uAE45 \uB808\uBCA8 (TRACE, DEBUG, INFO, WARNING, ERROR)
+LOGGER.level=INFO
+
+# ============================================================
+# \uBA85\uC2DC\uC801 \uD50C\uB7EC\uADF8\uC778 \uBE44\uD65C\uC131\uD654 (\uC27C\uD45C \uAD6C\uBD84)
+# ============================================================
+disabledPlugins=org.hotswap.agent.plugin.ibatis.IBatisPlugin,org.hotswap.agent.plugin.mybatis.MyBatisPlugin
+
+# ============================================================
+# Core Plugin \uD65C\uC131\uD654
+# ============================================================
+
+# Spring Framework (\uD544\uC218) - Bean \uC7AC\uB4F1\uB85D
+spring.enabled=true
+spring.basePackagePrefix=com.eactive.
+
+# Hibernate/JPA - \uC5D4\uD2F0\uD2F0 \uBCC0\uACBD \uAC10\uC9C0
+hibernate.enabled=true
+
+# Logback - \uB85C\uADF8 \uC124\uC815 \uB9AC\uB85C\uB4DC
+logback.enabled=true
+
+# Proxy - AOP \uD504\uB85D\uC2DC \uC7AC\uC0DD\uC131
+proxy.enabled=true
+
+# ============================================================
+# \uBE44\uD65C\uC131\uD654 Plugin (\uBBF8\uC0AC\uC6A9 \uB610\uB294 \uCDA9\uB3CC \uBC29\uC9C0)
+# ============================================================
+
+# iBatis (Spring\uC758 SqlMapClientFactoryBean\uACFC \uCDA9\uB3CC)
+ibatis.enabled=false
+mybatis.enabled=false
+jsf.enabled=false
+seam.enabled=false
+wicket.enabled=false
+mojarra.enabled=false
+omnifaces.enabled=false
+el.enabled=false
+weld.enabled=false
+owb.enabled=false
+cdi.enabled=false
+resteasy.enabled=false
+jersey1.enabled=false
+jersey2.enabled=false
+
+# ============================================================
+# Watch Resources (\uB9AC\uC18C\uC2A4 \uBCC0\uACBD \uAC10\uC9C0)
+# ============================================================
+
+# \uD074\uB798\uC2A4\uD328\uC2A4 \uC678 \uCD94\uAC00 \uAC10\uC2DC \uACBD\uB85C (XML \uC124\uC815 \uB4F1)
+watchResources=src/main/resources,WebContent/WEB-INF
+
+# ============================================================
+# Spring \uC804\uC6A9 \uC124\uC815
+# ============================================================
+
+# \uCEF4\uD3EC\uB10C\uD2B8 \uC2A4\uCE94 \uBCA0\uC774\uC2A4 \uD328\uD0A4\uC9C0
+spring.scanBasePackage=com.eactive.eai,com.eactive.apim
+
+# \uD504\uB85D\uC2DC \uC7AC\uC0DD\uC131 (AOP, @Transactional \uC0AC\uC6A9 \uC2DC \uD544\uC218)
+spring.proxyRegeneration=true
+
+# Bean \uC815\uC758 \uBCC0\uACBD \uC2DC \uB9AC\uB85C\uB4DC
+spring.reloadBeanDefinition=true
+
+# ============================================================
+# \uB514\uBC84\uAE45 (\uBB38\uC81C \uBC1C\uC0DD \uC2DC \uD65C\uC131\uD654)
+# ============================================================
+
+# \uC0C1\uC138 \uB85C\uADF8 \uCD9C\uB825 (\uD544\uC694\uC2DC DEBUG\uB85C \uBCC0\uACBD)
+# LOGGER.level=DEBUG
+
+# \uD074\uB798\uC2A4 \uBCC0\uACBD \uCD94\uC801
+# LOGGER.org.hotswap.agent.plugin.spring=DEBUG