All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.mosip.registration.service.config.impl.JobConfigurationServiceImpl Maven / Gradle / Ivy

package io.mosip.registration.service.config.impl;

import java.sql.Timestamp;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Properties;
import java.util.WeakHashMap;
import java.util.stream.Collectors;

import org.quartz.CronExpression;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.JobListener;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;

import com.cronutils.model.CronType;
import com.cronutils.model.definition.CronDefinitionBuilder;
import com.cronutils.model.time.ExecutionTime;
import com.cronutils.parser.CronParser;

import io.mosip.kernel.core.exception.ExceptionUtils;
import io.mosip.kernel.core.logger.spi.Logger;
import io.mosip.kernel.core.util.DateUtils;
import io.mosip.registration.config.AppConfig;
import io.mosip.registration.constants.LoggerConstants;
import io.mosip.registration.constants.RegistrationConstants;
import io.mosip.registration.dao.SyncJobConfigDAO;
import io.mosip.registration.dao.SyncJobControlDAO;
import io.mosip.registration.dao.SyncTransactionDAO;
import io.mosip.registration.dto.ResponseDTO;
import io.mosip.registration.dto.SyncDataProcessDTO;
import io.mosip.registration.entity.SyncControl;
import io.mosip.registration.entity.SyncJobDef;
import io.mosip.registration.entity.SyncTransaction;
import io.mosip.registration.jobs.BaseJob;
import io.mosip.registration.jobs.JobProcessListener;
import io.mosip.registration.jobs.JobTriggerListener;
import io.mosip.registration.repositories.SyncJobDefRepository;
import io.mosip.registration.service.BaseService;
import io.mosip.registration.service.config.JobConfigurationService;
import io.mosip.registration.service.config.LocalConfigService;

/**
 * implementation class of {@link JobConfigurationService}
 * 
 * @author YASWANTH S
 *
 */
@Service
public class JobConfigurationServiceImpl extends BaseService implements JobConfigurationService {

	/**
	 * To Fetch Job Configuration details
	 */
	@Autowired
	private SyncJobConfigDAO jobConfigDAO;

	@Autowired
	private SyncJobDefRepository syncJobDefRepository;

	/**
	 * Scheduler factory bean which will take Job and Trigger details and run jobs
	 * implicitly
	 */
	private SchedulerFactoryBean schedulerFactoryBean;

	/**
	 * To get Sync Transactions
	 */
	@Autowired
	private SyncTransactionDAO syncJobTransactionDAO;

	/**
	 * To get last completed transactions
	 */
	@Autowired
	private SyncJobControlDAO syncJobDAO;

	/**
	 * LOGGER for logging
	 */
	private static final Logger LOGGER = AppConfig.getLogger(JobConfigurationServiceImpl.class);

	/**
	 * Active sync job map with key as jobID and value as SyncJob (Entity)
	 */
	private Map syncActiveJobMap = new HashMap<>();

	/**
	 * Sync job map with key as jobID and value as SyncJob (Entity)
	 */
	private Map syncJobMap = new HashMap<>();

	private boolean isSchedulerRunning = false;

	/**
	 * To send it in job detail as Base job needs application context
	 */
	@Autowired
	private ApplicationContext applicationContext;

	@Autowired
	private LocalConfigService localConfigService;
	
	public ApplicationContext getApplicationContext() {
		return applicationContext;
	}

	/**
	 * To load in JobDetail
	 */
	private JobDataMap jobDataMap = null;

	/**
	 * Base Job
	 */
	private BaseJob baseJob;

	private List restartableJobList;

