diff --git a/src/main/java/com/eactive/eai/agent/command/ReloadInflowClientControlCommand.java b/src/main/java/com/eactive/eai/agent/command/ReloadInflowClientControlCommand.java
new file mode 100644
index 0000000..8333557
--- /dev/null
+++ b/src/main/java/com/eactive/eai/agent/command/ReloadInflowClientControlCommand.java
@@ -0,0 +1,58 @@
+package com.eactive.eai.agent.command;
+
+import com.eactive.eai.common.inflow.AbstractInflowControlManager;
+import com.eactive.eai.common.inflow.InflowControlUtil;
+import com.eactive.eai.common.util.Logger;
+import com.eactive.eai.custom.inflow.ClientDualInflowControlManager;
+
+public class ReloadInflowClientControlCommand extends Command {
+
+ private static final long serialVersionUID = 1L;
+
+ public ReloadInflowClientControlCommand() {
+ super("ReloadInflowClientControlCommand");
+ }
+
+ public Object execute() throws CommandException {
+ Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
+ if (logger.isInfo())
+ logger.info(this.name + " is executed");
+
+ if (!(args instanceof String)) {
+ String rspErrorCode = "RECEAIMCM001";
+ String msg = makeException(rspErrorCode, null);
+ if (logger.isError())
+ logger.error(msg);
+ throw new CommandException(msg);
+ }
+
+ String keyName = (String) args;
+ try {
+ AbstractInflowControlManager base = InflowControlUtil.getInflowControlManager();
+ if (!(base instanceof ClientDualInflowControlManager)) {
+ throw new IllegalStateException("ClientDualInflowControlManager 가 활성화되지 않았습니다.");
+ }
+ ClientDualInflowControlManager manager = (ClientDualInflowControlManager) base;
+
+ if (keyName != null) {
+ if ("ALL".equals(keyName)) {
+ manager.reloadClient();
+ if (logger.isWarn())
+ logger.warn(this.name + "] all rule Reload.");
+ } else {
+ manager.reloadClient(keyName);
+ if (logger.isWarn())
+ logger.warn(this.name + "] " + keyName + " Reload.");
+ }
+ }
+
+ } catch (Exception e) {
+ String rspErrorCode = "RECEAIMCM002";
+ String msg = makeException(rspErrorCode, e);
+ if (logger.isError())
+ logger.error(msg, e);
+ throw new CommandException(msg);
+ }
+ return "success";
+ }
+}
diff --git a/src/main/java/com/eactive/eai/agent/command/RemoveInflowClientControlCommand.java b/src/main/java/com/eactive/eai/agent/command/RemoveInflowClientControlCommand.java
new file mode 100644
index 0000000..3750073
--- /dev/null
+++ b/src/main/java/com/eactive/eai/agent/command/RemoveInflowClientControlCommand.java
@@ -0,0 +1,48 @@
+package com.eactive.eai.agent.command;
+
+import com.eactive.eai.common.inflow.AbstractInflowControlManager;
+import com.eactive.eai.common.inflow.InflowControlUtil;
+import com.eactive.eai.common.util.Logger;
+import com.eactive.eai.custom.inflow.ClientDualInflowControlManager;
+
+public class RemoveInflowClientControlCommand extends Command {
+
+ private static final long serialVersionUID = 1L;
+
+ public RemoveInflowClientControlCommand() {
+ super("RemoveInflowClientControlCommand");
+ }
+
+ public Object execute() throws CommandException {
+ Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
+ if (logger.isInfo())
+ logger.info(this.name + " is executed");
+
+ if (!(args instanceof String)) {
+ String rspErrorCode = "RECEAIMCM001";
+ String msg = makeException(rspErrorCode, null);
+ if (logger.isError())
+ logger.error(msg);
+ throw new CommandException(msg);
+ }
+
+ String key = (String) args;
+ try {
+ AbstractInflowControlManager base = InflowControlUtil.getInflowControlManager();
+ if (!(base instanceof ClientDualInflowControlManager)) {
+ throw new IllegalStateException("ClientDualInflowControlManager 가 활성화되지 않았습니다.");
+ }
+ ClientDualInflowControlManager manager = (ClientDualInflowControlManager) base;
+ manager.removeClient(key);
+ if (logger.isWarn())
+ logger.warn(this.name + "] " + key + " removed.");
+ } catch (Exception e) {
+ String rspErrorCode = "RECEAIMCM002";
+ String msg = makeException(rspErrorCode, e);
+ if (logger.isError())
+ logger.error(msg, e);
+ throw new CommandException(msg);
+ }
+ return "success";
+ }
+}
diff --git a/src/main/java/com/eactive/eai/custom/inbound/processor/DJBRequestProcessor.java b/src/main/java/com/eactive/eai/custom/inbound/processor/DJBRequestProcessor.java
index d2f8759..8e7c133 100644
--- a/src/main/java/com/eactive/eai/custom/inbound/processor/DJBRequestProcessor.java
+++ b/src/main/java/com/eactive/eai/custom/inbound/processor/DJBRequestProcessor.java
@@ -4,9 +4,9 @@ import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.common.inflow.Bucket;
import com.eactive.eai.common.inflow.InflowTargetVO;
-import com.eactive.eai.common.inflow.dual.ClientDualBucket;
import com.eactive.eai.common.inflow.dual.DualBucket;
import com.eactive.eai.common.inflow.dual.DualCustomBucket;
+import com.eactive.eai.custom.inflow.ClientDualBucket;
import com.eactive.eai.inbound.processor.RequestProcessor;
/**
diff --git a/src/main/java/com/eactive/eai/custom/inflow/ClientDualBucket.java b/src/main/java/com/eactive/eai/custom/inflow/ClientDualBucket.java
new file mode 100644
index 0000000..07a84de
--- /dev/null
+++ b/src/main/java/com/eactive/eai/custom/inflow/ClientDualBucket.java
@@ -0,0 +1,23 @@
+package com.eactive.eai.custom.inflow;
+
+import com.eactive.eai.common.inflow.InflowTargetVO;
+import com.eactive.eai.common.inflow.dual.DualBucket;
+
+/**
+ * DualBucket에 클라이언트 유량제어 메서드를 추가한 인터페이스.
+ * {@link ClientDualInflowControlManager}가 구현한다.
+ */
+public interface ClientDualBucket extends DualBucket {
+
+ /**
+ * 클라이언트 유량제어 체크 — 차단 원인 포함.
+ * @return null이면 통과, "PER_SECOND" 또는 "THRESHOLD"이면 해당 버킷에서 차단
+ */
+ String isClientPassDetail(String clientId);
+
+ /**
+ * 클라이언트 유량제어 설정 조회 — 에러 메시지 생성용.
+ * @return 등록된 설정이 없으면 null
+ */
+ InflowTargetVO getClientInflowThreshold(String clientId);
+}
diff --git a/src/main/java/com/eactive/eai/custom/inflow/ClientDualInflowControlManager.java b/src/main/java/com/eactive/eai/custom/inflow/ClientDualInflowControlManager.java
new file mode 100644
index 0000000..4d45ee1
--- /dev/null
+++ b/src/main/java/com/eactive/eai/custom/inflow/ClientDualInflowControlManager.java
@@ -0,0 +1,114 @@
+package com.eactive.eai.custom.inflow;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.springframework.stereotype.Component;
+
+import com.eactive.eai.common.inflow.Bucket;
+import com.eactive.eai.common.inflow.InflowTargetVO;
+import com.eactive.eai.common.inflow.InflowType;
+import com.eactive.eai.common.inflow.dual.DualCustomBucket;
+import com.eactive.eai.common.inflow.dual.DualInflowControlManager;
+import com.eactive.eai.common.lifecycle.LifecycleException;
+import com.eactive.eai.common.util.ApplicationContextProvider;
+import com.eactive.eai.common.util.Logger;
+
+/**
+ * 어댑터/인터페이스 이중 버킷(DualInflowControlManager)에 클라이언트 유량제어를 추가한 관리자.
+ *
+ *
적용 방법: INFLOW.properties에 아래 한 줄 추가.
+ *
inflow.control.bucket.className=com.eactive.eai.common.inflow.dual.ClientDualInflowControlManager
+ */
+@Component
+public class ClientDualInflowControlManager extends DualInflowControlManager implements ClientDualBucket {
+
+ private static final Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
+
+ public static synchronized Bucket getBucket() {
+ return ApplicationContextProvider.getContext().getBean(ClientDualInflowControlManager.class);
+ }
+
+ private volatile Map clientBucketMap = new ConcurrentHashMap<>();
+ private final ConcurrentHashMap clientMetricsMap = new ConcurrentHashMap<>();
+
+ // -------------------------------------------------------------------------
+ // Lifecycle
+ // -------------------------------------------------------------------------
+
+ @Override
+ public void start() throws LifecycleException {
+ super.start();
+ try {
+ initClients();
+ } catch (Exception e) {
+ throw new LifecycleException("ClientDualInflowControlManager init failed: " + e.getMessage());
+ }
+ }
+
+ // -------------------------------------------------------------------------
+ // 초기화 / 리로드
+ // -------------------------------------------------------------------------
+
+ private void initClients() throws Exception {
+ this.clientBucketMap = loadBucketMap(InflowType.CLIENT);
+ if (logger.isInfo()) logger.info("ClientDualInflowControlManager] initClients: " + clientBucketMap.size() + " buckets");
+ }
+
+ public synchronized void reloadClient() throws Exception {
+ initClients();
+ clientMetricsMap.clear();
+ }
+
+ public synchronized void reloadClient(String clientId) throws Exception {
+ reloadBucketMap(clientBucketMap, InflowType.CLIENT, clientId);
+ TargetMetrics m = clientMetricsMap.get(clientId);
+ if (m != null) m.reset();
+ }
+
+ public synchronized void removeClient(String clientId) {
+ clientBucketMap.remove(clientId);
+ clientMetricsMap.remove(clientId);
+ }
+
+ // -------------------------------------------------------------------------
+ // 클라이언트 메트릭 접근자
+ // -------------------------------------------------------------------------
+
+ public TargetMetrics getClientMetrics(String clientId) { return clientMetricsMap.get(clientId); }
+ public Map getAllClientMetrics() { return Collections.unmodifiableMap(clientMetricsMap); }
+ public void resetClientMetrics(String clientId) { TargetMetrics m = clientMetricsMap.get(clientId); if (m != null) m.reset(); }
+ public void resetAllClientMetrics() { clientMetricsMap.values().forEach(TargetMetrics::reset); }
+
+ // -------------------------------------------------------------------------
+ // ClientDualBucket — 클라이언트 유량제어
+ // -------------------------------------------------------------------------
+
+ @Override
+ public String isClientPassDetail(String clientId) {
+ DualCustomBucket b = clientBucketMap.get(clientId);
+ if (b == null) return DualCustomBucket.RESULT_PASS;
+ String result = b.tryConsume();
+ recordMetrics(clientMetricsMap, clientId, result);
+ return result;
+ }
+
+ @Override
+ public InflowTargetVO getClientInflowThreshold(String clientId) {
+ DualCustomBucket b = clientBucketMap.get(clientId);
+ return (b == null) ? null : b.getInflowTargetVo();
+ }
+
+ // -------------------------------------------------------------------------
+ // 모니터링용 접근자
+ // -------------------------------------------------------------------------
+
+ public DualCustomBucket getClientBucket(String clientId) {
+ return clientBucketMap.get(clientId);
+ }
+
+ public Map getClientBucketMap() {
+ return clientBucketMap;
+ }
+}
diff --git a/src/main/java/com/eactive/eai/custom/inflow/ClientInflowTargetBucketService.java b/src/main/java/com/eactive/eai/custom/inflow/ClientInflowTargetBucketService.java
index cb5e388..51c69b5 100644
--- a/src/main/java/com/eactive/eai/custom/inflow/ClientInflowTargetBucketService.java
+++ b/src/main/java/com/eactive/eai/custom/inflow/ClientInflowTargetBucketService.java
@@ -9,7 +9,6 @@ import org.springframework.stereotype.Service;
import com.eactive.eai.common.inflow.AbstractInflowControlManager;
import com.eactive.eai.common.inflow.InflowControlUtil;
import com.eactive.eai.common.inflow.InflowTargetVO;
-import com.eactive.eai.common.inflow.dual.ClientDualInflowControlManager;
import com.eactive.eai.common.inflow.dual.DualCustomBucket;
import com.eactive.eai.manage.inflow.GroupBucketStatusDTO;
import com.eactive.eai.manage.inflow.TargetBucketStatusDTO;
diff --git a/src/main/java/com/eactive/eai/custom/inflow/ClientInflowTargetMetricService.java b/src/main/java/com/eactive/eai/custom/inflow/ClientInflowTargetMetricService.java
index 39df98e..cb92dfc 100644
--- a/src/main/java/com/eactive/eai/custom/inflow/ClientInflowTargetMetricService.java
+++ b/src/main/java/com/eactive/eai/custom/inflow/ClientInflowTargetMetricService.java
@@ -8,7 +8,6 @@ import org.springframework.stereotype.Service;
import com.eactive.eai.common.inflow.AbstractInflowControlManager;
import com.eactive.eai.common.inflow.InflowControlUtil;
import com.eactive.eai.common.inflow.InflowTargetVO;
-import com.eactive.eai.common.inflow.dual.ClientDualInflowControlManager;
import com.eactive.eai.common.inflow.dual.DualInflowControlManager;
import com.eactive.eai.manage.inflow.TargetMetricDTO;
diff --git a/src/test/java/com/eactive/eai/custom/inbound/processor/ClientDualInflowControlManagerMetricsTest.java b/src/test/java/com/eactive/eai/custom/inbound/processor/ClientDualInflowControlManagerMetricsTest.java
new file mode 100644
index 0000000..58e6b2d
--- /dev/null
+++ b/src/test/java/com/eactive/eai/custom/inbound/processor/ClientDualInflowControlManagerMetricsTest.java
@@ -0,0 +1,196 @@
+package com.eactive.eai.custom.inbound.processor;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.time.Duration;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import com.eactive.eai.common.inflow.InflowTargetVO;
+import com.eactive.eai.common.inflow.dual.DualCustomBucket;
+import com.eactive.eai.common.inflow.dual.DualInflowControlManager;
+import com.eactive.eai.custom.inflow.ClientDualInflowControlManager;
+
+import io.github.bucket4j.Bandwidth;
+import io.github.bucket4j.Bucket4j;
+import io.github.bucket4j.local.LocalBucket;
+
+/**
+ * ClientDualInflowControlManager 클라이언트 메트릭 단위 테스트.
+ */
+class ClientDualInflowControlManagerMetricsTest {
+
+ private ClientDualInflowControlManager manager;
+
+ @BeforeEach
+ void setUp() {
+ manager = new ClientDualInflowControlManager();
+ }
+
+ // -------------------------------------------------------------------------
+ // Helpers
+ // -------------------------------------------------------------------------
+
+ private InflowTargetVO makeTargetVO(String name) {
+ InflowTargetVO vo = new InflowTargetVO();
+ vo.setName(name);
+ vo.setThresholdPerSecond(100);
+ vo.setThreshold(10000);
+ vo.setThresholdTimeUnit("DAY");
+ vo.setActivate(true);
+ return vo;
+ }
+
+ private LocalBucket stableBucket(long capacity) {
+ return Bucket4j.builder()
+ .addLimit(Bandwidth.simple(capacity, Duration.ofHours(1)))
+ .build();
+ }
+
+ private LocalBucket exhaustedBucket(long capacity) {
+ LocalBucket b = stableBucket(capacity);
+ b.tryConsume(capacity);
+ return b;
+ }
+
+ private DualCustomBucket passDualBucket(String name) {
+ return new DualCustomBucket(stableBucket(100), null, makeTargetVO(name));
+ }
+
+ private DualCustomBucket blockedPerSecondDualBucket(String name) {
+ return new DualCustomBucket(exhaustedBucket(1), null, makeTargetVO(name));
+ }
+
+ private DualCustomBucket blockedThresholdDualBucket(String name) {
+ return new DualCustomBucket(stableBucket(100), exhaustedBucket(1), makeTargetVO(name));
+ }
+
+ private void injectClientMap(Map map) {
+ ReflectionTestUtils.setField(manager, "clientBucketMap", map);
+ }
+
+ // =========================================================================
+ // isClientPassDetail() — 카운터 증가
+ // =========================================================================
+
+ @Test
+ void isClientPassDetail_bucketNotRegistered_noMetricsCreated() {
+ manager.isClientPassDetail("UNKNOWN");
+
+ assertNull(manager.getClientMetrics("UNKNOWN"));
+ }
+
+ @Test
+ void isClientPassDetail_pass_incrementsAllowed() {
+ Map map = new ConcurrentHashMap<>();
+ map.put("C1", passDualBucket("C1"));
+ injectClientMap(map);
+
+ manager.isClientPassDetail("C1");
+
+ assertEquals(1, manager.getClientMetrics("C1").allowed.get());
+ }
+
+ @Test
+ void isClientPassDetail_blockedThreshold_incrementsRejectedThreshold() {
+ Map map = new ConcurrentHashMap<>();
+ map.put("C1", blockedThresholdDualBucket("C1"));
+ injectClientMap(map);
+
+ manager.isClientPassDetail("C1");
+
+ assertEquals(1, manager.getClientMetrics("C1").rejectedThreshold.get());
+ }
+
+ @Test
+ void isClientPassDetail_blockedPerSecond_incrementsRejectedPerSecond() {
+ Map map = new ConcurrentHashMap<>();
+ map.put("C1", blockedPerSecondDualBucket("C1"));
+ injectClientMap(map);
+
+ manager.isClientPassDetail("C1");
+
+ assertEquals(1, manager.getClientMetrics("C1").rejectedPerSecond.get());
+ }
+
+ // =========================================================================
+ // getAllClientMetrics — 조회 및 불변성
+ // =========================================================================
+
+ @Test
+ void getAllClientMetrics_empty_returnsEmptyMap() {
+ assertTrue(manager.getAllClientMetrics().isEmpty());
+ }
+
+ @Test
+ void getAllClientMetrics_afterCalls_containsEntries() {
+ Map map = new ConcurrentHashMap<>();
+ map.put("C1", passDualBucket("C1"));
+ map.put("C2", passDualBucket("C2"));
+ injectClientMap(map);
+
+ manager.isClientPassDetail("C1");
+ manager.isClientPassDetail("C2");
+
+ Map all = manager.getAllClientMetrics();
+ assertEquals(2, all.size());
+ assertTrue(all.containsKey("C1"));
+ assertTrue(all.containsKey("C2"));
+ }
+
+ // =========================================================================
+ // resetClientMetrics / resetAllClientMetrics
+ // =========================================================================
+
+ @Test
+ void resetClientMetrics_resetsTargetClient() {
+ Map map = new ConcurrentHashMap<>();
+ map.put("C1", passDualBucket("C1"));
+ injectClientMap(map);
+
+ manager.isClientPassDetail("C1");
+ manager.resetClientMetrics("C1");
+
+ assertEquals(0, manager.getClientMetrics("C1").allowed.get());
+ }
+
+ @Test
+ void resetAllClientMetrics_resetsAll() {
+ Map map = new ConcurrentHashMap<>();
+ map.put("C1", passDualBucket("C1"));
+ map.put("C2", passDualBucket("C2"));
+ injectClientMap(map);
+
+ manager.isClientPassDetail("C1");
+ manager.isClientPassDetail("C2");
+ manager.resetAllClientMetrics();
+
+ assertEquals(0, manager.getClientMetrics("C1").allowed.get());
+ assertEquals(0, manager.getClientMetrics("C2").allowed.get());
+ }
+
+ // =========================================================================
+ // removeClient — 메트릭 자동 삭제
+ // =========================================================================
+
+ @Test
+ void removeClient_cleansUpMetrics() {
+ Map map = new ConcurrentHashMap<>();
+ map.put("C1", passDualBucket("C1"));
+ injectClientMap(map);
+
+ manager.isClientPassDetail("C1");
+ assertNotNull(manager.getClientMetrics("C1"));
+
+ manager.removeClient("C1");
+
+ assertNull(manager.getClientMetrics("C1"));
+ }
+}
diff --git a/src/test/java/com/eactive/eai/custom/inbound/processor/DJBRequestProcessorTest.java b/src/test/java/com/eactive/eai/custom/inbound/processor/DJBRequestProcessorTest.java
index 9b796b6..84cf054 100644
--- a/src/test/java/com/eactive/eai/custom/inbound/processor/DJBRequestProcessorTest.java
+++ b/src/test/java/com/eactive/eai/custom/inbound/processor/DJBRequestProcessorTest.java
@@ -1,7 +1,12 @@
package com.eactive.eai.custom.inbound.processor;
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.Mockito.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -10,11 +15,11 @@ import org.springframework.test.util.ReflectionTestUtils;
import com.eactive.eai.common.inflow.Bucket;
import com.eactive.eai.common.inflow.InflowTargetVO;
-import com.eactive.eai.common.inflow.dual.ClientDualBucket;
import com.eactive.eai.common.inflow.dual.DualBucket;
import com.eactive.eai.common.inflow.dual.DualCustomBucket;
import com.eactive.eai.common.server.EAIServerManager;
import com.eactive.eai.common.util.ApplicationContextProvider;
+import com.eactive.eai.custom.inflow.ClientDualBucket;
/**
* DJBRequestProcessor 단위 테스트.
diff --git a/src/test/java/com/eactive/eai/custom/inflow/ClientInflowTargetBucketServiceTest.java b/src/test/java/com/eactive/eai/custom/inflow/ClientInflowTargetBucketServiceTest.java
index d40cbe1..57be10b 100644
--- a/src/test/java/com/eactive/eai/custom/inflow/ClientInflowTargetBucketServiceTest.java
+++ b/src/test/java/com/eactive/eai/custom/inflow/ClientInflowTargetBucketServiceTest.java
@@ -1,7 +1,11 @@
package com.eactive.eai.custom.inflow;
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.Mockito.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import java.time.Duration;
import java.util.List;
@@ -11,7 +15,6 @@ import java.util.concurrent.ConcurrentHashMap;
import org.junit.jupiter.api.Test;
import com.eactive.eai.common.inflow.InflowTargetVO;
-import com.eactive.eai.common.inflow.dual.ClientDualInflowControlManager;
import com.eactive.eai.common.inflow.dual.DualCustomBucket;
import com.eactive.eai.manage.inflow.GroupBucketStatusDTO;
import com.eactive.eai.manage.inflow.TargetBucketStatusDTO;
diff --git a/src/test/java/com/eactive/eai/custom/inflow/ClientInflowTargetMetricServiceTest.java b/src/test/java/com/eactive/eai/custom/inflow/ClientInflowTargetMetricServiceTest.java
index 5e9325b..4289331 100644
--- a/src/test/java/com/eactive/eai/custom/inflow/ClientInflowTargetMetricServiceTest.java
+++ b/src/test/java/com/eactive/eai/custom/inflow/ClientInflowTargetMetricServiceTest.java
@@ -1,14 +1,22 @@
package com.eactive.eai.custom.inflow;
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.Mockito.*;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import java.util.List;
import org.junit.jupiter.api.Test;
import com.eactive.eai.common.inflow.InflowTargetVO;
-import com.eactive.eai.common.inflow.dual.ClientDualInflowControlManager;
import com.eactive.eai.common.inflow.dual.DualInflowControlManager;
import com.eactive.eai.manage.inflow.TargetMetricDTO;