snp-batch-validation/src/main/java/com/snp/batch/scheduler/SchedulerInitializer.java

122 lines
4.3 KiB
Java

package com.snp.batch.scheduler;
import com.snp.batch.global.model.JobScheduleEntity;
import com.snp.batch.global.repository.JobScheduleRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 애플리케이션 시작 시 DB에 저장된 스케줄을 Quartz에 자동 로드
* ApplicationReadyEvent를 수신하여 모든 빈 초기화 후 실행
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class SchedulerInitializer {
private final JobScheduleRepository scheduleRepository;
private final Scheduler scheduler;
/**
* 애플리케이션 준비 완료 시 호출
* DB의 활성화된 스케줄을 Quartz에 로드
*/
@EventListener(ApplicationReadyEvent.class)
public void initializeSchedules() {
log.info("========================================");
log.info("스케줄러 초기화 시작");
log.info("========================================");
try {
// DB에서 활성화된 스케줄 조회
List<JobScheduleEntity> activeSchedules = scheduleRepository.findAllActive();
if (activeSchedules.isEmpty()) {
log.info("활성화된 스케줄이 없습니다.");
return;
}
log.info("총 {}개의 활성 스케줄을 로드합니다.", activeSchedules.size());
int successCount = 0;
int failCount = 0;
// 각 스케줄을 Quartz에 등록
for (JobScheduleEntity schedule : activeSchedules) {
try {
registerSchedule(schedule);
successCount++;
log.info("✓ 스케줄 로드 성공: {} (Cron: {})",
schedule.getJobName(), schedule.getCronExpression());
} catch (Exception e) {
failCount++;
log.error("✗ 스케줄 로드 실패: {}", schedule.getJobName(), e);
}
}
log.info("========================================");
log.info("스케줄러 초기화 완료");
log.info("성공: {}개, 실패: {}개", successCount, failCount);
log.info("========================================");
// Quartz 스케줄러 시작
if (!scheduler.isStarted()) {
scheduler.start();
log.info("Quartz 스케줄러 시작됨");
}
} catch (Exception e) {
log.error("스케줄러 초기화 중 에러 발생", e);
}
}
/**
* 개별 스케줄을 Quartz에 등록
*
* @param schedule JobScheduleEntity
* @throws SchedulerException Quartz 스케줄러 예외
*/
private void registerSchedule(JobScheduleEntity schedule) throws SchedulerException {
String jobName = schedule.getJobName();
JobKey jobKey = new JobKey(jobName, "batch-jobs");
TriggerKey triggerKey = new TriggerKey(jobName + "-trigger", "batch-triggers");
// 기존 스케줄 확인 및 삭제
if (scheduler.checkExists(jobKey)) {
scheduler.deleteJob(jobKey);
log.debug("기존 Quartz Job 삭제: {}", jobName);
}
// JobDetail 생성
JobDetail jobDetail = JobBuilder.newJob(QuartzBatchJob.class)
.withIdentity(jobKey)
.usingJobData("jobName", jobName)
.withDescription(schedule.getDescription())
.storeDurably(true)
.build();
// CronTrigger 생성
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(triggerKey)
.withSchedule(CronScheduleBuilder.cronSchedule(schedule.getCronExpression())
.withMisfireHandlingInstructionDoNothing())
.forJob(jobKey)
.build();
// Quartz에 스케줄 등록
scheduler.scheduleJob(jobDetail, trigger);
// 다음 실행 시간 로깅
if (trigger.getNextFireTime() != null) {
log.debug(" → 다음 실행 예정: {}", trigger.getNextFireTime());
}
}
}