package com.arms.config; import com.arms.api.schedule.model.vo.ScheduleInfoVO; import com.arms.api.schedule.model.vo.ScheduleMapVO; import com.arms.api.schedule.service.ScheduleService; import com.arms.api.schedule.util.ScheduleMapProvider; import com.arms.api.schedule.util.ScheduleTaskDispatcher; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.event.EventListener; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.SchedulingConfigurer; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.scheduling.config.CronTask; import org.springframework.scheduling.config.ScheduledTask; import org.springframework.scheduling.config.ScheduledTaskRegistrar; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @Configuration @EnableScheduling @Slf4j public class DynamicSchedulerConfig implements SchedulingConfigurer { @Autowired private ScheduleService scheduleService; @Autowired private ScheduleTaskDispatcher dispatcher; @Autowired @Qualifier("dynamicTaskScheduler") private TaskScheduler taskScheduler; private ScheduledTaskRegistrar taskRegistrar; // 현재 실행 중인 스케줄 작업들을 추적 private final List scheduledTasks = new ArrayList<>(); private final ScheduleMapProvider scheduleMapProvider; public DynamicSchedulerConfig(ScheduleMapProvider scheduleMapProvider) { // 기본 생성자 추가 log.info("DynamicSchedulerConfig initialized."); this.scheduleMapProvider = scheduleMapProvider; } @Bean(name = "dynamicTaskScheduler") public TaskScheduler taskScheduler() { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.setPoolSize(10); scheduler.setThreadNamePrefix("Dynamic-scheduler-"); scheduler.initialize(); return scheduler; } @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { this.taskRegistrar = taskRegistrar; log.info("DynamicSchedulerConfig :: configureTasks"); taskRegistrar.setTaskScheduler(taskScheduler); scheduleTasks(); // 초기 등록 } private void scheduleTasks() { // 기존 스케줄된 작업이 있다면 모두 취소 -> 동작하는 schedule 에 대한 방법 고민 for (ScheduledTask task : scheduledTasks) { task.cancel(); } scheduledTasks.clear(); ScheduleMapVO scheduleMapVO = scheduleMapProvider.getScheduleMapVO(); if (scheduleMapVO == null) { log.warn("[DynamicSchedulerConfig :: scheduleTasks] :: scheduleMapVO is null."); return; } Map> scheduleInfoMap = scheduleMapVO.getScheduleInfoMap(); if (scheduleInfoMap == null || scheduleInfoMap.isEmpty()) { log.warn("[DynamicSchedulerConfig :: scheduleTasks] :: No schedule info found in scheduleInfoMap."); return; } scheduleInfoMap.forEach((fileName, scheduleList) -> { for (ScheduleInfoVO scheduleInfo : scheduleList) { if (Boolean.TRUE.equals(scheduleInfo.getEnabled())) { String name = scheduleInfo.getName(); String methodName = scheduleInfo.getName(); // 임시처리 String cron = scheduleInfo.getCron(); String notes = scheduleInfo.getNotes(); Runnable businessLogic = null; if (dispatcher.contains(name)) { log.info("businessLogic is fetched by scheduleName [{}]", name); businessLogic = dispatcher.getTask(name); } else if (dispatcher.contains(methodName)) { log.info("businessLogic is fetched by methodName [{}]", methodName); businessLogic = dispatcher.getTask(methodName); } Runnable task; if (businessLogic != null) { Runnable fetchedLogic = businessLogic; task = () -> { log.info("Executing [{}] from [{}] - {}", name, fileName, notes); long start = System.nanoTime(); try { fetchedLogic.run(); } catch (Exception e) { log.error("Error during execution of [{}]: {}", name, e.getMessage(), e); } finally { long end = System.nanoTime(); long elapsedMs = TimeUnit.NANOSECONDS.toMillis(end - start); log.info("Finished [{}] from [{}] in {} ms", name, fileName, elapsedMs); } }; } else { task = () -> log.info("Executing [{}] from [{}] - {} (No business logic mapped)", name, fileName, notes); } CronTask cronTask = new CronTask(task, cron); ScheduledTask scheduledTask = taskRegistrar.scheduleCronTask(cronTask); scheduledTasks.add(scheduledTask); log.info("Registered [{}] from [{}] with cron [{}]", name, fileName, cron); } } }); } @EventListener(RefreshScopeRefreshedEvent.class) public void onRefresh() throws Exception { log.info("[DynamicSchedulerConfig :: onRefresh] :: Refreshing dynamic schedules..."); ScheduleMapVO refreshedMap = scheduleService.getScheduleList(); scheduleMapProvider.setScheduleMapVO(refreshedMap); scheduleTasks(); log.info("[DynamicSchedulerConfig :: onRefresh] :: Schedules refreshed successfully."); } public void refreshSchedules() throws Exception { log.info("[DynamicSchedulerConfig :: refreshSchedules] :: Manually triggering schedule refresh..."); ScheduleMapVO refreshedMap = scheduleService.getScheduleList(); scheduleMapProvider.setScheduleMapVO(refreshedMap); scheduleTasks(); log.info("[DynamicSchedulerConfig :: refreshSchedules] :: Manual refresh complete."); } }