package com.bsmlab.dfx.agent.config; import com.bsmlab.dfx.agent.DfxAgentApplication; import com.bsmlab.dfx.agent.config.datasource.DynamicDataSourceService; import com.bsmlab.dfx.agent.config.datasource.DynamicRoutingDataSource; import com.bsmlab.dfx.agent.config.datasource.RefreshableSqlSessionFactoryBean; import io.micrometer.common.util.StringUtils; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.dbcp2.BasicDataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.annotation.MapperScans; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.*; import java.util.concurrent.Executor; @Slf4j @Getter @Configuration @RequiredArgsConstructor @MapperScans({ @MapperScan("com.bsmlab.dfx.agent") }) public class DfxAgentConfiguration { // gradle bootRun 실행 설정 // bootRun D:\projects\bsm-lab\dfx\dfxagent\src\docs\settings-examples\dfxagent.json" // command line java 실행 설정 // java -jar dfxagent.jar --setting.file=D:\projects\bsm-lab\dfx\dfxagent\src\docs\settings-examples\dfxagent.json //example: setting.file=D:\projects\bsm-lab\dfx\dfxagent\src\docs\settings-examples\dfxagent.json @Value("${setting.file}") private String settingFile; private String pid; private Map temporarySqlSessionFactoryMap = new HashMap<>(); Map temporaryTransactionManagerMap = new HashMap<>(); // agent 설정 관리자. 대부분의 기능에 필요함 @Bean(name = "agentConfigReader") public AgentConfigReader agentConfigReader() throws URISyntaxException, IOException { // 실행확인됨 if(StringUtils.isBlank(this.settingFile)) { log.error("cannot found a setting file. {}", this.settingFile); log.error("exit application"); System.exit(0); } AgentConfigReader agentConfigReader = new AgentConfigReader(); agentConfigReader.loadConfigFile(this.settingFile); // Process ID를 찾아 app_home/proc/pid 파일에 기록한다. 이후 shutdown.sh에서 해당 pid를 사용하여 kill -9 pid 를 실행한다. this.pid = String.valueOf(ProcessHandle.current().pid()); URL location = DfxAgentApplication.class.getProtectionDomain().getCodeSource().getLocation(); URI uri = location.toURI(); File file; if ("file".equalsIgnoreCase(uri.getScheme())) { // 파일 시스템 상의 경로 file = new File(uri); } else if ("jar".equalsIgnoreCase(uri.getScheme()) || location.toString().startsWith("jar:")) { // JAR 내부 경로일 경우 String path = location.toString(); log.debug("path: {}", path); if (path.startsWith("jar:")) { path = path.substring(4, path.indexOf("!") - 1); path = path.substring(path.indexOf(":") + 1, path.lastIndexOf("/")); } log.debug("path: {}", path); file = new File(path); } else { // 파일 시스템 상의 경로 file = new File(uri); } String procDirectoryPath = file.getParent() + File.separator + "proc"; File procFile = new File(procDirectoryPath); if(!procFile.exists()) { procFile.mkdirs(); } Path pidFilePath = Path.of(procDirectoryPath + File.separator, "dfxagent.pid"); Files.writeString(pidFilePath, pid, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); return agentConfigReader; } // 다중 데이터 소스 생성 @Bean(name = "dynamicRoutingDataSource") public DynamicRoutingDataSource dynamicRoutingDataSource(AgentConfigReader agentConfigReader) throws IOException { // 실행확인됨 DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource(); Map sqlSessionFactoryMap = new HashMap<>(); Map transactionManagerMap = new HashMap<>(); List xmlPathList = agentConfigReader.getAgentConfigDto().getSqlMapperLocations(); List resourceList = new ArrayList<>(); PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver(); for(String pathString : xmlPathList) { Resource[] resourceArray = pathMatchingResourcePatternResolver.getResources("file:" + pathString); resourceList.addAll(Arrays.asList(resourceArray)); } List dataSourceConfigList = agentConfigReader.getAgentConfigDto().getDataSourceConfig(); for(AgentConfigDto.DataSourceConfig dataSourceConfig : dataSourceConfigList) { try { BasicDataSource dataSource = createBasicDataSource(dataSourceConfig); SqlSessionFactoryBean sqlSessionFactoryBean = new RefreshableSqlSessionFactoryBean();//new SqlSessionFactoryBean(); org.apache.ibatis.session.Configuration mybatisConfiguration = new org.apache.ibatis.session.Configuration(); sqlSessionFactoryBean.setConfiguration(mybatisConfiguration); sqlSessionFactoryBean.setFailFast(true); sqlSessionFactoryBean.setDataSource(dataSource); sqlSessionFactoryBean.setMapperLocations(resourceList.toArray(new Resource[0])); sqlSessionFactoryBean.afterPropertiesSet(); sqlSessionFactoryMap.put(dataSourceConfig.getDataSourceId(), sqlSessionFactoryBean.getObject()); DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource); transactionManagerMap.put(dataSourceConfig.getDataSourceId(), transactionManager); dynamicRoutingDataSource.addDataSource(dataSourceConfig.getDataSourceId(), dataSource); } catch (Exception e) { log.error("DynamicRoutingDataSource 생성 중 오류: {}", e.getMessage(), e); throw new RuntimeException(e); } } this.temporarySqlSessionFactoryMap = sqlSessionFactoryMap; this.temporaryTransactionManagerMap = transactionManagerMap; return dynamicRoutingDataSource; } private static BasicDataSource createBasicDataSource(AgentConfigDto.DataSourceConfig dataSourceConfig) { BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName(dataSourceConfig.getDriverClassName()); dataSource.setUrl(dataSourceConfig.getUrl()); dataSource.setUsername(dataSourceConfig.getUsername()); dataSource.setPassword(dataSourceConfig.getPassword()); if(oracle.jdbc.driver.OracleDriver.class.getCanonicalName().equals(dataSourceConfig.getDriverClassName())) { dataSource.setValidationQuery("SELECT 1 FROM DUAL"); } else if(org.postgresql.Driver.class.getCanonicalName().equals(dataSourceConfig.getDriverClassName())) { dataSource.setValidationQuery("SELECT 1"); } else if(com.mysql.jdbc.Driver.class.getCanonicalName().equals(dataSourceConfig.getDriverClassName())) { dataSource.setValidationQuery("SELECT 1"); } else if(org.mariadb.jdbc.Driver.class.getCanonicalName().equals(dataSourceConfig.getDriverClassName())) { dataSource.setValidationQuery("SELECT 1"); } else if(com.ibm.db2.jcc.DB2Driver.class.getCanonicalName().equals(dataSourceConfig.getDriverClassName())) { dataSource.setValidationQuery("SELECT 1 FROM SYSIBM.SYSDUMMY1"); } dataSource.setTestOnBorrow(false); dataSource.setTestOnReturn(false); dataSource.setTestOnCreate(true); dataSource.setTestWhileIdle(true); dataSource.setInitialSize(3); dataSource.setMinIdle(3); dataSource.setMaxIdle(30); dataSource.setMaxTotal(30); return dataSource; } // 다중 데이터 소스와 그에 해당하는 sqlSession, transactionManager 설정 @Bean(name = "dynamicDataSourceService") public DynamicDataSourceService dynamicDataSourceService() { // 실행확인됨 DynamicDataSourceService dynamicDataSourceService = new DynamicDataSourceService(); dynamicDataSourceService.setSqlSessionFactoryMap(this.temporarySqlSessionFactoryMap); dynamicDataSourceService.setTransactionManagerMap(this.temporaryTransactionManagerMap); return dynamicDataSourceService; } // (수신 처리) 메시지 수신 - 저장 후 수신 메시지 처리 쓰레드 설정 @Bean(name = "dropBoxProcessorThreadPoolTaskExecutor") public Executor dropBoxProcessorThreadPoolTaskExecutor() { ThreadPoolTaskExecutor dropBoxProcessorThreadPoolTaskExecutor = new ThreadPoolTaskExecutor(); dropBoxProcessorThreadPoolTaskExecutor.setCorePoolSize(30); // 최소 쓰레드 dropBoxProcessorThreadPoolTaskExecutor.setMaxPoolSize(300); // 최대 쓰레드 dropBoxProcessorThreadPoolTaskExecutor.setQueueCapacity(300); // 대기 큐 dropBoxProcessorThreadPoolTaskExecutor.setThreadNamePrefix("dropBoxProcessor-"); dropBoxProcessorThreadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true); // 종료 시 대기 여부 dropBoxProcessorThreadPoolTaskExecutor.initialize(); return dropBoxProcessorThreadPoolTaskExecutor; } // (송신 처리) 메시지 송신 쓰레드 설정 @Bean(name = "scheduledPostmanThreadPoolTaskScheduler") public ThreadPoolTaskScheduler scheduledPostmanThreadPoolTaskScheduler() { // 실행확인됨 ThreadPoolTaskScheduler scheduledPostmanThreadPoolTaskScheduler = new ThreadPoolTaskScheduler(); scheduledPostmanThreadPoolTaskScheduler.setPoolSize(10); scheduledPostmanThreadPoolTaskScheduler.setThreadNamePrefix("postman-scheduler-"); scheduledPostmanThreadPoolTaskScheduler.initialize(); return scheduledPostmanThreadPoolTaskScheduler; } }