package org.akaza.openclinica.controller;
import java.text.SimpleDateFormat;
import java.util.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.akaza.openclinica.bean.extract.ExtractPropertyBean;
import org.akaza.openclinica.i18n.core.LocaleResolver;
import org.akaza.openclinica.i18n.util.ResourceBundleProvider;
import org.akaza.openclinica.service.extract.XsltTriggerService;
import org.akaza.openclinica.web.table.scheduledjobs.ScheduledJobTableFactory;
import org.akaza.openclinica.web.table.scheduledjobs.ScheduledJobs;
import org.akaza.openclinica.web.table.sdv.SDVUtil;
import org.jmesa.facade.TableFacade;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
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.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;
/**
*
* @author jnyayapathi
* Controller for listing all the scheduled jobs. Also an interface for canceling the jobs which are running.
*/
@Controller("ScheduledJobController")
public class ScheduledJobController {
private final static Logger logger = LoggerFactory.getLogger(ScheduledJobController.class);
public final static String SCHEDULED_TABLE_ATTRIBUTE = "scheduledTableAttribute";
@Autowired
@Qualifier("scheduledJobTableFactory")
private ScheduledJobTableFactory scheduledJobTableFactory;
public static final String EP_BEAN = "epBean";
@Autowired
@Qualifier("sdvUtil")
private SDVUtil sdvUtil;
@Autowired
private Scheduler scheduler;
@RequestMapping("/listCurrentScheduledJobs")
public ModelMap listScheduledJobs(HttpServletRequest request, HttpServletResponse response) throws SchedulerException{
Locale locale = LocaleResolver.getLocale(request);
ResourceBundleProvider.updateLocale(locale);
ModelMap gridMap = new ModelMap();
String[] triggerNames;
boolean showMoreLink = false;
if(request.getParameter("showMoreLink")!=null){
showMoreLink = Boolean.parseBoolean(request.getParameter("showMoreLink").toString());
}else{
showMoreLink = true;
}
request.setAttribute("showMoreLink", showMoreLink+"");
// request.setAttribute("studySubjectId",studySubjectId);
/*SubjectIdSDVFactory tableFactory = new SubjectIdSDVFactory();
* @RequestParam("studySubjectId") int studySubjectId,*/
request.setAttribute("imagePathPrefix", "../");
ArrayList<String> pageMessages = (ArrayList<String>) request.getAttribute("pageMessages");
if (pageMessages == null) {
pageMessages = new ArrayList<String>();
}
request.setAttribute("pageMessages", pageMessages);
List<JobExecutionContext> listCurrentJobs = new ArrayList<JobExecutionContext>();
listCurrentJobs = scheduler.getCurrentlyExecutingJobs();
Iterator<JobExecutionContext> itCurrentJobs = listCurrentJobs.iterator();
List<String> currentJobList = new ArrayList<String>();
while(itCurrentJobs.hasNext()){
JobExecutionContext temp = itCurrentJobs.next();
currentJobList.add(temp.getTrigger().getJobKey().getName()+temp.getTrigger().getKey().getGroup());
}
List<String> triggerGroupNames = scheduler.getTriggerGroupNames();
String[] triggerGroups = triggerGroupNames.stream().toArray(String[]::new);
List<SimpleTrigger> simpleTriggers = new ArrayList<SimpleTrigger>();
int index1 =0;
for (String triggerGroup : triggerGroups) {
logger.debug("Group: " + triggerGroup + " contains the following triggers");
Set<TriggerKey> triggerKeys = scheduler.getTriggerKeys(GroupMatcher.triggerGroupEquals(triggerGroup));
triggerNames = triggerKeys.stream().toArray(String[]::new);
for (String triggerName : triggerNames) {
Trigger.TriggerState state = scheduler.getTriggerState(TriggerKey.triggerKey(triggerName, triggerGroup));
logger.debug("- " + triggerName);
if (state != Trigger.TriggerState.PAUSED) {
simpleTriggers.add(index1,(SimpleTrigger) scheduler.getTrigger(TriggerKey.triggerKey(triggerName, triggerGroup)));
index1++;
}
}
}
List <ScheduledJobs>jobsScheduled = new ArrayList<ScheduledJobs>();
int index = 0;
for (SimpleTrigger st : simpleTriggers) {
boolean isExecuting = currentJobList.contains(st.getJobKey().getName() + st.getJobKey().getGroup());
ScheduledJobs jobs = new ScheduledJobs();
ExtractPropertyBean epBean = null;
if (st.getJobDataMap() != null) {
epBean = (ExtractPropertyBean) st.getJobDataMap().get(EP_BEAN);
}
if (epBean != null) {
StringBuilder checkbox = new StringBuilder();
checkbox.append("<input style='margin-right: 5px' type='checkbox'/>");
StringBuilder actions = new StringBuilder("<table><tr><td>");
if (isExecuting) {
actions.append(" ");
} else {
String contextPath = request.getContextPath();
StringBuilder jsCodeString = new StringBuilder("this.form.method='GET'; this.form.action='").
append(contextPath).append("/pages/cancelScheduledJob").append("';").
append("this.form.theJobName.value='").append(st.getJobKey().getName() ).append("';").
append("this.form.theJobGroupName.value='").append(st.getJobKey().getGroup()).append("';").
append("this.form.theTriggerName.value='").append(st.getJobKey().getName()).append("';").
append("this.form.theTriggerGroupName.value='").append(st.getJobKey().getGroup()).append("';").
append("this.form.submit();");
actions.append("<td><input type=\"submit\" class=\"button\" value=\"Cancel Job\" ").
append("name=\"cancelJob\" onclick=\"").append(jsCodeString.toString()).append("\" />");
}
actions.append("</td></tr></table>");
jobs.setCheckbox(checkbox.toString());
jobs.setDatasetId(epBean.getDatasetName());
String fireTime = st.getStartTime() != null ? longFormat(locale).format(st.getStartTime()) : "";
jobs.setFireTime(fireTime);
if(st.getNextFireTime() != null) {
jobs.setScheduledFireTime(longFormat(locale).format(st.getNextFireTime()));
}
jobs.setExportFileName(epBean.getExportFileName()[0]);
jobs.setAction(actions.toString());
jobs.setJobStatus(isExecuting ? "Currently Executing" : "Scheduled");
jobsScheduled.add(index, jobs);
index++;
}
}
logger.debug("totalRows"+index);
request.setAttribute("totalJobs", index);
request.setAttribute("jobs", jobsScheduled);
TableFacade facade = scheduledJobTableFactory.createTable(request, response);
String sdvMatrix = facade.render();
gridMap.addAttribute(SCHEDULED_TABLE_ATTRIBUTE, sdvMatrix);
return gridMap;
}
@RequestMapping("/cancelScheduledJob")
public String cancelScheduledJob(HttpServletRequest request, HttpServletResponse response,
@RequestParam("theJobName") String theJobName,@RequestParam("theJobGroupName") String theJobGroupName,
@RequestParam("theTriggerName") String triggerName,@RequestParam("theTriggerGroupName") String triggerGroupName,
@RequestParam("redirection") String redirection, ModelMap model) throws SchedulerException
{
scheduler.getJobDetail(JobKey.jobKey(theJobName, theJobGroupName));
logger.debug("About to pause the job-->"+theJobName+"Job Group Name -->"+theJobGroupName);
SimpleTrigger oldTrigger = (SimpleTrigger) scheduler.getTrigger(TriggerKey.triggerKey(triggerName, triggerGroupName));
if(oldTrigger!=null)
{
Date startTime = new Date(oldTrigger.getStartTime().getTime()+oldTrigger.getRepeatInterval());
if(triggerGroupName.equals(ExtractController.TRIGGER_GROUP_NAME))
{
interruptQuartzJob(scheduler, theJobName, theJobGroupName);
}
scheduler.pauseJob(JobKey.jobKey(theJobName, theJobGroupName));
SimpleTrigger newTrigger = (SimpleTrigger) newTrigger()
.forJob(theJobName, theJobGroupName)
.startAt(startTime)
.withSchedule(simpleSchedule().withRepeatCount(oldTrigger.getRepeatCount()).withIntervalInSeconds(new Long(oldTrigger.getRepeatInterval()).intValue())
.withMisfireHandlingInstructionNextWithRemainingCount());
newTrigger.getTriggerBuilder().usingJobData(oldTrigger.getJobDataMap());
scheduler.unscheduleJob(TriggerKey.triggerKey(triggerName,triggerGroupName));// these are the jobs which are from extract data and are not not required to be rescheduled.
ArrayList<String> pageMessages = new ArrayList<String>();
if(triggerGroupName.equals(ExtractController.TRIGGER_GROUP_NAME))
{
scheduler.rescheduleJob(TriggerKey.triggerKey(triggerName,triggerGroupName), newTrigger);
pageMessages.add("The Job "+theJobName+" has been cancelled");
}
else if(triggerGroupName.equals(XsltTriggerService.TRIGGER_GROUP_NAME))
{
JobDetailFactoryBean JobDetailFactoryBean = new JobDetailFactoryBean();
JobDetailFactoryBean.setGroup(XsltTriggerService.TRIGGER_GROUP_NAME);
JobDetailFactoryBean.setName(newTrigger.getKey().getName());
JobDetailFactoryBean.setJobClass(org.akaza.openclinica.job.XsltStatefulJob.class);
JobDetailFactoryBean.setJobDataMap(newTrigger.getJobDataMap());
JobDetailFactoryBean.setDurability(true); // need durability?
scheduler.deleteJob(JobKey.jobKey(theJobName, theJobGroupName));
scheduler.scheduleJob(JobDetailFactoryBean.getObject(), newTrigger);
pageMessages.add("The Job "+theJobName+" has been rescheduled");
}
request.setAttribute("pageMessages", pageMessages);
logger.debug("jobDetails>"+ scheduler.getJobDetail(JobKey.jobKey(theJobName, theJobGroupName)));
}
sdvUtil.forwardRequestFromController(request, response, "/pages/" + redirection);
return null;
}
private void interruptQuartzJob(Scheduler scheduler, String jobName, String jobGroup) throws SchedulerException {
scheduler.interrupt(JobKey.jobKey(jobName, jobGroup));
}
private String longFormatString() {
return "EEE MMM dd HH:mm:ss zzz yyyy";
}
private SimpleDateFormat longFormat(Locale locale) {
return new SimpleDateFormat(longFormatString(), locale);
}
}