	/**
	 * create a parser based on provided definition
	 */
	private static CronParser cronParser = new CronParser(CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ));

	@Autowired
	private JobTriggerListener commonTriggerListener;
	@Autowired
	private JobProcessListener jobProcessListener;

	private List offlineJobs;

	private List unTaggedJobs;

	private static Map parentJobMap = new HashMap<>();

	/**
	 * Active sync job map with key as jobID and value as SyncJob (Entity)
	 */
	private Map syncActiveJobMapExecutable = new HashMap<>();

	public static Map getParentJobMap() {
		return parentJobMap;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see io.mosip.registration.service.JobConfigurationService#initiateJobs()
	 */
	//@PostConstruct -- should be explicitly invoked, currently invoked from pre-loader
	// because, when jobs are triggered before proper sync, it starts failing in sync precondition checks
	public void initiateJobs() {
		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "Jobs initiation was started");

		try {

			offlineJobs = getGlobalConfigValueOf(RegistrationConstants.offlineJobs) != null
					? Arrays.asList(getGlobalConfigValueOf(RegistrationConstants.offlineJobs).split(","))
					: offlineJobs;
			unTaggedJobs = getGlobalConfigValueOf(RegistrationConstants.unTaggedJobs) != null
					? Arrays.asList(getGlobalConfigValueOf(RegistrationConstants.unTaggedJobs).split(","))
					: unTaggedJobs;

			// it contains the list of job id, once this job is successfully completed then
			// application should be restarted to pick the updated config.
			restartableJobList = getGlobalConfigValueOf(RegistrationConstants.restartableJobs) != null
					? Arrays.asList(getGlobalConfigValueOf(RegistrationConstants.restartableJobs).split(","))
					: restartableJobList;

			/* Get All Jobs */
			List jobDefs = getJobs();

			if (!isNull(jobDefs) && !isEmpty(jobDefs)) {

				/* Set Job-map and active sync-job-map */
				setSyncJobMap(jobDefs);

			}

			if (!syncActiveJobMap.isEmpty()) {

				/* Check and Execute missed triggers */
				executeMissedTriggers(syncActiveJobMapExecutable);

				schedulerFactoryBean = getSchedulerFactoryBean(String.valueOf(syncActiveJobMapExecutable.size()));

				startScheduler();

			}

		} catch (RuntimeException runtimeException) {
			LOGGER.error(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
					RegistrationConstants.APPLICATION_ID,
					runtimeException.getMessage() + ExceptionUtils.getStackTrace(runtimeException));

		} catch (Exception exception) {
			LOGGER.error(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
					RegistrationConstants.APPLICATION_ID,
					exception.getMessage() + ExceptionUtils.getStackTrace(exception));

		}

		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "Jobs initiation was completed");

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see io.mosip.registration.service.JobConfigurationService#startJobs(org.
	 * springframework.context.ApplicationContext)
	 */
	public ResponseDTO startScheduler() {
		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "start jobs invocation started");

		ResponseDTO responseDTO = new ResponseDTO();

		/* Check Whether Scheduler is running or not */
		if (isSchedulerRunning()) {
			return setErrorResponse(responseDTO, RegistrationConstants.SYNC_DATA_PROCESS_ALREADY_STARTED, null);
		} else if (RegistrationConstants.ENABLE.equalsIgnoreCase(
				getGlobalConfigValueOf(RegistrationConstants.IS_REGISTRATION_JOBS_SCHEDULER_ENABLED))) {

			try {
				schedulerFactoryBean.start();
				isSchedulerRunning = true;

				/* Job Data Map */
				Map jobDataAsMap = new WeakHashMap<>();
				jobDataAsMap.put("applicationContext", applicationContext);
				jobDataAsMap.putAll(syncJobMap);

				jobDataMap = new JobDataMap(jobDataAsMap);

				loadScheduler(responseDTO);
			} catch (RuntimeException runtimeException) {
				LOGGER.error(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, runtimeException);
				setErrorResponse(responseDTO, RegistrationConstants.START_SCHEDULER_ERROR_MESSAGE, null);
			}

		} else {

			// Configuration was disabled to run the jobs scheduler
			return setErrorResponse(responseDTO, RegistrationConstants.START_SCHEDULER_ERROR_MESSAGE, null);
		}

		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "start jobs invocation ended");

		return responseDTO;

	}

	private void loadScheduler(ResponseDTO responseDTO) {
		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "Loading Scheduler started");

		syncActiveJobMapExecutable.forEach((jobId, syncJob) -> {

			try {
				LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
						RegistrationConstants.APPLICATION_ID, "Checking the Job to load in scheduler : " + jobId);

				if (!isNull(syncJob.getApiName()) && responseDTO.getErrorResponseDTOs() == null && isSchedulerRunning()
						&& !schedulerFactoryBean.getScheduler().checkExists(new JobKey(jobId))) {

					// Get Job instance through application context
					baseJob = (BaseJob) applicationContext.getBean(syncJob.getApiName());

					JobDetail jobDetail = JobBuilder.newJob(baseJob.jobClass()).withIdentity(syncJob.getId())
							.usingJobData(jobDataMap).build();

					CronTrigger trigger = (CronTrigger) TriggerBuilder.newTrigger().forJob(jobDetail)
							.withIdentity(syncJob.getId())
							.withSchedule(CronScheduleBuilder.cronSchedule(getSyncFrequency(syncJob))).build();

					schedulerFactoryBean.getScheduler().scheduleJob(jobDetail, trigger);

					LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
							RegistrationConstants.APPLICATION_ID, "Scheduler loaded the job :" + jobId);

				}
			} catch (SchedulerException | RuntimeException exception) {
				LOGGER.error(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, exception);

				/* Stop, Clear Scheduler and set Error response */
				setStartExceptionError(responseDTO);

			}

			if (isSchedulerRunning()) {
				setSuccessResponse(responseDTO, RegistrationConstants.BATCH_JOB_START_SUCCESS_MESSAGE, null);
			}

		}

		);

		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "Loading Scheduler completed");

	}

	private void setStartExceptionError(ResponseDTO responseDTO) {

		try {
			/* Clear Scheduler */
			clearScheduler();

		} catch (SchedulerException schedulerException) {
			LOGGER.error(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
					RegistrationConstants.APPLICATION_ID,
					schedulerException.getMessage() + ExceptionUtils.getStackTrace(schedulerException));
		}

		/* Error Response */
		setErrorResponse(responseDTO, RegistrationConstants.START_SCHEDULER_ERROR_MESSAGE, null);

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * io.mosip.registration.service.config.JobConfigurationService#stopScheduler()
	 */
	public ResponseDTO stopScheduler() {
		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "stop jobs invocation started");

		ResponseDTO responseDTO = new ResponseDTO();

		try {
			if (schedulerFactoryBean.isRunning()) {

				/* Clear and Stop Scheduler */
				clearScheduler();

				setSuccessResponse(responseDTO, RegistrationConstants.BATCH_JOB_STOP_SUCCESS_MESSAGE, null);

			} else {
				setErrorResponse(responseDTO, RegistrationConstants.SYNC_DATA_PROCESS_ALREADY_STOPPED, null);

			}
		} catch (RuntimeException | SchedulerException schedulerException) {
			LOGGER.error(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
					RegistrationConstants.APPLICATION_ID,
					schedulerException.getMessage() + ExceptionUtils.getStackTrace(schedulerException));
			setErrorResponse(responseDTO, RegistrationConstants.STOP_SCHEDULER_ERROR_MESSAGE, null);

		}

		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "stop jobs invocation ended");

		return responseDTO;
	}

	private void clearScheduler() throws SchedulerException {

		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "Clearing and stopping the Scheduler");

		/* Clear Scheduler */
		schedulerFactoryBean.getScheduler().clear();
		schedulerFactoryBean.stop();
		isSchedulerRunning = false;

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see io.mosip.registration.service.JobConfigurationService#
	 * getCurrentRunningJobDetails()
	 */
	public ResponseDTO getCurrentRunningJobDetails() {
		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "get current running job details started");

		ResponseDTO responseDTO = new ResponseDTO();

		try {

			if (schedulerFactoryBean != null && isSchedulerRunning()) {
				// Get currently executing jobs from scheduler factory
				List executingJobList = schedulerFactoryBean.getScheduler()
						.getCurrentlyExecutingJobs();

				if (isNull(executingJobList) || isEmpty(executingJobList)) {
					setErrorResponse(responseDTO, RegistrationConstants.NO_JOBS_RUNNING, null);
				} else {
					List dataProcessDTOs = executingJobList.stream().map(jobExecutionContext -> {

						SyncJobDef syncJobDef = syncJobMap.get(jobExecutionContext.getJobDetail().getKey().getName());

						return constructDTO(syncJobDef.getId(), syncJobDef.getName(), RegistrationConstants.JOB_RUNNING,
								Timestamp.valueOf(DateUtils.getUTCCurrentDateTime()).toString());

					}).collect(Collectors.toList());

					setResponseDTO(dataProcessDTOs, responseDTO, null, RegistrationConstants.NO_JOBS_RUNNING);

				}
			} else {
				setErrorResponse(responseDTO, RegistrationConstants.NO_JOBS_RUNNING, null);
			}

		} catch (SchedulerException schedulerException) {
			LOGGER.error(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
					RegistrationConstants.APPLICATION_ID,
					schedulerException.getMessage() + ExceptionUtils.getStackTrace(schedulerException));

			setErrorResponse(responseDTO, RegistrationConstants.CURRENT_JOB_DETAILS_ERROR_MESSAGE, null);

		}

		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "get current running job details ended");

		return responseDTO;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * io.mosip.registration.service.config.JobConfigurationService#executeJob(java.
	 * lang.String, java.lang.String)
	 */

	public ResponseDTO executeJob(String jobId, String triggerPoint) {
		ResponseDTO responseDTO = new ResponseDTO();
		if (jobId != null && triggerPoint != null) {
			try {
				SyncJobDef syncJobDef = jobConfigDAO.getSyncJob(jobId);

				if (syncJobDef != null && !isNull(syncJobDef.getApiName()) && syncActiveJobMap.containsKey(jobId) 
						&& syncJobDef.getIsActive()) {
					LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
							RegistrationConstants.APPLICATION_ID, "Execute job started : " + jobId);
					
					// Get Job using application context and api name
					baseJob = (BaseJob) applicationContext.getBean(syncJobDef.getApiName());

					BaseJob.removeCompletedJobInMap(jobId);

					baseJob.setApplicationContext(applicationContext);

					// Job Invocation
					responseDTO = baseJob.executeJob(triggerPoint, jobId);

				} else {
					setErrorResponse(responseDTO, RegistrationConstants.EXECUTE_JOB_ERROR_MESSAGE, null);
				}
				return responseDTO;

			} catch (RuntimeException runtimeException) {
				LOGGER.error(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
						RegistrationConstants.APPLICATION_ID,
						runtimeException.getMessage() + ExceptionUtils.getStackTrace(runtimeException));

				setErrorResponse(responseDTO, RegistrationConstants.EXECUTE_JOB_ERROR_MESSAGE, null);
			}
		} else {

			LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
					RegistrationConstants.APPLICATION_ID,
					"Unable to execute job as job id or trigger point value was null");

			setErrorResponse(responseDTO, RegistrationConstants.EXECUTE_JOB_ERROR_MESSAGE, null);

		}

		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "Execute job ended : " + jobId);

		return responseDTO;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see io.mosip.registration.service.config.JobConfigurationService#
	 * getLastCompletedSyncJobs()
	 */
	@Override
	public ResponseDTO getLastCompletedSyncJobs() {

		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "get Last Completed Jobs Started");

		ResponseDTO responseDTO = new ResponseDTO();

		/* Fetch Sync control records */
		List syncControls = syncJobDAO.findAll();

		if (!isNull(syncControls) && !isEmpty(syncControls)) {
			List syncDataProcessDTOs = syncControls.stream().map(syncControl -> {

				String jobName = (syncJobMap.get(syncControl.getSyncJobId()) == null)
						? RegistrationConstants.JOB_UNKNOWN
						: syncJobMap.get(syncControl.getSyncJobId()).getName();

				String lastUpdTimes = (syncControl.getUpdDtimes() == null) ? syncControl.getCrDtime().toString()
						: syncControl.getUpdDtimes().toString();

				return constructDTO(syncControl.getSyncJobId(), jobName, RegistrationConstants.JOB_COMPLETED,
						lastUpdTimes);

			}).collect(Collectors.toList());

			setResponseDTO(syncDataProcessDTOs, responseDTO, null, RegistrationConstants.NO_JOB_COMPLETED);

		} else {
			setErrorResponse(responseDTO, RegistrationConstants.NO_JOB_COMPLETED, null);
		}

		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "get Last Completed Jobs Ended");

		return responseDTO;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see io.mosip.registration.service.config.JobConfigurationService#
	 * getSyncJobsTransaction()
	 */
	@Override
	public ResponseDTO getSyncJobsTransaction() {

		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "get Sync Transaction Started");

		ResponseDTO responseDTO = new ResponseDTO();

		String val = getGlobalConfigValueOf(RegistrationConstants.SYNC_TRANSACTION_NO_OF_DAYS_LIMIT);

		if (val != null) {
			int syncTransactionConfiguredDays = Integer.parseInt(val);

			/* Get Calendar instance */
			Calendar cal = Calendar.getInstance();
			cal.setTime(Timestamp.valueOf(DateUtils.getUTCCurrentDateTime()));
			cal.add(Calendar.DATE, -syncTransactionConfiguredDays);

			/* To-Date */
			Timestamp req = new Timestamp(cal.getTimeInMillis());

			/* Get All sync Transaction Details from DataBase */
			List syncTransactionList = syncJobTransactionDAO.getSyncTransactions(req,
					RegistrationConstants.JOB_TRIGGER_POINT_USER);

			if (!isNull(syncTransactionList) && !isEmpty(syncTransactionList)) {

				List syncDataProcessDTOs = syncTransactionList.stream().map(syncTransaction -> {

					String jobName = (syncJobMap.get(syncTransaction.getSyncJobId()) == null)
							? RegistrationConstants.JOB_UNKNOWN
							: syncJobMap.get(syncTransaction.getSyncJobId()).getName();

					return constructDTO(syncTransaction.getSyncJobId(), jobName, syncTransaction.getStatusCode(),
							syncTransaction.getCrDtime().toString());

				}).collect(Collectors.toList());

				setResponseDTO(syncDataProcessDTOs, responseDTO, null, RegistrationConstants.NO_JOBS_TRANSACTION);

			} else {
				setErrorResponse(responseDTO, RegistrationConstants.NO_JOBS_TRANSACTION, null);
			}
		}

		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "get Sync Transaction Ended");

		return responseDTO;
	}

	private SyncDataProcessDTO constructDTO(String jobId, String jobName, String statusCode, String crDtimes) {
		/* create new Sync Data Process DTO */
		return new SyncDataProcessDTO(jobId, jobName, statusCode, crDtimes);

	}

	private void setResponseDTO(List syncDataProcessDTOs, ResponseDTO responseDTO,
			String successMsg, String errorMsg) {

		/* Set Response DTO with Error or Success result */

		if (isNull(syncDataProcessDTOs) || isEmpty(syncDataProcessDTOs)) {
			setErrorResponse(responseDTO, errorMsg, null);

		} else {
			Map attributes = new WeakHashMap<>();
			attributes.put(RegistrationConstants.SYNC_DATA_DTO, syncDataProcessDTOs);

			setSuccessResponse(responseDTO, successMsg, attributes);
		}
	}

	private List getJobs() {

		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "Fetching sync_job_defs from db");
		return jobConfigDAO.getAll();
	}

	private void setSyncJobMap(List syncJobDefs) {
		syncJobDefs.forEach(syncJob -> {

			/* All Jobs */
			syncJobMap.put(syncJob.getId(), syncJob);

			/* Active Jobs Map */
			if (syncJob.getIsActive()) {
				syncActiveJobMap.put(syncJob.getId(), syncJob);
			}

		});

		syncActiveJobMapExecutable.putAll(syncActiveJobMap); 

		syncActiveJobMap.forEach((jobID, syncJobDef) -> {
			if (!isNull(syncJobDef.getParentSyncJobId())) {
				if (syncActiveJobMap.get(syncJobDef.getParentSyncJobId()) == null) {
					syncActiveJobMapExecutable.remove(jobID);
				} else {
					parentJobMap.put(jobID, syncActiveJobMap.get(syncJobDef.getParentSyncJobId()));
					syncActiveJobMapExecutable.remove(syncJobDef.getParentSyncJobId());
				}
			}
		});
	}

	private void executeMissedTrigger(final String jobId, final String syncFrequency) {

		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "Started Checking whether Trigger missed for  : " + jobId);

		ExecutionTime executionTime = getExecutionTime(syncFrequency);

		Instant last = getLast(executionTime);
		Instant next = getNext(executionTime);

		/* Check last and next has values present */
		if (last != null && next != null) {

			/* Get all Transactions in between last and next crDtimes */
			List syncTransactions = syncJobTransactionDAO.getAll(jobId, Timestamp.from(last),
					Timestamp.from(next));

			LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
					RegistrationConstants.APPLICATION_ID, "Completed Checking whether Trigger missed for  : " + jobId);

			/* Execute the Job if it was not started on previous pre-scheduled time */
			if ((isNull(syncTransactions) || isEmpty(syncTransactions))) {
				executeJob(jobId, RegistrationConstants.JOB_TRIGGER_POINT_SYSTEM);

			}
		}

	}

	private Instant getLast(ExecutionTime executionTime) {
		ZonedDateTime currentTime = ZonedDateTime.now(ZoneId.of("UTC"));

		Optional lastDate = executionTime.lastExecution(currentTime);
		Instant last = null;
		if (lastDate.isPresent()) {
			last = lastDate.get().toInstant();

		}

		return last;

	}

	private Instant getNext(ExecutionTime executionTime) {
		ZonedDateTime currentTime = ZonedDateTime.now(ZoneId.of("UTC"));

		Optional nextDate = executionTime.nextExecution(currentTime);
		Instant next = null;
		if (nextDate.isPresent()) {
			next = nextDate.get().toInstant();

		}

		return next;

	}

	private ExecutionTime getExecutionTime(String syncFrequency) {
		return ExecutionTime.forCron(cronParser.parse(syncFrequency));
	}

	private void executeMissedTriggers(Map map) {

		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "Started invoking Missed Trigger Jobs");

		map.forEach((jobId, syncJob) -> {
			String syncFrequency = getSyncFrequency(syncJob);
			if (!isNull(syncFrequency) && !isNull(syncJob.getApiName())) {
				/* An A-sync task to complete missed trigger */
				new Thread(() -> executeMissedTrigger(jobId, syncFrequency)).start();
			}

		});

		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "Completed invoking Missed Trigger Jobs");

	}

	private String getSyncFrequency(SyncJobDef syncJob) {
		String localPreference = localConfigService.getValue(syncJob.getId());
		return (localPreference == null || localPreference.isBlank()) ? syncJob.getSyncFreq() : localPreference;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * io.mosip.registration.service.config.JobConfigurationService#executeAllJobs()
	 */
	@Override
	public ResponseDTO executeAllJobs() {

		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "Started execute all jobs");
		ResponseDTO responseDTO = new ResponseDTO();
		BaseJob.successJob.clear();
		BaseJob.clearCompletedJobMap();
		List failureJobs = new LinkedList<>();
		Map attributes = null;

		for (Entry syncJob : syncActiveJobMapExecutable.entrySet()) {
			LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
					RegistrationConstants.APPLICATION_ID, "Validating job to execute : " + syncJob.getKey());

			if ((offlineJobs == null || !offlineJobs.contains(syncJob.getKey())
					&& (unTaggedJobs == null || !unTaggedJobs.contains(syncJob.getKey())))
					&& !isNull(syncJob.getValue().getApiName())) {

				String triggerPoint = getUserIdFromSession().equals(RegistrationConstants.JOB_TRIGGER_POINT_SYSTEM)
						? RegistrationConstants.JOB_TRIGGER_POINT_SYSTEM
						: RegistrationConstants.JOB_TRIGGER_POINT_USER;

				ResponseDTO response = executeJob(syncJob.getKey(), triggerPoint);
				if (response != null && response.getSuccessResponseDTO() != null && response.getSuccessResponseDTO().getOtherAttributes() != null 
						&& response.getSuccessResponseDTO().getOtherAttributes().containsKey(RegistrationConstants.ROLES_MODIFIED)) {
					attributes = response.getSuccessResponseDTO().getOtherAttributes();
				}
			}
		}

		/* Child Job's check */
		BaseJob.getCompletedJobMap().forEach((jobId, status) -> {
			if (!status.toLowerCase().contains(RegistrationConstants.JOB_EXECUTION_SUCCESS.toLowerCase())) {
				failureJobs.add(syncActiveJobMap.get(jobId).getName());
			}
		});

		if (!isEmpty(failureJobs)) {
			setErrorResponse(responseDTO, failureJobs.toString().replace("[", "").replace("]", ""), attributes);
		} else {
			setSuccessResponse(responseDTO, RegistrationConstants.SUCCESS, attributes);
		}
		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "completed execute all jobs");

		return responseDTO;

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see io.mosip.registration.service.config.JobConfigurationService#isRestart()
	 */
	@Override
	public ResponseDTO isRestart() {

		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "Checking for is to be re-start started");

		ResponseDTO responseDTO = new ResponseDTO();
		/* Fetch completed job map */
		Map completedSyncJobMap = BaseJob.getCompletedJobMap();

		/* Compare with restart-able job list */
		for (String jobId : restartableJobList) {

			/* Check the job completed with success/failure */
			if (completedSyncJobMap.get(jobId) != null && completedSyncJobMap.get(jobId).contains(RegistrationConstants.RESTART)) {

				/* Store job info in attributes of response */
				Map successJobAttribute = new WeakHashMap<>();
				successJobAttribute.put(RegistrationConstants.JOB_ID, jobId);

				return setSuccessResponse(responseDTO,
						syncActiveJobMap.get(jobId).getName() + " " + RegistrationConstants.OTP_VALIDATION_SUCCESS,
						successJobAttribute);
			}
		}
		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "Checking for is to be re-start completed");
		return responseDTO;
	}

	@Override
	public ResponseDTO getRestartTime() {

		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "Getting Re-start time started");

		ResponseDTO responseDTO = new ResponseDTO();

		String syncDataFreq = io.mosip.registration.context.ApplicationContext.getStringValueFromApplicationMap(RegistrationConstants.SYNC_DATA_FREQ);
		if (syncDataFreq != null) {
			ExecutionTime executionTime = getExecutionTime(syncDataFreq);
			Instant last = getLast(executionTime);
			Instant next = getNext(executionTime);

			if (last != null && next != null) {
				setSuccessResponse(responseDTO, String.valueOf((Duration.between(last, next).toMillis()) / 5), null);
			}
		}
		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "Getting Re-start time completed");

		return responseDTO;
	}
	
	@Override
	public String getNextRestartTime(String syncFrequency) {
		ExecutionTime executionTime = getExecutionTime(syncFrequency);
		Instant next = getNext(executionTime);
		return next != null ? next.atOffset(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) : null;
	}

	/**
	 * scheduler factory bean used to schedule the batch jobs
	 * 
	 * @return scheduler factory which includes job detail and trigger detail
	 * @throws Exception
	 */
	private SchedulerFactoryBean getSchedulerFactoryBean(String count) throws Exception {

		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "Initializing Scheduler Factory Bean started");

		SchedulerFactoryBean schFactoryBean = new SchedulerFactoryBean();
		schFactoryBean.setGlobalTriggerListeners(new TriggerListener[] { commonTriggerListener });
		schFactoryBean.setGlobalJobListeners(new JobListener[] { jobProcessListener });
		Properties quartzProperties = new Properties();
		quartzProperties.put("org.quartz.threadPool.threadCount", count);
		schFactoryBean.setQuartzProperties(quartzProperties);
		schFactoryBean.afterPropertiesSet();

		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "Initializing Scheduler Factory Bean completed");

		return schFactoryBean;
	}

	@Override
	public boolean isSchedulerRunning() {
		return isSchedulerRunning;
	}

	@Override
	public Map getActiveSyncJobMap() {
		return syncActiveJobMap;
	}

	public List getOfflineJobs() {
		return offlineJobs;
	}

	public List getUnTaggedJobs() {
		return unTaggedJobs;
	}

	@Override
	public SyncControl getSyncControlOfJob(String syncJobId) {

		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "Getting Job info in sync control Started");

		SyncControl syncControl = null;
		if (syncJobId != null) {
			syncControl = syncJobDAO.findBySyncJobId(syncJobId);
		}
		LOGGER.info(LoggerConstants.BATCH_JOBS_CONFIG_LOGGER_TITLE, RegistrationConstants.APPLICATION_NAME,
				RegistrationConstants.APPLICATION_ID, "Getting Job info in sync control Completed");

		return syncControl;
	}
	
	@Override
	public boolean isValidCronExpression(String cronExpression) {
		return CronExpression.isValidExpression(cronExpression);
	}

	@Override
	public List getSyncJobs() {
		return syncJobDefRepository.findAllByIsActiveTrue();
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy