/* ==================================================================   
 * Created [2016-06-22] by Jon.King 
 * ==================================================================  
 * TSS 
 * ================================================================== 
 * mailTo:[email protected]
 * Copyright (c) boubei.com, 2015-2018 
 * ================================================================== 
 */

package com.boubei.tss.modules.timer;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;
import org.quartz.CronScheduleBuilder;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;

import com.boubei.tss.PX;
import com.boubei.tss.framework.Config;
import com.boubei.tss.framework.Global;
import com.boubei.tss.framework.exception.BusinessException;
import com.boubei.tss.util.BeanUtil;

/** 
 * 定时器调度。
 * 
 * 新增或删除一个job失败,不影响其它job的生成和删除。
 * 
 */
@SuppressWarnings("unchecked")
public class SchedulerBean {
    
	protected Logger log = Logger.getLogger(this.getClass());
	
    private Scheduler scheduler;
    private Map<String, JobDef> defsMap;
    
    private static SchedulerBean sb;
    public  static SchedulerBean getInstanse() {
    	if( sb == null) {
    		sb = new SchedulerBean();
    	}
    	return sb;
    }
    
    SchedulerBean() {
    	// 根据配置决定是否启用定时Job
    	if( Config.TRUE.equals(Config.getAttribute(PX.ENABLE_JOB)) ) {

    		log.info("SchedulerBean is starting....." );
	    	
	    	defsMap = new HashMap<String, JobDef>();
	    	try {
				scheduler = StdSchedulerFactory.getDefaultScheduler();
				scheduler.start();
			} catch (SchedulerException e) {
	            throw new BusinessException("start SchedulerBean error", e);
	        } 
	    	
	    	refresh(true);
    	}
    }
    
    public void refresh(boolean init) {
    	if(scheduler == null) return; // job_enable = false
    	
    	log.debug("SchedulerBean refresh begin...");
    	
		List<JobDef> list = (List<JobDef>) Global.getCommonService().getList(" from JobDef ");
    	
        List<String> jobCodes = new ArrayList<String>();
		for(JobDef def : list) {
			
			String code  = def.getCode();
			String jobName = code;
			jobCodes.add( code );
			
			// 如果已经生成且没做过修过(包括没被停用),则不变
			if( def.equals(defsMap.get(code)) ) { 
				continue; 
			} 
			// 如果已经存在,且value发生了变化,则先删除旧的Job,重新生成新的Job
			else if(defsMap.containsKey(code)) {
				deleteJob(code); 
			}
			
			if( def.isDisabled() ) {
				continue; // 停用的JOB剔除后无需再生成
			}
			
			// 新增或修过过的定时配置,需要重新生成定时Job
			try {
				Class<Job> jobClazz = (Class<Job>) BeanUtil.createClassByName(def.getJobClassName());
				JobDetail aJob = JobBuilder.newJob(jobClazz)
						.withIdentity(jobName)
						.usingJobData(jobName, def.getCustomizeInfo())
						.usingJobData(jobName + "-ID", def.getId())
						.build();
				
				String ts = def.getTimeStrategy(); // 定时策略
				Trigger trigger = TriggerBuilder.newTrigger().withSchedule( CronScheduleBuilder.cronSchedule(ts) ).build(); 
				scheduler.scheduleJob(aJob, trigger);
				
				log.info(" scheduler.scheduleJob: " + jobName + " successed. timeStrategy=" + ts );
				
				JobDef copy = new JobDef();
				BeanUtil.copy(copy, def);
				defsMap.put(code, copy);
			} 
			catch (Exception e) {
				log.error("init Job[" + jobName + "] failed, config = " + def, e);
			}  
		}
		
		Set<String> deleteJobCodes = new HashSet<String>(defsMap.keySet());
		deleteJobCodes.removeAll(jobCodes);
		for(String code : deleteJobCodes) {
			deleteJob(code); // 停用/删除的定时配置
		}
        
        log.debug("SchedulerBean init end.");
    }
    
    private void deleteJob(String jobName) {
    	try {
			JobKey key = new JobKey(jobName);
			scheduler.deleteJob(key);
			
			defsMap.remove(jobName);
			log.info(" scheduler.deleteJob: " + jobName + " successed." );
		} 
		catch (SchedulerException e) {
			log.error("remove Job[" + jobName + "] failed", e);
		}
    }
}