java/배치 & Scheduling

ScheduledExecutorService + db 저장업무

내가 만드는게 길이 된다 2023. 10. 25. 15:28
package com.twokimss.config;

import javax.annotation.PostConstruct;

import org.apache.ibatis.javassist.bytecode.stackmap.TypeData.ClassName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;

import com.twokimss.web.batch.service.ScheduleExec;
import com.twokimss.web.batch.service.SchedulerFuture2Service;
import com.twokimss.web.batch.service.SchedulerFutureService;
import com.twokimss.web.batch.service.SchedulerService;
import com.twokimss.web.util.ContextUtil;

//스케쥴 테스트하고 싶을때 아래 3개 주석 풀어서 사용하기
@Configuration
@EnableScheduling
@EnableAsync
public class ScheduleConfig {
	// 스프링 부트에서 스케줄러 기능을 사용하기 위해 @EnableScheduling 설정

	private Logger logger = LoggerFactory.getLogger(ClassName.class);
	
	//@ConditionalOnBean(ContextUtil.class)

	@Autowired
	private SchedulerService schedulerService;

	@Autowired
	private SchedulerFutureService schedulerFutureService;
	
	@Autowired
	private SchedulerFuture2Service schedulerFuture2Service;	
	

//	1. Component는 class level에 선언,
//	   Bean은 메소드 레벨에 선언
//	2. Bean을 사용시 Configuration 어노테이션을 꼭 사용
//	   또는 @SpringBootApplication 어노테이션이 들어간 스프링 실행부에 사용
//	3. Bean은 외부 라이브러리가 제공하는 객체를 사용할 때
//	   Component는 내부에서 직접 접근 가능한 클래스에 사용
//	4. Bean은 유연한 빈 등록이 필요할때 사용(환경파일읽어서 동적생성)
	
	
	//순서: 생성자, @PostConstruct, @Bean

	public ScheduleConfig() {
		logger.debug("ScheduledConfig=================>생성자");
	}

	@PostConstruct
	public void init() {
		logger.debug("ScheduledConfig=================>init");

		//테스트할때 아래 열고하기...
		//schedulerFutureService.start();
		//schedulerFuture2Service.scheduleJob("biz003");
		

//      Thread.sleep(10000);
//      schedulerFutureService.changeCron("*/3 * * * * *");
//      Thread.sleep(20000);	    

	}
	
	@Bean
	public ScheduleExec scheduler() {
		logger.debug("ScheduleExec=================>ScheduleExec Bean 등록");
		
		ScheduleExec scheduleExec = new ScheduleExec();
		
		scheduleExec.createScheduler();
		
		return scheduleExec;
	}	

//	@Bean
//	public TaskScheduler scheduler() {
//		logger.debug("ScheduledConfig=================>TaskScheduler Bean 등록");
//		
//		ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
//		scheduler.setPoolSize(4);
//		scheduler.setThreadNamePrefix("threadBiz-");//threadNamePrefix
//		scheduler.setBeanName("threadPoolTaskScheduler");
//		scheduler.initialize();
//
//		
//		return scheduler;
//	}

}

 

package com.twokimss.web.batch.service;

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;

import org.apache.ibatis.javassist.bytecode.stackmap.TypeData.ClassName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;


public class ScheduleExec {
	
	private Logger logger = LoggerFactory.getLogger(ClassName.class);
	
	
	@Autowired
	private JobBeforeProc jobBeforeProc;
	
		
	private String beanType = "";
	private int SINGLE_POOL_SIZE = 4;
	private long delay = 1000 * 2; //2초 주기
	
	private ScheduledExecutorService scheduler = null;
	private HashMap<String, ScheduledFuture<?>> mapJobs = null;
	


	public ScheduleExec() {
		logger.debug("ScheduleExec=========================>");
		this.mapJobs = new HashMap<String, ScheduledFuture<?>>();
		
		this.beanType = JobConst.BEAN_TYPE_ONLY_CLASS;
		//this.beanType = JobConst.BEAN_TYPE_WITH_PACKAGE; //다른 서비스 막 호출해서 쓸려면 이방법으로는 안되겠다.
	}
	
	
	public void createScheduler() {
		logger.debug("createScheduler=========================>");
		this.scheduler = Executors.newScheduledThreadPool(SINGLE_POOL_SIZE);
	}
	
	@PostConstruct
	public void startScheduleList() {
		logger.debug("startScheduleList=========================>");
		
		//사용할 배치 작업 여기에 등록
		//현재는 scheduleWithFixedDelay 만 사용했는데, next trigger 운영환경에서 변경할수 있도록 매소드 추가 필요. (Cron, Rate, Fix....)
		
//테스트 할때 아래 주석 풀고 하기		
		//this.scheduleJob("job00401"); 
		this.scheduleJob("job00402");
		
	}
	
	


