/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2009-2011 The OpenNMS Group, Inc.
* OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc.
*
* OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
*
* OpenNMS(R) is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* OpenNMS(R) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenNMS(R). If not, see:
* http://www.gnu.org/licenses/
*
* For more information contact:
* OpenNMS(R) Licensing <license@opennms.org>
* http://www.opennms.org/
* http://www.opennms.com/
*******************************************************************************/
package org.opennms.netmgt.provision.service;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import java.util.Arrays;
import java.util.Iterator;
import org.opennms.core.utils.BeanUtils;
import org.opennms.core.utils.ThreadCategory;
import org.opennms.netmgt.config.provisiond.RequisitionDef;
import org.opennms.netmgt.dao.ProvisiondConfigurationDao;
import org.opennms.netmgt.provision.service.dns.DnsUrlFactory;
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.spi.JobFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.util.StringUtils;
/**
* Maintains the Provisioner's import schedule defined in provisiond-configuration.xml
*
* @author <a href="mailto:david@opennms.org">David Hustace</a>
* @version $Id: $
*/
public class ImportScheduler implements InitializingBean {
/** Constant <code>JOB_GROUP="Provisiond"</code> */
protected static final String JOB_GROUP = "Provisiond";
@Autowired
private Scheduler m_scheduler;
@Autowired
private Provisioner m_provisioner;
@Autowired
private ProvisiondConfigurationDao m_configDao;
private Object m_lock = new Object();
private JobFactory m_importJobFactory;
/**
* <p>Constructor for ImportScheduler.</p>
*
* @param scheduler a {@link org.quartz.Scheduler} object.
*/
protected ImportScheduler(Scheduler scheduler) {
m_scheduler = scheduler;
}
/**
* <p>afterPropertiesSet</p>
*/
@Override
public void afterPropertiesSet() throws Exception {
BeanUtils.assertAutowiring(this);
try {
getScheduler().setJobFactory(getImportJobFactory());
} catch (SchedulerException e) {
log().fatal("afterPropertiesSet: couldn't set proper JobFactory for scheduler: "+e, e);
}
//TODO: this needs to be done in application context
try {
new URL("dns://host/zone");
} catch (MalformedURLException e) {
URL.setURLStreamHandlerFactory(new DnsUrlFactory());
}
buildImportSchedule();
}
/**
* <p>start</p>
*
* @throws org.quartz.SchedulerException if any.
*/
public void start() throws SchedulerException {
getScheduler().start();
}
/**
* <p>pause</p>
*
* @throws org.quartz.SchedulerException if any.
*/
public void pause() throws SchedulerException {
getScheduler().pauseAll();
}
/**
* <p>standBy</p>
*
* @throws org.quartz.SchedulerException if any.
*/
public void standBy() throws SchedulerException {
getScheduler().standby();
}
/**
* <p>resume</p>
*
* @throws org.quartz.SchedulerException if any.
*/
public void resume() throws SchedulerException {
getScheduler().resumeAll();
}
/**
* <p>stop</p>
*
* @throws org.quartz.SchedulerException if any.
*/
public void stop() throws SchedulerException {
getScheduler().shutdown();
}
/**
* Removes all jobs from the current scheduled and the builds a new schedule
* from the reloaded configuration. Since all jobs are Cron like, removing and re-adding
* shouldn't be an issue.
*
* @throws java.lang.Exception if any.
*/
protected void rebuildImportSchedule() throws Exception {
log().info("rebuildImportSchedule: acquiring lock...");
synchronized (m_lock) {
log().debug("rebuildImportSchedule: lock acquired. reloading configuration.");
try {
m_configDao.reloadConfiguration();
log().debug("rebuildImportSchedule: removing current import jobs from schedule...");
removeCurrentJobsFromSchedule();
log().debug("rebuildImportSchedule: recreating import schedule based on configuration...");
buildImportSchedule();
printCurrentSchedule();
} catch (DataAccessResourceFailureException e) {
log().error("rebuildImportSchedule: "+e.getLocalizedMessage(),e);
throw new IllegalStateException(e);
} catch (SchedulerException e) {
log().error("rebuildImportSchedule: "+e.getLocalizedMessage(),e);
throw e;
}
}
log().info("rebuildImportSchedule: schedule rebuilt and lock released.");
}
/**
* Iterates of current job list and removes each job from the underlying schedule
*
* @throws org.quartz.SchedulerException if any.
*/
protected void removeCurrentJobsFromSchedule() throws SchedulerException {
printCurrentSchedule();
synchronized (m_lock) {
Iterator<String> it = Arrays.asList(m_scheduler.getJobNames(JOB_GROUP)).iterator();
while (it.hasNext()) {
String jobName = it.next();
try {
getScheduler().deleteJob(jobName, JOB_GROUP);
} catch (SchedulerException e) {
log().error("removeCurrentJobsFromSchedule: "+e.getLocalizedMessage(), e);
}
}
}
printCurrentSchedule();
}
/**
* <p>buildImportSchedule</p>
*/
protected void buildImportSchedule() {
synchronized (m_lock) {
Iterator<RequisitionDef> it = m_configDao.getDefs().iterator();
while (it.hasNext()) {
RequisitionDef def = it.next();
JobDetail detail = null;
Trigger trigger = null;
try {
detail = new JobDetail(def.getImportName(), JOB_GROUP, ImportJob.class, false, false, false);
detail.getJobDataMap().put(ImportJob.KEY, def.getImportUrlResource());
trigger = new CronTrigger(def.getImportName(), JOB_GROUP, def.getCronSchedule());
trigger.setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING);
getScheduler().scheduleJob(detail, trigger);
} catch (ParseException e) {
log().error("buildImportSchedule: "+e.getLocalizedMessage(), e);
} catch (SchedulerException e) {
log().error("buildImportSchedule: "+e.getLocalizedMessage(), e);
}
}
}
printCurrentSchedule();
}
/**
* <p>getScheduler</p>
*
* @return a {@link org.quartz.Scheduler} object.
*/
public Scheduler getScheduler() {
return m_scheduler;
}
/**
* <p>setProvisioner</p>
*
* @param provisioner a {@link org.opennms.netmgt.provision.service.Provisioner} object.
*/
public void setProvisioner(Provisioner provisioner) {
m_provisioner = provisioner;
}
/**
* <p>getProvisioner</p>
*
* @return a {@link org.opennms.netmgt.provision.service.Provisioner} object.
*/
protected final Provisioner getProvisioner() {
return m_provisioner;
}
/**
* <p>setImportJobFactory</p>
*
* @param importJobFactory a {@link org.quartz.spi.JobFactory} object.
*/
public void setImportJobFactory(JobFactory importJobFactory) {
m_importJobFactory = importJobFactory;
}
/**
* <p>getImportJobFactory</p>
*
* @return a {@link org.quartz.spi.JobFactory} object.
*/
public JobFactory getImportJobFactory() {
return m_importJobFactory;
}
private void printCurrentSchedule() {
try {
log().info("calendarNames: "+ StringUtils.arrayToCommaDelimitedString(getScheduler().getCalendarNames()));
log().info("current executing jobs: "+StringUtils.arrayToCommaDelimitedString(getScheduler().getCurrentlyExecutingJobs().toArray()));
log().info("current job names: "+StringUtils.arrayToCommaDelimitedString(getScheduler().getJobNames(JOB_GROUP)));
log().info("scheduler metadata: "+getScheduler().getMetaData());
log().info("trigger names: "+StringUtils.arrayToCommaDelimitedString(getScheduler().getTriggerNames(JOB_GROUP)));
Iterator<String> it = Arrays.asList(getScheduler().getTriggerNames(JOB_GROUP)).iterator();
while (it.hasNext()) {
String triggerName = it.next();
CronTrigger t = (CronTrigger) getScheduler().getTrigger(triggerName, JOB_GROUP);
StringBuilder sb = new StringBuilder("trigger: ");
sb.append(triggerName);
sb.append(", calendar name: ");
sb.append(t.getCalendarName());
sb.append(", cron expression: ");
sb.append(t.getCronExpression());
sb.append(", URL: ");
sb.append(t.getJobDataMap().get(ImportJob.KEY));
sb.append(", next fire time: ");
sb.append(t.getNextFireTime());
sb.append(", previous fire time: ");
sb.append(t.getPreviousFireTime());
sb.append(", time zone: ");
sb.append(t.getTimeZone());
sb.append(", priority: ");
sb.append(t.getPriority());
log().info(sb.toString());
}
} catch (Throwable e) {
log().error("printCurrentSchedule: "+e.getLocalizedMessage(), e);
}
}
private ThreadCategory log() {
return ThreadCategory.getInstance(this.getClass());
}
}