package com.tsi.comm.server; import com.tsi.comm.server.config.ApplicationConfig; import com.tsi.comm.server.config.TsiCvimServerConfig; import com.tsi.comm.server.kafka.KafkaConsumerService; import com.tsi.comm.server.kafka.KafkaProducerService; import com.tsi.comm.server.process.dbms.TsiCvimDbmsProcess; import com.tsi.comm.server.process.logging.TsiCvimLoggingProcess; import com.tsi.comm.server.process.packet.TsiCvimPacketProcess; import com.tsi.comm.server.repository.ApplicationRepository; import com.tsi.comm.server.service.TsiCommServerService; import com.tsi.comm.server.tcp.TsiCvimServer; import com.tsi.comm.server.vo.TsiAlarmConfigVo; import com.tsi.comm.server.vo.mariadb.AbstractDbmsVo; import com.tsi.comm.server.vo.mariadb.AlarmOccrVo; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.text.SimpleDateFormat; import java.util.Date; @Slf4j @RequiredArgsConstructor //@Component public class ApplicationLifecycle { private final ApplicationConfig applicationConfig; private final TsiCvimServerConfig serverConfig; private final TsiCommServerService tsiCommServerService; private final KafkaConsumerService kafkaConsumerService; private final KafkaProducerService kafkaProducerService; private final TsiCvimPacketProcess packetProcess; private final TsiCvimLoggingProcess loggingProcess; private final TsiCvimDbmsProcess dbmsProcess; private final TsiCvimServer cvimServer; private String applicationName; private int serverId = 0; @PostConstruct public void start() { SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); log.info(""); log.info("************************************************************************************"); log.info("** TSI CVIM System **"); log.info("** CVIM Communication Server Program. **"); log.info("** [ver.1.0] **"); log.info("** startup: {}", sdfDate.format(new Date())); log.info("************************************************************************************"); this.applicationConfig.setStartSchedule(false); serverId = this.serverConfig.getServerId(); this.applicationName = TsiCommServerApplication.APPLICATION_NAME + "-" + this.serverConfig.getServerId(); ApplicationRepository.processStateVo.setProcessId(applicationName); this.tsiCommServerService.init(); this.kafkaConsumerService.start(); this.kafkaProducerService.start(); this.packetProcess.start(); this.loggingProcess.start(); this.dbmsProcess.start(); // Netty 서버 시작은 백그라운드 스레드에서 실행되므로, 이 메서드를 블로킹하지 않습니다. this.cvimServer.run(); this.serverConfig.setStartup(true); try { // 시스템 시작 알람 이력 저장 AlarmOccrVo alarm = new AlarmOccrVo(AbstractDbmsVo.DBMS_ALARM_OCCR_HS); alarm.setAlarmCode(TsiAlarmConfigVo.SYS_00); alarm.setAlarmTarget(applicationName); alarm.setAlarmValue("Running"); this.tsiCommServerService.insertAlarmOccrHs(alarm); this.tsiCommServerService.updateProcessState(0); } catch(Exception e) { log.error("Error during startup alarm logging: {}", e.getMessage()); } this.applicationConfig.setStartSchedule(true); log.info("Application has been started successfully."); } /** * Spring 컨텍스트가 종료될 때, 빈이 소멸되기 직전에 호출됩니다. */ @PreDestroy public void stop() { SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); log.error("************************************************************************************"); log.error("** Application Terminating: {}", sdfDate.format(new Date())); // 종료는 생성의 역순이 안전합니다. // 1. 새로운 연결을 막습니다. if (this.cvimServer != null) { this.cvimServer.stop(); log.info("Netty Server stopped."); } if (this.kafkaConsumerService != null) { this.kafkaConsumerService.shutdown(); log.info("Kafka Consumer stopped."); } // 2. 내부 처리 로직(워커 스레드 풀)을 종료합니다. if (this.packetProcess != null) { this.packetProcess.stop(); log.info("Packet Process stopped."); } if (this.loggingProcess != null) { this.loggingProcess.stop(); log.info("Logging Process stopped."); } if (this.dbmsProcess != null) { this.dbmsProcess.stop(); log.info("DBMS Process stopped."); } // 3. 외부로 나가는 통신(Kafka Producer)을 종료합니다. if (this.kafkaProducerService != null) { this.kafkaProducerService.shutdown(); log.info("Kafka Producer stopped."); } // 4. DB에 최종 상태를 기록합니다. if (this.tsiCommServerService != null) { this.serverConfig.setStartup(false); int serverId = this.serverConfig.getServerId(); this.tsiCommServerService.updateNodeStatusTerm(serverId); // ... 시스템 종료 알람 저장 로직 ... this.tsiCommServerService.updateProcessState(2); log.info("Final DB states updated."); } log.error("************************************************************************************"); } }