	public void scheduleJob(String jobKey) {
		logger.debug("scheduleJob start=========================>");
		logger.debug("futureKey=================================>"+jobKey);
				
		
		HashMap<String,Object> mapRtn = jobBeforeProc.getClassNm(jobKey, this.beanType);
		if("-1".equals(mapRtn.get("rtnCd").toString())){
			logger.debug("futureKey================["+jobKey+"]실행중지된 작업");
			return;
		}
		

		logger.debug("scheduleJob start========classNm=================>"+mapRtn.get("classNm").toString());
		
		Runnable runnable = new JobRunnable(jobKey, mapRtn); //jobKey 에 맞는 Biz Class Get
		//long initialDelay = 1000 * 60;
		//this.delay = 1000 * 60;
		logger.debug(jobKey+"=========>job등록시간 ====>"+JobDateUtil.getDateTime());
		//ScheduledFuture<?> job = this.scheduler.scheduleWithFixedDelay(runnable, initialDelay, this.delay, TimeUnit.MILLISECONDS); //schedule 에 Biz Class 등록, job종료시점에서 delay후 재시작
		//ScheduledFuture<?> job = this.scheduler.scheduleAtFixedRate(runnable, initialDelay, this.delay, TimeUnit.MILLISECONDS); //schedule 에 Biz Class 등록, job시작시점에서 delay후 재시작
		
		
		/*
		 * 오전 6시 30분 30초에 시작
		 */
		int iH = 6;
		int iM = 30;
		int iS = 30;
		long initialDelay = JobDateUtil.getInitialDelay(iH, iM, iS);
		
//		/**
//		 * 매일
//		 */
//		this.delay = JobDateUtil.getDelay(1, JobConst.DELAY_TYPE_DAY);		
		
		
//		/**
//		 * 2시간 주기로
//		 */
//		this.delay = JobDateUtil.getDelay(2, JobConst.DELAY_TYPE_HH);
		
		/**
		 * 5분 주기로
		 */
		this.delay = JobDateUtil.getDelay(5, JobConst.DELAY_TYPE_MI);		
		
		
		ScheduledFuture<?> job = this.scheduler.scheduleAtFixedRate(runnable, initialDelay, this.delay, TimeUnit.MILLISECONDS); //schedule 에 Biz Class 등록, job시작시점에서 delay후 재시작
		
		
 
		this.mapJobs.put(jobKey, job); //pause, delete, reschedule 용으로 담아두기
	}

	
	public void pauseDeleteJob(String jobKey) {
		
		if(jobKey == null|| jobKey.isBlank()) return;
		if(this.mapJobs == null || this.mapJobs.isEmpty()) return;
		
		ScheduledFuture<?> targetJob = this.mapJobs.get(jobKey);
				
		if(targetJob == null) return;
		if(targetJob.isCancelled()) return;
		if(targetJob.isDone()) return;
		
		targetJob.cancel(true); 
	}
	
	
	public void rescheduleJob(String jobKey) {

		this.pauseDeleteJob(jobKey);

		this.mapJobs.remove(jobKey);

		this.setDelay(1000 * 10);

		this.scheduleJob(jobKey);

	}	
	
	
	private void setDelay(long delay) {
		this.delay = delay;
		
		logger.debug("setDelay=====>"+this.delay);
	}		
	
	
	public void shutdownScheduler(boolean isNow) {
		if(isNow) {
			logger.debug("shutdownScheduler=====>true");
			
			this.scheduler.shutdownNow();
		}else {
			logger.debug("shutdownScheduler=====>false");
			
			this.scheduler.shutdown();
		}
	}
	
	

}

 

package com.twokimss.web.batch.service;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;

import org.apache.ibatis.javassist.bytecode.stackmap.TypeData.ClassName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.twokimss.web.util.ContextUtil;

public class JobRunnable implements Runnable {
	private Logger logger = LoggerFactory.getLogger(ClassName.class);

	private String jobKey = "";
	private String classNm = "";
	private String beanType = null; 

	public JobRunnable() {
	}

	public JobRunnable(String jobKey, HashMap<String, Object> clssInfo) {
		this.jobKey = jobKey;
		this.classNm = clssInfo.get("classNm").toString();
		this.beanType = clssInfo.get("beanType").toString();
	}

	@Override
	public void run() {

		this.invokeClass(this.beanType);

	}


