/**********************************************************************************
* $URL$
* $Id$
**********************************************************************************
*
* Copyright (c) 2003, 2004, 2005, 2006, 2008 The Sakai Foundation
*
* Licensed under the Educational Community License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**********************************************************************************/
package org.sakaiproject.tool.app.scheduler;
import java.text.ParseException;
import java.util.*;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.model.SelectItem;
import javax.faces.validator.ValidatorException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.CronTrigger;
import org.quartz.InterruptableJob;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.JobExecutionContext;
import org.sakaiproject.api.app.scheduler.ConfigurableJobBeanWrapper;
import org.sakaiproject.api.app.scheduler.ConfigurableJobProperty;
import org.sakaiproject.api.app.scheduler.ConfigurableJobPropertyValidationException;
import org.sakaiproject.api.app.scheduler.ConfigurableJobPropertyValidator;
import org.sakaiproject.api.app.scheduler.JobDetailWrapper;
import org.sakaiproject.api.app.scheduler.SchedulerManager;
import org.sakaiproject.api.app.scheduler.TriggerWrapper;
import org.sakaiproject.api.app.scheduler.JobBeanWrapper;
import org.sakaiproject.api.app.scheduler.events.TriggerEventManager;
import org.sakaiproject.component.app.scheduler.JobDetailWrapperImpl;
import org.sakaiproject.component.app.scheduler.TriggerWrapperImpl;
import org.sakaiproject.util.ResourceLoader;
public class SchedulerTool
{
private static final Log LOG = LogFactory.getLog(SchedulerTool.class);
private static final String CRON_CHECK_ASTERISK = "**";
private static final String CRON_CHECK_QUESTION_MARK = "??";
/** The maximum length of a trigger name. */
private static final int TRIGGER_NAME_LENGTH_LIMIT = 80;
/** The maximum length of a job name. */
private static final int JOB_NAME_LENGTH_LIMIT = 80;
private SchedulerManager schedulerManager;
private String jobName;
private String triggerName;
private String triggerExpression;
private String selectedClass;
private List<JobDetailWrapper> jobDetailWrapperList;
private List<JobDetailWrapper> filteredJobDetailWrapperList;
private JobDetailWrapper selectedJobDetailWrapper;
private boolean isSelectAllJobsSelected = false;
private boolean isSelectAllTriggersSelected = false;
private List<TriggerWrapper> filteredTriggersWrapperList;
private LinkedList<String> configurableJobErrorMessages = null;
private ConfigurableJobBeanWrapper configurableJobBeanWrapper;
private Map<String, String> configurableJobResources;
private List<ConfigurablePropertyWrapper> configurableJobProperties;
private JobDetail jobDetail;
protected ResourceLoader rb = new ResourceLoader("org.sakaiproject.tool.scheduler.bundle.Messages");
private TriggerWrapper triggerWrapper = null;
private TriggerEventManager
triggerEventManager = null;
private EventPager evtPager = new EventPager();
public SchedulerTool()
{
}
public void setTriggerEventManager (TriggerEventManager tem)
{
triggerEventManager = tem;
evtPager.setTriggerEventManager(triggerEventManager);
}
public TriggerEventManager getTriggerEventManager()
{
return triggerEventManager;
}
public EventPager getEventPager()
{
return evtPager;
}
/**
* @return Returns the filteredTriggersWrapperList.
*/
public List<TriggerWrapper> getFilteredTriggersWrapperList()
{
return filteredTriggersWrapperList;
}
/**
* @param filteredTriggersWrapperList The filteredTriggersWrapperList to set.
*/
public void setFilteredTriggersWrapperList(List<TriggerWrapper> filteredTriggersWrapperList)
{
this.filteredTriggersWrapperList = filteredTriggersWrapperList;
}
/**
* Returns a List of ConfigurablePropertyWrapper objects which correspond to the properties which
* can be configured for the current ConfigurableJobBeanWrapper. {@link #setConfigurableJobBeanWrapper(ConfigurableJobBeanWrapper)}
* must be called first, or this method will return null.
*
* @return List of ConfigurablePropertyWrappers, or null.
*/
public List<ConfigurablePropertyWrapper> getConfigurableProperties()
{
return configurableJobProperties;
}
/**
* This method runs internally to refresh the set of ConfigurablePropertyWrappers whenever
* {@link #setConfigurableJobBeanWrapper(ConfigurableJobBeanWrapper)} or
* {@link #setJobDetail(JobDetail)} is called. {@link getConfigurableJobBeanWrapper()} must not be null
* for this operation to succeed.
*/
private void refreshProperties ()
{
final ConfigurableJobBeanWrapper
job = getConfigurableJobBeanWrapper();
if (job == null)
{
configurableJobProperties = null;
return;
}
final Set<ConfigurableJobProperty>
props = job.getConfigurableJobProperties();
final JobDetail
jd = getJobDetail();
final JobDataMap
dataMap = (jd != null) ? jd.getJobDataMap() : null;
if (configurableJobResources == null)
{
LOG.error ("no resource bundle provided for jobs of type: " + job.getJobType() + ". Labels will not be rendered correctly in the scheduler UI");
}
//create a List of jobs, b/c JSF can't handle Sets as a backing bean for a dataTable
if (props != null)
{
configurableJobProperties = new LinkedList<ConfigurablePropertyWrapper>();
for (ConfigurableJobProperty prop : props)
{
ConfigurablePropertyWrapper
wrapper = new ConfigurablePropertyWrapper();
String
value = null;
wrapper.setJobProperty(prop);
if (dataMap == null || (value = (String) dataMap.get(prop.getLabelResourceKey())) == null)
{
wrapper.setValue (prop.getDefaultValue());
}
else
{
wrapper.setValue (value);
}
configurableJobProperties.add(wrapper);
if (configurableJobResources != null)
{
//check for resource strings for label and desc - warn if they are not present
final ConfigurableJobProperty
property = wrapper.getJobProperty();
final String
labelKey = property.getLabelResourceKey(),
descKey = property.getDescriptionResourceKey();
if (labelKey == null)
{
LOG.error ("no resource key provided for property label - NullPointerExceptions may occur in scheduler when processing jobs of type " + job.getJobType());
}
else if (configurableJobResources.get(labelKey) == null)
{
LOG.warn("no resource string provided for the property label key '" + labelKey + "' for the job type " + job.getJobType());
}
if (descKey == null)
{
LOG.warn ("no resource key provided for property description in job type " + job.getJobType());
}
else if (configurableJobResources.get(descKey) == null)
{
LOG.warn("no resource string provided for the property description key '" + descKey + "' for the job type " + job.getJobType());
}
}
}
}
}
/**
* Sets the JobDetail object for the job presently being editted or sor which properties or triggers are being
* editted.
*
* @param detail
*/
public void setJobDetail (JobDetail detail)
{
jobDetail = detail;
refreshProperties();
}
/**
* Returns the JobDetail object for the job presently being editted or sor which properties or triggers are being
* editted.
*
* @returns JobDetail
*/
public JobDetail getJobDetail()
{
return jobDetail;
}
/**
* Sets the TriggerWrapper being editted.
*
* @param tw
*/
public void setTriggerWrapper (TriggerWrapper tw)
{
triggerWrapper = tw;
}
/**
* Returns the TriggerWrapper being editted.
*
* @return TroggerWrapper
*/
public TriggerWrapper getTriggerWrapper ()
{
return triggerWrapper;
}
/**
* Sets the ConfigurableJobBeanWrapper to establish the job whose properties are being editted
* during a job or trigger creation process.
*
* @param job
*/
public void setConfigurableJobBeanWrapper(ConfigurableJobBeanWrapper job)
{
configurableJobBeanWrapper = job;
if (job != null)
{
final String
rbBase = job.getResourceBundleBase();
//process the ResourceBundle into a map, b/c JSF won't allow method calls like rb.getString()
final ResourceBundle
rb = ResourceBundle.getBundle(rbBase);
if(rb != null)
{
configurableJobResources = new HashMap<String, String> ();
final Enumeration
keyIt = rb.getKeys();
while (keyIt.hasMoreElements())
{
final String
key = (String)keyIt.nextElement();
configurableJobResources.put(key, rb.getString(key));
}
}
else
{
configurableJobResources = null;
}
}
else
{
configurableJobResources = null;
}
refreshProperties();
}
/**
* Returns the ConfigurableJobBeanWrapper currently the focus for editting properties either to create a job
* or to schedule a trigger for a job.
*
* @return ConfigurableJobBeanWrapper
*/
public ConfigurableJobBeanWrapper getConfigurableJobBeanWrapper ()
{
return configurableJobBeanWrapper;
}
/**
* Returns the resource map which will provide labels for the ConfigurableJobBeanWrapper currently the focus
* of the job or trigger creation process. This is a read-only property which is set by {@link #setConfigurableJobBeanWrapper(ConfigurableJobBeanWrapper)}
*
* @return
*/
public Map<String, String> getConfigurableJobResources()
{
return configurableJobResources;
}
/**
* Returns validation errors which have occured during the editting of properties for a ConfigurableJobBeanWrapper.
*
* @return List of validation error Strings
*/
public List<String> getConfigurableJobErrorMessages()
{
return configurableJobErrorMessages;
}
/**
* @return Returns the isSelectAllJobsSelected.
*/
public boolean isSelectAllJobsSelected()
{
return isSelectAllJobsSelected;
}
/**
* @param isSelectAllJobsSelected
* The isSelectAllJobsSelected to set.
*/
public void setSelectAllJobsSelected(boolean isSelectAllJobsSelected)
{
this.isSelectAllJobsSelected = isSelectAllJobsSelected;
}
/**
* @return Returns the selectedClass.
*/
public String getSelectedClass()
{
return selectedClass;
}
/**
* @param selectedClass
* The selectedClass to set.
*/
public void setSelectedClass(String selectedClass)
{
this.selectedClass = selectedClass;
}
/**
* @return Returns the jobName.
*/
public String getJobName()
{
return jobName;
}
/**
* @param jobName
* The jobName to set.
*/
public void setJobName(String jobName)
{
this.jobName = jobName;
}
/**
* @return Returns the triggerName.
*/
public String getTriggerName()
{
return triggerName;
}
/**
* @param triggerName The triggerName to set.
*/
public void setTriggerName(String triggerName)
{
this.triggerName = triggerName;
}
/**
* @return Returns the triggerExpression.
*/
public String getTriggerExpression()
{
return triggerExpression;
}
/**
* @param triggerExpression The triggerExpression to set.
*/
public void setTriggerExpression(String triggerExpression)
{
this.triggerExpression = triggerExpression;
}
/**
* @return Returns the jobClasses.
*/
public Map getJobClasses()
{
return schedulerManager.getQrtzQualifiedJobs();
}
/**
* @return Returns the schedulerManager.
*/
public SchedulerManager getSchedulerManager()
{
return schedulerManager;
}
/**
* @param schedulerManager
* The schedulerManager to set.
*/
public void setSchedulerManager(SchedulerManager schedulerManager)
{
this.schedulerManager = schedulerManager;
}
/**
* @return Returns the jobDetailWrapperList.
*/
public List<JobDetailWrapper> getJobDetailWrapperList()
{
return jobDetailWrapperList;
}
/**
* @param jobDetailWrapperList
* The jobDetailWrapperList to set.
*/
public void setJobDetailWrapperList(List<JobDetailWrapper> jobDetailWrapperList)
{
this.jobDetailWrapperList = jobDetailWrapperList;
}
/**
* @return Returns the filteredJobDetailWrapperList.
*/
public List getFilteredJobDetailWrapperList()
{
return filteredJobDetailWrapperList;
}
/**
* @param filteredJobDetailWrapperList
* The filteredJobDetailWrapperList to set.
*/
public void setFilteredJobDetailWrapperList(List filteredJobDetailWrapperList)
{
this.filteredJobDetailWrapperList = filteredJobDetailWrapperList;
}
/**
* @return Returns the selectedJobDetailWrapper.
*/
public JobDetailWrapper getSelectedJobDetailWrapper()
{
return selectedJobDetailWrapper;
}
/**
* @param selectedJobDetailWrapper
* The selectedJobDetailWrapper to set.
*/
public void setSelectedJobDetailWrapper(
JobDetailWrapper selectedJobDetailWrapper)
{
this.selectedJobDetailWrapper = selectedJobDetailWrapper;
}
/**
* This method runs the current job only once, right now
* @return int 0 if it's not running, 1 if it is, 2 if there is an error
*/
public int getSelectedJobRunning()
{
Scheduler scheduler = schedulerManager.getScheduler();
if (scheduler == null)
{
LOG.error("Scheduler is down!");
return 2;
}
try
{
List executingJobs = scheduler.getCurrentlyExecutingJobs();
for(Iterator i = executingJobs.iterator(); i.hasNext(); ) {
JobExecutionContext jobExecContext = (JobExecutionContext)i.next();
if(selectedJobDetailWrapper.getJobDetail().getFullName().equals(
jobExecContext.getJobDetail().getFullName()) )
return 1;
}
return 0;
}
catch (Exception e)
{
LOG.error("Failed to trigger job now");
return 2;
}
}
/**
* Convenience method for creating a JobDetail object from a JobBeanWrapper. The JobDetail object is
* used to actually create a job within Quartz, and is also tracked by the {@link getJobDetail()} property
* for use during the property editting process.
*
* @param job
* @return JobDetail object constructed from the job argument
*/
private JobDetail createJobDetail (JobBeanWrapper job)
{
JobDetail
jd = new JobDetail (jobName, Scheduler.DEFAULT_GROUP, job.getJobClass(), false, true, true);
JobDataMap
map = jd.getJobDataMap();
map.put(JobBeanWrapper.SPRING_BEAN_NAME, job.getBeanId());
map.put(JobBeanWrapper.JOB_TYPE, job.getJobType());
return jd;
}
public String processCreateJob()
{
Scheduler scheduler = schedulerManager.getScheduler();
if (scheduler == null)
{
LOG.error("Scheduler is down!");
return "error";
}
try
{
//get a JobDetail object in case one is already in the Session
// (eg. if we have returned her from a validation error
JobDetail jd = getJobDetail();
JobBeanWrapper job = getSchedulerManager().getJobBeanWrapper(selectedClass);
if (job != null)
{
// create a new JobDetail object for this job
jd = createJobDetail(job);
//we have a job, so check to see if properties need to be set
if (ConfigurableJobBeanWrapper.class.isAssignableFrom(job.getClass()))
{
//this job is configurable, provide a screen to edit properties
final ConfigurableJobBeanWrapper
configurableJob = (ConfigurableJobBeanWrapper)job;
// prepare properties for use within the property configuration UI
setConfigurableJobBeanWrapper (configurableJob);
setJobDetail(jd);
return "edit_properties";
}
else
{
//not a configurable job, create the job and move on
setConfigurableJobBeanWrapper(null);
}
}
else
{
// this is not a job configured via a JobBeanWrapper
// assume the class is a Job and schedule its execution
setConfigurableJobBeanWrapper(null);
jd = new JobDetail(jobName, Scheduler.DEFAULT_GROUP,
Class.forName(selectedClass.toString()), false, true, true);
}
// create the job and show the list of jobs
scheduler.addJob(jd, false);
jobName = null;
// Clear out the form.
selectedClass = null;
processRefreshJobs();
return "jobs";
}
catch (Exception e)
{
LOG.error("Failed to create job");
return "error";
}
}
/**
* Processes the properties which have been set during creation of a job.
*
* @return
*/
public String processSetProperties()
{
Scheduler scheduler = schedulerManager.getScheduler();
if (scheduler == null)
{
LOG.error("Scheduler is down!");
return "error";
}
configurableJobErrorMessages = new LinkedList<String>();
try
{
JobDetail
jd = getJobDetail();
JobBeanWrapper
job = getSchedulerManager().getJobBeanWrapper(selectedClass);
final ConfigurableJobBeanWrapper
configurableJob = getConfigurableJobBeanWrapper();
final List<ConfigurablePropertyWrapper>
properties = getConfigurableProperties();
final ResourceBundle
jobRb = ResourceBundle.getBundle(configurableJob.getResourceBundleBase());
final ConfigurableJobPropertyValidator
validator = configurableJob.getConfigurableJobPropertyValidator();
if (jd != null)
{
final JobDataMap
dataMap = jd.getJobDataMap();
for (ConfigurablePropertyWrapper wrapper : properties)
{
final ConfigurableJobProperty
property = wrapper.getJobProperty();
final String
label = property.getLabelResourceKey(),
value = wrapper.getValue();
if (property.isRequired() && (value == null || value.trim().length() == 0))
{
String
propName = (jobRb != null)?jobRb.getString(label):label,
msg = null;
if (propName == null)
propName = label;
try
{
msg = rb.getString("properties_required");
}
catch (MissingResourceException mre)
{
msg = "<Missing resource string: properties_required>";
}
configurableJobErrorMessages.add(msg + ": " + propName);
}
else
{
try
{
validator.assertValid(label, value);
}
catch (ConfigurableJobPropertyValidationException cjpve)
{
String
errorKey = cjpve.getMessage(),
errorMessage = jobRb.getString(errorKey);
configurableJobErrorMessages.add ((errorMessage == null)?errorKey:errorMessage);
continue;
}
dataMap.put(property.getLabelResourceKey(), value);
}
}
if (!configurableJobErrorMessages.isEmpty())
{
return null;
}
}
else
{
setJobDetail (createJobDetail(job));
return null;
}
scheduler.addJob(jd, false);
// Clear out the form
jobName = null;
jobDetail = null;
processRefreshJobs();
return "jobs";
}
catch (Exception e)
{
LOG.error("Failed to create job");
return "error";
}
}
/**
* Convenience method for scheduling a Trigger with the Quartz Scheduler object.
* @param wrapper
* @throws SchedulerException
*/
private void scheduleTrigger(TriggerWrapper wrapper) throws SchedulerException
{
Trigger
trigger = wrapper.getTrigger();
Scheduler
scheduler = schedulerManager.getScheduler();
scheduler.scheduleJob(trigger);
selectedJobDetailWrapper.getTriggerWrapperList().add(wrapper);
int currentTriggerCount = selectedJobDetailWrapper.getTriggerCount()
.intValue();
selectedJobDetailWrapper.setTriggerCount(Integer.valueOf(
currentTriggerCount + 1));
}
/**
* Creates a trigger for the currently selected JobDetailWrapper. This method redirects to UI
* to override configured properties if the Job is configurable.
*
* @return
*/
public String processCreateTrigger()
{
Scheduler scheduler = schedulerManager.getScheduler();
if (scheduler == null)
{
LOG.error("Scheduler is down!");
return "error";
}
try
{
JobDetail
jd = selectedJobDetailWrapper.getJobDetail();
Trigger trigger = new CronTrigger(triggerName, Scheduler.DEFAULT_GROUP,
jd.getName(),
Scheduler.DEFAULT_GROUP, triggerExpression);
TriggerWrapper tempTriggerWrapper = new TriggerWrapperImpl();
tempTriggerWrapper.setTrigger(trigger);
JobBeanWrapper
job = getSchedulerManager().getJobBeanWrapper(selectedJobDetailWrapper.getJobType());
if (job != null)
{
if (ConfigurableJobBeanWrapper.class.isAssignableFrom(job.getClass()))
{
final ConfigurableJobBeanWrapper
configurableJob = (ConfigurableJobBeanWrapper)job;
setJobDetail (jd);
setConfigurableJobBeanWrapper (configurableJob);
setTriggerWrapper (tempTriggerWrapper);
return "edit_trigger_properties";
}
else
{
setConfigurableJobBeanWrapper(null);
setJobDetail(null);
setTriggerWrapper(null);
}
}
scheduleTrigger (tempTriggerWrapper);
return "edit_triggers";
}
catch (Exception e)
{
LOG.error("Failed to create trigger");
return "error";
}
finally
{
triggerName = null;
triggerExpression = null;
}
}
/**
* Validates and sets the properties for a Trigger once the property configuration UI has been completed.
*
* @return
*/
public String processSetTriggerProperties ()
{
Scheduler scheduler = schedulerManager.getScheduler();
if (scheduler == null)
{
LOG.error("Scheduler is down!");
return "error";
}
configurableJobErrorMessages = new LinkedList<String>();
try
{
final JobDetail
jd = selectedJobDetailWrapper.getJobDetail();
final ConfigurableJobBeanWrapper
configurableJob = getConfigurableJobBeanWrapper();
final TriggerWrapper
triggerWrapper = getTriggerWrapper();
final Trigger
trigger = triggerWrapper.getTrigger();
final List<ConfigurablePropertyWrapper>
properties = getConfigurableProperties();
final ResourceBundle
jobRb = ResourceBundle.getBundle(configurableJob.getResourceBundleBase());
final ConfigurableJobPropertyValidator
validator = configurableJob.getConfigurableJobPropertyValidator();
final JobDataMap
dataMap = trigger.getJobDataMap();
for (ConfigurablePropertyWrapper wrapper : properties)
{
final ConfigurableJobProperty
property = wrapper.getJobProperty();
final String
label = property.getLabelResourceKey(),
value = wrapper.getValue();
if (property.isRequired() && (value == null || value.trim().length() == 0))
{
String
propName = (jobRb != null)?jobRb.getString(label):label,
msg = null;
if (propName == null)
propName = label;
try
{
msg = rb.getString("properties_required");
}
catch (MissingResourceException mre)
{
msg = "<Missing resource string: properties_required>";
}
configurableJobErrorMessages.add(msg + ": " + propName);
}
else
{
try
{
validator.assertValid(label, value);
}
catch (ConfigurableJobPropertyValidationException cjpve)
{
String
errorKey = cjpve.getMessage(),
errorMessage = jobRb.getString(errorKey);
configurableJobErrorMessages.add ((errorMessage == null)?errorKey:errorMessage);
continue;
}
dataMap.put(property.getLabelResourceKey(), value);
}
}
if (!configurableJobErrorMessages.isEmpty())
{
return null;
}
scheduleTrigger(triggerWrapper);
}
catch (Exception e)
{
LOG.error("Failed to create job");
return "error";
}
return "edit_triggers";
}
public String processSelectAllJobs()
{
isSelectAllJobsSelected = !isSelectAllJobsSelected;
processRefreshJobs();
return "jobs";
}
public String processSelectAllTriggers()
{
isSelectAllTriggersSelected = !isSelectAllTriggersSelected;
for (Iterator i = selectedJobDetailWrapper.getTriggerWrapperList()
.iterator(); i.hasNext();)
{
if (isSelectAllTriggersSelected)
{
((TriggerWrapper) i.next()).setIsSelected(true);
}
else
{
((TriggerWrapper) i.next()).setIsSelected(false);
}
}
return "edit_triggers";
}
public String processDeleteJobs()
{
try
{
for (Iterator i = filteredJobDetailWrapperList.iterator(); i.hasNext();)
{
JobDetailWrapper jobDetailWrapper = (JobDetailWrapper) i.next();
schedulerManager.getScheduler().deleteJob(
jobDetailWrapper.getJobDetail().getName(), Scheduler.DEFAULT_GROUP);
}
}
catch (SchedulerException e)
{
LOG.error("Scheduler Down");
}
processRefreshJobs();
return "jobs";
}
public String processDeleteTriggers()
{
try
{
TriggerWrapper triggerWrapper;
for (Iterator<TriggerWrapper> i = filteredTriggersWrapperList.iterator(); i.hasNext();)
{
triggerWrapper = (TriggerWrapper) i.next();
schedulerManager.getScheduler().unscheduleJob(
triggerWrapper.getTrigger().getName(), triggerWrapper.getTrigger().getGroup());
selectedJobDetailWrapper.getTriggerWrapperList().remove(triggerWrapper);
}
}
catch (SchedulerException e)
{
LOG.error("Scheduler Down");
}
return "edit_triggers";
}
public String processRefreshJobs()
{
try
{
Scheduler scheduler = schedulerManager.getScheduler();
String[] jobNames = scheduler.getJobNames(Scheduler.DEFAULT_GROUP);
jobDetailWrapperList = new ArrayList();
for (int i = 0; i < jobNames.length; i++)
{
JobDetailWrapper jobDetailWrapper = new JobDetailWrapperImpl();
jobDetailWrapper.setJobDetail(scheduler.getJobDetail(jobNames[i],
Scheduler.DEFAULT_GROUP));
Trigger[] triggerArr = scheduler.getTriggersOfJob(jobNames[i],
Scheduler.DEFAULT_GROUP);
List<TriggerWrapper> triggerWrapperList = new ArrayList<TriggerWrapper>();
TriggerWrapper tw;
for (int j = 0; j < triggerArr.length; j++)
{
tw = new TriggerWrapperImpl();
tw.setTrigger(triggerArr[j]);
triggerWrapperList.add(tw);
}
jobDetailWrapper.setTriggerWrapperList(triggerWrapperList);
jobDetailWrapperList.add(jobDetailWrapper);
}
}
catch (SchedulerException e)
{
LOG.error("scheduler error while getting job detail");
}
// test for select all
if (isSelectAllJobsSelected)
{
for (Iterator<JobDetailWrapper> i = jobDetailWrapperList.iterator(); i.hasNext();)
{
if (isSelectAllJobsSelected)
{
((JobDetailWrapper) i.next()).setIsSelected(true);
}
else
{
((JobDetailWrapper) i.next()).setIsSelected(false);
}
}
}
return "jobs";
}
/**
* Determines if the Job has configuration properties which might need to be overriden when the
* job is run. If so this redirects to a property configuration screen. Otherwise it simply continues
* to a confirmation screen.
*
* @return
*/
public String processPrepRunJobNow()
{
Scheduler scheduler = schedulerManager.getScheduler();
if (scheduler == null)
{
LOG.error("Scheduler is down!");
return "error";
}
try
{
JobDetail
jd = selectedJobDetailWrapper.getJobDetail();
JobBeanWrapper
job = getSchedulerManager().getJobBeanWrapper(selectedJobDetailWrapper.getJobType());
if (job != null)
{
if (ConfigurableJobBeanWrapper.class.isAssignableFrom(job.getClass()))
{
final ConfigurableJobBeanWrapper
configurableJob = (ConfigurableJobBeanWrapper)job;
setJobDetail (jd);
setConfigurableJobBeanWrapper (configurableJob);
return "edit_runnow_properties";
}
else
{
setConfigurableJobBeanWrapper(null);
setJobDetail(null);
}
}
return "run_job_confirm";
}
catch (Exception e)
{
LOG.error("Failed to run job now");
return "error";
}
}
/**
* Processes the properties set for a Job that will be run now.
*
* @return
*/
public String processSetRunNowProperties()
{
Scheduler scheduler = schedulerManager.getScheduler();
if (scheduler == null)
{
LOG.error("Scheduler is down!");
return "error";
}
try
{
JobDetail
jd = getJobDetail();
JobBeanWrapper
job = getSchedulerManager().getJobBeanWrapper(selectedJobDetailWrapper.getJobType());
if (job != null)
{
if (ConfigurableJobBeanWrapper.class.isAssignableFrom(job.getClass()))
{
configurableJobErrorMessages = new LinkedList<String>();
final ConfigurableJobBeanWrapper
configurableJob = (ConfigurableJobBeanWrapper) job;
final List<ConfigurablePropertyWrapper>
properties = getConfigurableProperties();
final ResourceLoader
jobRb = new ResourceLoader(configurableJob.getResourceBundleBase());
final ConfigurableJobPropertyValidator
validator = configurableJob.getConfigurableJobPropertyValidator();
for (ConfigurablePropertyWrapper wrapper : properties)
{
final ConfigurableJobProperty
property = wrapper.getJobProperty();
final String
label = property.getLabelResourceKey(),
value = wrapper.getValue();
if (property.isRequired() && (value == null || value.trim().length() == 0))
{
String
propName = (jobRb != null)?jobRb.getString(label):label,
msg = null;
if (propName == null)
propName = label;
try
{
msg = rb.getString("properties_required");
}
catch (MissingResourceException mre)
{
msg = "<Missing resource string: properties_required>";
}
configurableJobErrorMessages.add(msg + ": " + propName);
}
else
{
try
{
validator.assertValid(label, value);
}
catch (ConfigurableJobPropertyValidationException cjpve)
{
String
errorKey = cjpve.getMessage(),
errorMessage = jobRb.getString(errorKey);
configurableJobErrorMessages.add ((errorMessage==null)?errorKey:errorMessage);
continue;
}
}
}
if (!configurableJobErrorMessages.isEmpty())
{
return null;
}
}
}
return "run_now_confirm";
}
catch (Exception e)
{
LOG.error("Failed to trigger job now");
return "error";
}
}
/**
* This method runs the current job only once, right now. It will set properties on the Job execution if the
* Job has configurable properties.
*
* @return String
*/
public String processRunJobNow()
{
Scheduler scheduler = schedulerManager.getScheduler();
if (scheduler == null)
{
LOG.error("Scheduler is down!");
return "error";
}
try
{
JobDetail
jd = getJobDetail();
JobBeanWrapper
job = getSchedulerManager().getJobBeanWrapper(selectedJobDetailWrapper.getJobType());
JobDataMap
dataMap = null;
if (job != null)
{
if (ConfigurableJobBeanWrapper.class.isAssignableFrom(job.getClass()))
{
configurableJobErrorMessages = new LinkedList<String>();
final List<ConfigurablePropertyWrapper>
properties = getConfigurableProperties();
dataMap = new JobDataMap (jd.getJobDataMap());
for (ConfigurablePropertyWrapper wrapper : properties)
{
final ConfigurableJobProperty
property = wrapper.getJobProperty();
final String
label = property.getLabelResourceKey(),
value = wrapper.getValue();
dataMap.put(label, value);
}
}
}
if (dataMap == null)
{
scheduler.triggerJob(selectedJobDetailWrapper.getJobDetail().getName(),
selectedJobDetailWrapper.getJobDetail().getGroup());
}
else
{
scheduler.triggerJob(selectedJobDetailWrapper.getJobDetail().getName(),
selectedJobDetailWrapper.getJobDetail().getGroup(),
dataMap);
}
return "success";
}
catch (Exception e)
{
LOG.error("Failed to trigger job now");
return "error";
}
}
public List<JobExecutionContextWrapperBean> getRunningJobs() {
List<JobExecutionContext> currentJobs = new ArrayList<JobExecutionContext>();
List<JobExecutionContextWrapperBean> currentWrappedJobs = new ArrayList<JobExecutionContextWrapperBean>();
Scheduler scheduler = schedulerManager.getScheduler();
if (scheduler == null)
{
LOG.error("Scheduler is down!");
}
else {
try {
currentJobs = scheduler.getCurrentlyExecutingJobs();
} catch (SchedulerException e) {
LOG.warn("Unable to get currently executing jobs");
}
}
for (JobExecutionContext jec : currentJobs)
{
JobExecutionContextWrapperBean jobExecutionContextWrapper = new JobExecutionContextWrapperBean(this, jec);
jobExecutionContextWrapper.setIsKillable(isJobKillable(jec.getJobDetail()));
currentWrappedJobs.add(jobExecutionContextWrapper);
}
return currentWrappedJobs;
}
public boolean isJobKillable(JobDetail detail) {
if (InterruptableJob.class.isAssignableFrom(detail.getJobClass())) {
return true;
}
return false;
}
public String processRefreshFilteredJobs()
{
filteredJobDetailWrapperList = new ArrayList<JobDetailWrapper>();
for (Iterator<JobDetailWrapper> i = jobDetailWrapperList.iterator(); i.hasNext();)
{
JobDetailWrapper jobDetailWrapper = (JobDetailWrapper) i.next();
if (jobDetailWrapper.getIsSelected())
{
filteredJobDetailWrapperList.add(jobDetailWrapper);
}
}
return "delete_jobs";
}
public String processRefreshFilteredTriggers()
{
filteredTriggersWrapperList = new ArrayList<TriggerWrapper>();
for (Iterator<TriggerWrapper> i = selectedJobDetailWrapper.getTriggerWrapperList()
.iterator(); i.hasNext();)
{
TriggerWrapper triggerWrapper = (TriggerWrapper) i.next();
if (triggerWrapper.getIsSelected())
{
filteredTriggersWrapperList.add(triggerWrapper);
}
}
return "delete_triggers";
}
public int getJobNameMaxLength() {
return JOB_NAME_LENGTH_LIMIT;
}
public void validateJobName(FacesContext context, UIComponent component,
Object object)
{
if (object instanceof String)
{
String value = (String)object;
try
{
JobDetail jd = schedulerManager.getScheduler().getJobDetail(value, Scheduler.DEFAULT_GROUP);
if (jd != null)
{
FacesMessage message = new FacesMessage(rb.getString("existing_job_name"));
message.setSeverity(FacesMessage.SEVERITY_WARN);
throw new ValidatorException(message);
}
}
catch (SchedulerException e)
{
LOG.error("Scheduler down!");
}
}
}
public int getTriggerNameMaxLength() {
return TRIGGER_NAME_LENGTH_LIMIT;
}
public void validateTriggerName(FacesContext context, UIComponent component,
Object object)
{
if (object instanceof String)
{
String value = (String)object;
try
{
Trigger trigger = schedulerManager.getScheduler().getTrigger(
(String) value, Scheduler.DEFAULT_GROUP);
if (trigger != null)
{
FacesMessage message = new FacesMessage(rb.getString("existing_trigger_name"));
message.setSeverity(FacesMessage.SEVERITY_WARN);
throw new ValidatorException(message);
}
}
catch (SchedulerException e)
{
LOG.error("Scheduler down!");
}
}
}
public void validateTriggerExpression(FacesContext context,
UIComponent component, Object value)
{
if (value != null)
{
try
{
String expression = (String) value;
CronTrigger trigger = new CronTrigger();
trigger.setCronExpression(expression);
// additional checks
// quartz does not check for more than 7 tokens in expression
String[] arr = expression.split("\\s");
if (arr.length > 7)
{
throw new ParseException("Expression has more than 7 tokens", 7);
}
//(check that last 2 entries are not both * or ?
String trimmed_expression = expression.replaceAll("\\s", ""); // remove whitespace
if (trimmed_expression.endsWith(CRON_CHECK_ASTERISK)
|| trimmed_expression.endsWith(CRON_CHECK_QUESTION_MARK))
{
throw new ParseException("Cannot End in * * or ? ?", 1);
}
}
catch (ParseException e)
{
// not giving a detailed message to prevent line wraps
FacesMessage message = new FacesMessage(rb.getString("parse_exception"));
message.setSeverity(FacesMessage.SEVERITY_WARN);
throw new ValidatorException(message);
}
}
}
public void editTriggersListener(ActionEvent e)
{
FacesContext context = FacesContext.getCurrentInstance();
Map requestParams = context.getExternalContext().getRequestParameterMap();
String jobName = (String) requestParams.get("jobName");
//loop through jobDetailWrapperList finding the one selected by user.
for (Iterator i = jobDetailWrapperList.iterator(); i.hasNext();)
{
JobDetailWrapper jobDetailWrapper = (JobDetailWrapper) i.next();
if (jobDetailWrapper.getJobDetail().getName().equals(jobName))
{
selectedJobDetailWrapper = jobDetailWrapper;
break;
}
}
}
public Map<String, String> getBeanJobs() {
Map<String, String> beanJobs = new TreeMap<String, String>();
Map<String, JobBeanWrapper> serverJobs = getSchedulerManager().getBeanJobs();
for (Iterator<String> i=serverJobs.keySet().iterator();i.hasNext();) {
String job = i.next();
beanJobs.put(job, job);
}
return beanJobs;
}
public String processSetFilters()
{
getEventPager().setFilterEnabled(true);
return "events";
}
public String processClearFilters()
{
getEventPager().setFilterEnabled(false);
return "events";
}
public List<SelectItem> getScheduledJobs() throws SchedulerException
{
ArrayList<SelectItem>
scheduledJobs = new ArrayList<SelectItem> ();
String[]
jArr = schedulerManager.getScheduler().getJobNames(Scheduler.DEFAULT_GROUP);
for (int i = 0; i < jArr.length; i++)
{
scheduledJobs.add(new SelectItem(jArr[i]));
}
return scheduledJobs;
}
}