	private void invokeClass(String beanType) {
		
		try {
			if (beanType.equals(JobConst.BEAN_TYPE_WITH_PACKAGE)) {

				this.invokeWithPackage();

			} else if (beanType.equals(JobConst.BEAN_TYPE_ONLY_CLASS)) {
				
				this.invokeOnlyClass();
			}

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	private void invokeOnlyClass() {
		
		try {
			String methodNm = "execute";
			// Object[] methodArgs = null;
			Method method = null;
			Object obj = null;			
			
			////ContextUtil.getBean 방식으로 했더니 톰켓 start 할때 nullpoint 에러 나서. ( 실행할때는 문제가 없는데 )
			try {
				obj = ContextUtil.getBean(this.classNm); // 스프링에 등록된 서비스 클래스 가져오기
			} catch (NullPointerException ee) {
				logger.debug("ContextUtil.getBean 로드 에러");
				return;
			}

			String taskClassName = obj.getClass().getName().split("\\$")[0];

			Class<?> taskClass = getClass().getClassLoader().loadClass(taskClassName);

			method = taskClass.getDeclaredMethod(methodNm);
			// methodArgs = new Object[] {}; //메서드 param
			// method.invoke(obj, methodArgs);
			method.invoke(obj);			
			
		} catch (Exception e) {

		}

	}

	private void invokeWithPackage() {
		
		try {
			
			String methodNm = "execute";
			// Object[] methodArgs = null;

			Class<?> clss = Class.forName(this.classNm);

			Constructor<?> constructor = clss.getConstructor();
			Object obj = constructor.newInstance();

			Method method = clss.getDeclaredMethod(methodNm);
			// methodArgs = new Object[] {}; //메서드 param
			// method.invoke(obj, methodArgs); //파라미터 있을때
			method.invoke(obj);			
			
		} catch (Exception e) {
			
		}

	}

}

 

package com.twokimss.web.batch.service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.javassist.bytecode.stackmap.TypeData.ClassName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.twokimss.web.filemng.service.impl.FileServiceImpl;

@Service("jobBiz00401")
public class JobBiz00401 {

	private Logger logger = LoggerFactory.getLogger(ClassName.class);
	
//JobConst.BEAN_TYPE_WITH_PACKAGE 방식일때 	@Autowired 로는 작동을 안해서
	@Autowired
	private JobSyncBiz jobSyncBiz;
	
	@Autowired
	private JobAsyncBiz jobAsyncBiz;
	
	@Autowired
	private FileServiceImpl fileServiceImpl;
	
    @Autowired
    private ScheduleExec scheduleExec;   	
	
//	private JobSyncBiz jobSyncBiz;
//	private JobAsyncBiz jobAsyncBiz;
	

	public JobBiz00401() {
//다른 서비스 막 호출해서 쓸려면 이방법으로는 안되겠다.		
//		this.jobSyncBiz = new JobSyncBiz();
//		this.jobAsyncBiz = new JobAsyncBiz();
	}
	
	@Transactional(value="transactionManagerOracle", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
	public void execute() throws Exception{
		//Biz 로직 구현
		logger.info("[00401===>실행 시작] : {}", JobDateUtil.getDateTime());
		
		this.jobSyncBiz.saveFileInfo();
		//this.jobAsyncBiz.createFile();		

		//비동기 실행때문에 CompletableFuture 에서 호출		
//		CompletableFuture<Void> future = CompletableFuture.runAsync(()->{
//			
//				try {
//					this.jobAsyncBiz.createFile();
//				} catch (Exception e) {
//					throw new CompletionException(e); //jobAsyncBiz.createFile 에서 throw 발생시켰을때
//				}
//				
//			}
//		);
		
//		//비동기 실행때문에 CompletableFuture 에서 호출	
//		//jobAsyncBiz.createFile 에서 return 값이 있을때 supplyAsync 사용후 Integer return 타입으로 받음		
//	    CompletableFuture<Integer> a = CompletableFuture.supplyAsync(() -> {
//		        try { 
//		        	return this.jobAsyncBiz.createFile();
//		        }
//		        catch(Exception ex) { 
//		        	throw new CompletionException(ex); //jobAsyncBiz.createFile 에서 throw 발생시켰을때 
//		        }
//	    	}
//	    );		
		
		
		//**********************************************************************************
		//결과정보 조회
		//**********************************************************************************
		Map mapGrqSeq = new HashMap<String,Object>();
		//mapGrqSeq.put("GRP_SEQ", "1"); 
		List<?> list = null;
		try {
			list = fileServiceImpl.selFileList(mapGrqSeq);
		} catch (Exception e) {
			e.printStackTrace();
		}
		logger.debug("DB 저장후 조회된 파일건수 >> " + list.size());		
		
		//11번 생성후 rollback 처리되고, job 은 멈추고
		if(list.size() >= 11) {
			logger.debug("강제에러발생===========================>");
			scheduleExec.pauseDeleteJob("job00401");
			throw new Exception("강제에러");
		}		
		
		logger.info("[00401===>실행 종료] : {}", JobDateUtil.getDateTime());
	}
	
	

//	public void execute() {
//		
//		logger.info("[00401===>실행 시작] : {}", JobDateUtil.getDateTime());
//
//		try {
//			Thread.sleep(1000 * 2); //2초 대기
//			
//			
//		} catch (InterruptedException e) {
//			e.printStackTrace();
//		}	
//		
//		logger.info("[00401===>실행 종료] : {}", JobDateUtil.getDateTime());
//		
//	}


}

 

package com.twokimss.web.batch.service;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FilenameUtils;
import org.apache.ibatis.javassist.bytecode.stackmap.TypeData.ClassName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.twokimss.web.filemng.service.impl.FileServiceImpl;

@Service("jobSyncBiz")
public class JobSyncBiz {
	
	//빨리 끝나는 업무 : 동기
	
	private Logger logger = LoggerFactory.getLogger(ClassName.class);
	
	@Autowired
	private FileServiceImpl fileServiceImpl;
	
	
	@Transactional(value="transactionManagerOracle", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
	public void saveFileInfo() {
		
		logger.info("[saveFileInfo===>실행 시작] : {}", JobDateUtil.getDateTime());
		
		
//		try {
//			Thread.sleep(1000 * 2); //2초 대기
//		} catch (InterruptedException e) {
//			e.printStackTrace();
//		}	
		
		
		//**********************************************************************************
		//결과정보 table 저장
		//**********************************************************************************		
		List<Map<String, Object>> listDs = new ArrayList<Map<String, Object>>();
		Map<String, Object> mapRow = new HashMap<String,Object>(); //파일정보 map
		String filePath = "D:\\devmine\\fileroot\\";
		
		filePath = filePath.replace("\\\\", "\\").replace("//", "/");
		mapRow.put("DEL_YN", "N");						
		

		String fileName = "tempTest.png";
		long fileSize = 1024;
		fileName = FilenameUtils.getName(fileName);
		String baseName = FilenameUtils.getBaseName(fileName); //확장자제외한 파일명
		String fileExt = FilenameUtils.getExtension(fileName); //확장자만 구하기
				
		
		mapRow.put("PATH", filePath); 				//파일경로(파일명을 제외한 PATH)
		mapRow.put("FULL_PATH", filePath+fileName); //파일경로(파일명 포함한 PATH)
		mapRow.put("FILE_NAME", fileName); 			//서버에 저장된 파일이름
		mapRow.put("ORIGINAL_NAME", fileName); 		//파일원래이름
		mapRow.put("ORIGINAL_EXTENSION", fileExt);	//파일원래 확장자
		mapRow.put("FILE_SIZE", fileSize);			//파일사이즈					
		mapRow.put("FULL_PATH", filePath+fileName); //파일경로(파일명 포함한 PATH)
		mapRow.put("FILE_NAME", fileName);			//서버에 저장된 파일이름
		listDs.add(mapRow);
		
		Map<String, Object> inListInfo = new HashMap<String,Object>();
		inListInfo.put("ds_fileInfo", listDs); 		
		
		Map mapGrqSeq = null;
		try {
			mapGrqSeq = fileServiceImpl.saveFileInfo(inListInfo); ////파일그룹번호 return 됨
			logger.debug("생성된 파일그룹 SEQ >>> "  + mapGrqSeq.get("GRP_SEQ"));
		} catch (Exception e) {
			e.printStackTrace();
		} 
		
		
		logger.info("[saveFileInfo===>실행 종료] : {}", JobDateUtil.getDateTime());
	}

}

 

package com.twokimss.web.batch.service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.javassist.bytecode.stackmap.TypeData.ClassName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import com.twokimss.web.filemng.service.impl.FileServiceImpl;

@Service("jobAsyncBiz")
public class JobAsyncBiz {
	
	//오래걸리는 업무 : 비동기 
	
	private Logger logger = LoggerFactory.getLogger(ClassName.class);
	
	@Autowired
	private FileServiceImpl fileServiceImpl;
	
	@Async
	public int createFile() throws Exception {
		
		
		logger.info("[createFile==============>실행 시작] : {}", JobDateUtil.getDateTime());
		
//		try {
//			Thread.sleep(1000 * 20); //20초 대기
//			
//			//물리파일 생성 작업 여기에 코딩...
//			
//		} catch (InterruptedException e) {
//			e.printStackTrace();
//		}	
		
		//**********************************************************************************
		//결과정보 조회
		//**********************************************************************************
		Map mapGrqSeq = new HashMap<String,Object>();
		//mapGrqSeq.put("GRP_SEQ", "1"); 
		List<?> list = null;
		try {
			list = fileServiceImpl.selFileList(mapGrqSeq);
		} catch (Exception e) {
			e.printStackTrace();
		}
		logger.debug("DB 저장후 조회된 파일건수 >> " + list.size());		
		
		if(list.size() >= 10) {
			throw new Exception("강제에러"); //throw 발생시켰을때 확인하려고
		}
		
		
		logger.info("[createFile=============>실행 종료] : {}", JobDateUtil.getDateTime());
		
		return 0;
	}		

}

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.twokimss.filemng.database.mapper.FileMngMapper"> 


	<select id="selNewGrpSeq" parameterType="map" resultType="map">
		SELECT NVL(MAX(GRP_SEQ), 0) + 1 AS NEW_GRP_SEQ
		  FROM TBL_FILE_MNG
	</select>
  
	
	<!-- 저장 -->
	<insert id="insFileMng" parameterType="map">
	<selectKey resultType="map" keyProperty="NEW_SEQ" order="BEFORE">
		 SELECT TO_NUMBER(NVL(MAX(X.SEQ), 0)) + 1 AS NEW_SEQ 
	  	   FROM TBL_FILE_MNG X
	  	  WHERE X.GRP_SEQ = #{NEW_GRP_SEQ} 
   	</selectKey>	
		INSERT INTO TBL_FILE_MNG(
			  GRP_SEQ               /*파일그룹번호*/
			, SEQ                   /*파일번호*/
			, PATH                  /*파일경로(파일명을 제외한 PATH)*/
			, FULL_PATH             /*파일경로(파일명 포함한 PATH)*/
			, FILE_NAME             /*서버에 저장된 파일이름*/
			, ORIGINAL_NAME         /*파일원래이름*/
			, ORIGINAL_EXTENSION    /*파일원래 확장자*/
			, FILE_SIZE             /*파일사이즈*/
			, REF_TABLE             /*관련 업무테이블 ID >>  업무테이블에는 FILE_GR_NO(파일 다건 저장시는 이컬럼만 관리), FILE_NO(파일 단건 저장시 이 컬럼까지 관리)*/
			, REF_TABLE_KEY         /*관련 업무테이블의 KEY (||)JOIN 으로 입력*/
			, DEL_YN                /*삭제여부*/
			, REG_DT                /*등록일*/
			, REG_ID                /*등록자*/
			, MOD_DT                /*수정일*/
			, MOD_ID                /*수정자*/
		)VALUES(
			  #{NEW_GRP_SEQ}                         /*파일그룹번호*/
			, #{NEW_SEQ}    						 /*파일번호*/
			, #{PATH}                                /*파일경로(파일명을 제외한 PATH)*/
			, #{FULL_PATH}                           /*파일경로(파일명 포함한 PATH)*/
			, #{FILE_NAME}                           /*서버에 저장된 파일이름*/
			, #{ORIGINAL_NAME}                       /*파일원래이름*/
			, #{ORIGINAL_EXTENSION}                  /*파일원래 확장자*/
			, #{FILE_SIZE}                           /*파일사이즈*/
			, #{REF_TABLE}                           /*관련 업무테이블 ID >>  업무테이블에는 FILE_GR_NO(파일 다건 저장시는 이컬럼만 관리), FILE_NO(파일 단건 저장시 이 컬럼까지 관리)*/
			, #{REF_TABLE_KEY}                       /*관련 업무테이블의 KEY (||)JOIN 으로 입력*/
			, #{DEL_YN}                              /*삭제여부*/
			, TO_CHAR(SYSDATE, 'YYYYMMDD')           /*등록일*/
			, #{USER_ID}                             /*등록자*/
			, TO_CHAR(SYSDATE, 'YYYYMMDD')           /*수정일*/
			, #{USER_ID}                             /*수정자*/
		)

	</insert>
	
	<select id="selFileInfo" parameterType="map" resultType="map">
		SELECT 'N'                    AS CHK
		     , GRP_SEQ                AS GRP_SEQ              /*파일그룹번호*/
			 , SEQ                    AS SEQ                  /*파일번호*/
			 , PATH                   AS PATH                 /*파일경로(파일명을 제외한 PATH)*/
			 , FULL_PATH              AS FULL_PATH            /*파일경로(파일명 포함한 PATH)*/
			 , FILE_NAME              AS FILE_NAME            /*서버에 저장된 파일이름*/
			 , ORIGINAL_NAME          AS ORIGINAL_NAME        /*파일원래이름*/
			 , ORIGINAL_EXTENSION     AS ORIGINAL_EXTENSION   /*파일원래 확장자*/
			 , FILE_SIZE              AS FILE_SIZE            /*파일사이즈*/
			 , REF_TABLE              AS REF_TABLE            /*관련 업무테이블 ID >>  업무테이블에는 FILE_GR_NO(파일 다건 저장시는 이컬럼만 관리), FILE_NO(파일 단건 저장시 이 컬럼까지 관리)*/
			 , REF_TABLE_KEY          AS REF_TABLE_KEY        /*관련 업무테이블의 KEY (||)JOIN 으로 입력*/
			 , DEL_YN                 AS DEL_YN               /*삭제여부*/
		  FROM TBL_FILE_MNG
		 WHERE 1=1
	    <if test="GRP_SEQ != null and GRP_SEQ != '' ">
		   AND GRP_SEQ = #{GRP_SEQ}
	    </if>
	     ORDER BY GRP_SEQ, SEQ
	</select>
     
    
</mapper>

 

package com.twokimss.web.filemng.service.impl;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.session.SqlSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;


@Service("fileServiceImpl")
public class FileServiceImpl {
	private static final Logger logger = LoggerFactory.getLogger(ClassName.class);


	@Autowired
	@Qualifier("sqlSessionOracle")
	private SqlSession sqlSessionOracle;
		
	
	private final static String baseDomain = "com.twokimss.filemng.database.mapper.";
	
	
	//파일정보저장 - 신규
	@Transactional(value="transactionManagerOracle", propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Map saveFileInfo(Map inListInfo) throws Exception {
		
		
		List<Map> listSave = (List<Map>)inListInfo.get("ds_fileInfo");
		
		logger.debug("saveFileInfo>>listSave>>{}", listSave);
		
		
		String sUserId = "adminTEST";
		
		String querySel = baseDomain + "FileMngMapper.selNewGrpSeq";
		String queryIns = baseDomain + "FileMngMapper.insFileMng";
		
		logger.debug("querySel>>"+querySel);
		logger.debug("queryIns>>"+queryIns);
		
		
		Map mapNewGrpSeq = new HashMap<String,Object>();
		for(int i=0;i<listSave.size();i++) {
			Map mapRow = listSave.get(i);
			logger.debug("saveFileInfo>>{}", mapRow);
			
			if(i == 0) {
				Object oResult = sqlSessionOracle.selectOne(querySel, mapRow);
				mapNewGrpSeq = (Map)oResult;
			}
			mapRow.put("USER_ID", sUserId);
			mapRow.put("NEW_GRP_SEQ", mapNewGrpSeq.get("NEW_GRP_SEQ"));
			
			sqlSessionOracle.insert(queryIns, mapRow);
		}
		
		Map mapReturn = new HashMap<String, Object>();
		mapReturn.put("GRP_SEQ", mapNewGrpSeq.get("NEW_GRP_SEQ"));
		return mapReturn;
	}
	
	
	@SuppressWarnings("rawtypes")
	public List selFileList(Map map) throws Exception {

		String queryId = baseDomain + "FileMngMapper.selFileInfo";
		
		List<Map> list = sqlSessionOracle.selectList(queryId, map);
		
		logger.debug("selFileList >> list cnt >>" + list.size());
		
		return list;	
	}
	
	
	
}

 

package com.twokimss.web.batch.service;

import java.io.Serializable;

public class JobConst implements Cloneable, Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	
	public static final String BEAN_TYPE_WITH_PACKAGE = "1";
	public static final String BEAN_TYPE_ONLY_CLASS = "2";
	
	public static final String DELAY_TYPE_DAY = "Day";
	public static final String DELAY_TYPE_HH = "HH";
	public static final String DELAY_TYPE_MI = "Mi";
	

}

 

package com.twokimss.web.batch.service;

import java.util.HashMap;

import org.apache.ibatis.javassist.bytecode.stackmap.TypeData.ClassName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component("jobBeforeProc")
public class JobBeforeProc {
	
	private Logger logger = LoggerFactory.getLogger(ClassName.class);
	
	
	public HashMap<String, Object> getClassNm(String jobKey, String beanType) {
		HashMap<String, Object> mapRtn = new HashMap<String,Object>();
		
		if(JobConst.BEAN_TYPE_WITH_PACKAGE.equals(beanType)) {
			mapRtn = this.getBizClassNmWithPackage(jobKey);
		}else if(JobConst.BEAN_TYPE_ONLY_CLASS.equals(beanType)) {
			mapRtn = this.getBizClassNmOnlyClass(jobKey);
		}
		mapRtn.put("beanType", beanType);
		return mapRtn;
	}		


	//db/xml ( sm 하기 편한 방법 찾아서 변경 필요 ) : 운영하다가 사용여부 변경하고, 정지시킬수 있도록 처리 필요.
	private HashMap<String, Object> getBizClassNmWithPackage(String jobKey) {
		
		HashMap<String,Object> mapRtn = new HashMap<String, Object>();
		mapRtn.put("classNm", "");
		mapRtn.put("rtnCd", "-1");
		mapRtn.put("rtnMsg", "");
		
		String sPackage = "com.twokimss.web.batch.service.";
		
		
		switch (jobKey) {
		case "job00401":
			mapRtn.put("classNm", sPackage+"JobBiz00401");
			mapRtn.put("rtnCd", "0");
			mapRtn.put("rtnMsg", "가능");
			break;
		case "job00402":
			mapRtn.put("classNm", sPackage+"JobBiz00402");
			mapRtn.put("rtnCd", "0");
			mapRtn.put("rtnMsg", "가능");			
			break;			
		default:
			break;
		}
		
		
		return mapRtn;
		
	}
	

	private HashMap<String, Object> getBizClassNmOnlyClass(String jobKey) {
		
		
		HashMap<String,Object> mapRtn = new HashMap<String, Object>();
		mapRtn.put("classNm", "");
		mapRtn.put("rtnCd", "-1");
		mapRtn.put("rtnMsg", "");
		
		
		switch (jobKey) {
		case "job00401":
			mapRtn.put("classNm", "jobBiz00401");
			mapRtn.put("rtnCd", "0");
			mapRtn.put("rtnMsg", "가능");
			break;
		case "job00402":
			mapRtn.put("classNm", "jobBiz00402");
			//mapRtn.put("rtnCd", "-1"); //실행중지된 작업
			mapRtn.put("rtnCd", "0");
			mapRtn.put("rtnMsg", "가능");			
			break;			
		default:
			break;
		}
		
		
		return mapRtn;
		
	}



		
}

 

package com.twokimss.web.batch.service;

import org.apache.ibatis.javassist.bytecode.stackmap.TypeData.ClassName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

@Service("jobBiz00402")
public class JobBiz00402 {

	private Logger logger = LoggerFactory.getLogger(ClassName.class);


	public void execute() {
		//Biz 로직 구현
		
		logger.info("[00402===>실행 시작] : {}", JobDateUtil.getDateTime());

		try {
			Thread.sleep(1000 * 2); //2초 대기
		} catch (InterruptedException e) {
			e.printStackTrace();
		}		
		
		logger.info("[00402===>실행 종료] : {}", JobDateUtil.getDateTime());
		
	}


}

 

package com.twokimss.web.login.controller;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.ibatis.javassist.bytecode.stackmap.TypeData.ClassName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.twokimss.web.batch.service.JobDateUtil;
import com.twokimss.web.batch.service.ScheduleExec;

@Controller
public class MyAjaxController {
	
	private Logger logger = LoggerFactory.getLogger(ClassName.class);
	       
    
    @Autowired
    private ScheduleExec scheduleExec;   
    
	@RequestMapping(value="/simpleTest.do")
	@ResponseBody
	public void simpleTest(HttpServletRequest req) {
		trace("[simpleTest]=====================start");	
		
		//http://localhost:9080/simpleTest.do?targetH=12&targetM=0&targetS=0
		//http://localhost:9080/simpleTest.do?targetH=03&targetM=0&targetS=0
		
		Object objH = req.getParameter("targetH");
		Object objM = req.getParameter("targetM");
		Object objS = req.getParameter("targetS");
		
		if(!ObjectUtils.isEmpty(objH)
				&& !ObjectUtils.isEmpty(objM)
				&& !ObjectUtils.isEmpty(objS)) {
			
			String sH = objH.toString();
			String sM = objM.toString();
			String sS = objS.toString();
			
			logger.debug("sH===============>{}", sH);		
			logger.debug("sM===============>{}", sM);
			logger.debug("sS===============>{}", sS);
		
			//long delay3 = JobDateUtil.getInitialDelay2(10, 54, 0);
			long delay3 = JobDateUtil.getInitialDelay(Integer.parseInt(sH), Integer.parseInt(sM), Integer.parseInt(sS));
			logger.debug("delay3===========>{}", delay3);
		}
		
//		long delay =JobDateUtil.getInitialDelay(6);
//		logger.debug("delay===========>"+delay);
//		
//		long delay2 =JobDateUtil.getInitialDelay(20);
//		logger.debug("delay2===========>"+delay2);		
//		
//		//date
//		LocalDate localDate = JobDateUtil.getNowDate();
//		logger.debug("localDate===========>{}", localDate);
//		
//		String sLocalDate = JobDateUtil.formatDate(localDate);
//		logger.debug("sLocalDate===========>{}", sLocalDate);
//		
//		String sLocalDate2 = JobDateUtil.formatDate2(localDate);
//		logger.debug("sLocalDate2===========>{}", sLocalDate2);
//		
//		//plus
//		LocalDate nextDate = localDate.plusDays(1);
//		logger.debug("nextDate===========>{}", nextDate);
//		
//		
//		//현재시간
//		LocalDateTime ldtNow = JobDateUtil.getNowTime();
//		logger.debug("ldtNow===========>{}", ldtNow);		
//		
//		String sLdtNow = JobDateUtil.formatDateTime2(ldtNow);
//		logger.debug("sLdtNow===========>{}", sLdtNow);
//		
//		LocalDateTime nextTime = ldtNow.plusHours(1);
//		logger.debug("nextTime===========>{}", nextTime);
//		
//		//오늘 + 특정시간
//		//String sDateTime = sLocalDate + "063000"; 
//		String sToday = JobDateUtil.getToday();
//		logger.debug("sToday===========>{}", sToday);
//		
//		String sDateTime = JobDateUtil.getToday() + "063000"; 
//		LocalDateTime ldt = JobDateUtil.parseDateTime(sDateTime);
//		logger.debug("ldt===========>{}", ldt);
//		
//		String sLdt = JobDateUtil.formatDateTime(ldt);
//		logger.debug("sLdt===========>{}", sLdt);
//		
//		String sLdt2 = JobDateUtil.formatDateTime2(ldt);
//		logger.debug("sLdt2===========>{}", sLdt2);
	
	}	    
    
	@RequestMapping(value="/pauseDeleteJob.do")
	@ResponseBody
	public void pauseDeleteJob(HttpServletRequest req) {
		trace("[pauseDeleteJob]=====================start");	
		//http://localhost:9080/pauseDeleteJob.do?jobKey=job00401
		//http://localhost:9080/pauseDeleteJob.do?jobKey=job00402
		
		Object obj = req.getParameter("jobKey");
		
		if(!ObjectUtils.isEmpty(obj)) {
			String jobKey = obj.toString();
			
			logger.debug("jobKey===============>{}", jobKey);

			scheduleExec.pauseDeleteJob(jobKey);			
		}
	}	
	
	@RequestMapping(value="/rescheduleJob.do")
	@ResponseBody
	public void rescheduleJob(HttpServletRequest req) {
		trace("[rescheduleJob]=====================start");	
		//http://localhost:9080/rescheduleJob.do?jobKey=job00401
		//http://localhost:9080/rescheduleJob.do?jobKey=job00402
		
		Object obj = req.getParameter("jobKey");
		
		if(!ObjectUtils.isEmpty(obj)) {
			String jobKey = obj.toString();
			
			logger.debug("jobKey===============>{}", jobKey);

			scheduleExec.rescheduleJob(jobKey);			
		}
	}	
	
	@RequestMapping(value="/shutdownScheduler.do")
	@ResponseBody
	public void shutdownScheduler(HttpServletRequest req) {
		trace("[shutdownScheduler]=====================start");	
		//http://localhost:9080/shutdownScheduler.do?isNow=true
		//http://localhost:9080/shutdownScheduler.do?isNow=false
		
		Object obj = req.getParameter("isNow");
		
		if(!ObjectUtils.isEmpty(obj)) {
			boolean isNow = Boolean.parseBoolean(obj.toString());
			
			logger.debug("isNow===============>{}", isNow);

			scheduleExec.shutdownScheduler(isNow);			
		}else {
			scheduleExec.shutdownScheduler(true);
		}
	}		
	

	
	@RequestMapping(value="/changeCron.do")
	@ResponseBody
	public void changeCron() {
		trace("[changeCron]=====================start");	
		
		//http://localhost:9080/changeCron.do
			
		
//		try {
//			Thread.sleep(10000);
//			//schedulerFutureService.changeCron("*/5 * * * * *");//5초 주기로 변경
//			//schedulerFuture2Service.reScheduleJob(1000*5);//5초 주기로 변경
//			Thread.sleep(20000);
//			
//		} catch (InterruptedException e) {
//			e.printStackTrace();
//		}
	
	}
	

	

	// 요청 매핑 어트리뷰트
	@RequestMapping(value = "/hello.do")
	@ResponseBody
	public String helloworld(Model model) {
	
		ExecutorService execTP = Executors.newCachedThreadPool();

		// 오래걸리는업무 (스레드)
		execTP.submit(() -> {
			trace("[오래걸리는업무]=====================start");
			try {
				Thread.sleep(5000); //5초
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			trace("[오래걸리는업무]=======================end");
		});

		// 작업2
		trace("[간단업무]=====================start"); 
		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		trace("[간단업무]=====================end");

		execTP.shutdown();
		
//		HashMap<String,Object> mapResult = new HashMap<String, Object>();
//		mapResult.put("RTN_CODE", "0");
//		mapResult.put("RTN_MSG", "성공적으로 처리되었습니다.");

		// 결과는 hello world
		return "hello world react call"; //Json return
		
	}
	
	// 요청 매핑 어트리뷰트
	@RequestMapping(value = "/helloJson.do")
	@ResponseBody
	public ModelAndView helloJson(Model model) {
		trace("[helloJson.do]=====================start");
		
		ModelAndView mv = new ModelAndView("/helloJson");
	
		ExecutorService execTP = Executors.newCachedThreadPool();

		// 오래걸리는업무 (스레드)
		execTP.submit(() -> {
			trace("[오래걸리는업무]=====================start");
			try {
				Thread.sleep(5000); //5초
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			trace("[오래걸리는업무]=======================end");
		});

		// 작업2
		trace("[간단업무]=====================start");
		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		trace("[간단업무]=====================end");

		execTP.shutdown();
		
		HashMap<String,Object> mapResult = new HashMap<String, Object>();
		mapResult.put("RTN_CODE", "0");
		mapResult.put("RTN_MSG", "성공적으로 처리되었습니다.");
		
		logger.debug("mapResult========={}", mapResult);
		
		mv.addObject("mapResult", mapResult);
		mv.addObject("msg", "테스트입니다");

		// 결과는 hello world
		//return "hello world react call"; //Json return
		
		return mv;
		
	}	

	// 출력을 어떤 스레드에서 하고 있는지 확인
	private static void trace(String strLog) {
		System.out.println(Thread.currentThread().getName() + ">>>>" + strLog);
	}
}

 

package com.twokimss.web.batch.service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

import org.apache.ibatis.javassist.bytecode.stackmap.TypeData.ClassName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class JobDateUtil {
	private static Logger logger = LoggerFactory.getLogger(ClassName.class);
	
	public static final DateTimeFormatter ymd = DateTimeFormatter.ofPattern("yyyyMMdd");
	public static final DateTimeFormatter ymdHms = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
	
	
	public static final DateTimeFormatter ymd2 = DateTimeFormatter.ofPattern("yyyy.MM.dd");
	public static final DateTimeFormatter ymdHms2 = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss");
	
	public static long getInitialDelay(int targetH, int targetM, int targetS) {
		long initialDelay = 0;
		
		LocalDate nowDate = LocalDate.now();
		LocalDateTime nowDateTime = LocalDateTime.now();
		
		int nowY = nowDate.getYear();
		int nowM = nowDate.getMonthValue();
		int nowD = nowDate.getDayOfMonth();
		
		System.out.println("nowY==========>"+nowY);
		System.out.println("nowM==========>"+nowM);
		System.out.println("nowD==========>"+nowD);
		
		
		LocalDateTime compareDateTime = LocalDateTime.of(nowY, nowM, nowD, targetH, targetM, targetS);
		LocalDateTime targetDateTime = null;
		
		if(nowDateTime.isBefore(compareDateTime)) { 
			System.out.println("현재시간은 목표시간보다 이전");
			targetDateTime = compareDateTime;
			
		}else {
			System.out.println("현재시간은 목표시간보다 이후");
			targetDateTime = compareDateTime.plusDays(1);
			
		}
		logger.debug("targetDateTime=========================>{}", targetDateTime);
		
		initialDelay = ChronoUnit.MILLIS.between(nowDateTime, targetDateTime);
		//initialDelay = ChronoUnit.SECONDS.between(nowDateTime, targetDateTime);
		//initialDelay = ChronoUnit.MINUTES.between(nowDateTime, targetDateTime);
		
	
		
		logger.debug("initialDelay===============>[{}]", initialDelay);
		
		return initialDelay;
	}
	
	public static long getDelay(long aNum, String type) {
		long delay = 0;
		
		switch (type) {
		case JobConst.DELAY_TYPE_DAY:
			delay = aNum * 24 * 60 * 60 * 1000;
			break;
		case JobConst.DELAY_TYPE_HH:
			delay = aNum * 60 * 60 * 1000;
			break;
		case JobConst.DELAY_TYPE_MI:
			delay = aNum * 60 * 1000;
			break;
		default:
			break;
		}
		
		return delay;
	}

	
	public static String getDateTime() {
		
//		SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
//		Date nowDate = new Date();
//		String strDate = sdf.format(nowDate);		
		
		return formatDateTime2(getNowTime());
		
	}
	
	public static String getToday() {
		return formatDate(getNowDate());
	}

	/**
	 * date
	 * @param type
	 * @return
	 */
	public static LocalDate getNowDate() {
		
		return LocalDate.now(ZoneId.of("Asia/Seoul"));
	}
	
	public static String formatDate(LocalDate date) {
		return ymd.format(date);
	}
	
	public static String formatDate2(LocalDate date) {
		return ymd2.format(date);
	}
	
	public static LocalDate parseDate(String strYmd) {
		return LocalDate.parse(strYmd, ymd);
	}
	
	
	/**
	 * date time
	 * @param type
	 * @return
	 */	
	
	public static LocalDateTime getNowTime() {
		return LocalDateTime.now(ZoneId.of("Asia/Seoul"));		
	}	

	public static String formatDateTime(LocalDateTime datetime) {
		return ymdHms.format(datetime);
	}
	
	public static String formatDateTime2(LocalDateTime datetime) {
		return ymdHms2.format(datetime);
	}	
	
	public static LocalDateTime parseDateTime(String strYmdHms) {
		return LocalDateTime.parse(strYmdHms, ymdHms);
	}
	


}