/*
* NOTE: This copyright does *not* cover user programs that use HQ
* program services by normal system calls through the application
* program interfaces provided as part of the Hyperic Plug-in Development
* Kit or the Hyperic Client Development Kit - this is merely considered
* normal use of the program, and does *not* fall under the heading of
* "derived work".
*
* Copyright (C) [2004-2008], Hyperic, Inc.
* This file is part of HQ.
*
* HQ is free software; you can redistribute it and/or modify
* it under the terms version 2 of the GNU General Public License as
* published by the Free Software Foundation. This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
package org.hyperic.hq.autoinventory.server.session;
import java.text.ParseException;
import java.util.Collection;
import java.util.Date;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.hq.appdef.server.session.Platform;
import org.hyperic.hq.appdef.shared.AppdefEntityID;
import org.hyperic.hq.appdef.shared.PlatformManager;
import org.hyperic.hq.appdef.shared.PlatformNotFoundException;
import org.hyperic.hq.authz.server.session.AuthzSubject;
import org.hyperic.hq.autoinventory.AIHistory;
import org.hyperic.hq.autoinventory.AISchedule;
import org.hyperic.hq.autoinventory.AutoinventoryException;
import org.hyperic.hq.autoinventory.DuplicateAIScanNameException;
import org.hyperic.hq.autoinventory.ScanConfigurationCore;
import org.hyperic.hq.autoinventory.shared.AIScheduleManager;
import org.hyperic.hq.autoinventory.shared.AIScheduleValue;
import org.hyperic.hq.common.NotFoundException;
import org.hyperic.hq.common.SystemException;
import org.hyperic.hq.context.Bootstrap;
import org.hyperic.hq.dao.AIHistoryDAO;
import org.hyperic.hq.dao.AIScheduleDAO;
import org.hyperic.hq.scheduler.ScheduleParseException;
import org.hyperic.hq.scheduler.ScheduleParser;
import org.hyperic.hq.scheduler.ScheduleValue;
import org.hyperic.hq.scheduler.ScheduleWillNeverFireException;
import org.hyperic.hq.scheduler.server.session.BaseScheduleManager;
import org.hyperic.util.jdbc.DBUtil;
import org.hyperic.util.pager.PageControl;
import org.hyperic.util.pager.PageList;
import org.hyperic.util.pager.SortAttribute;
import org.quartz.CronTrigger;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleTrigger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* Manager for dealing with scheduled autoinventory scans.
*
*/
@Service
public class AIScheduleManagerImpl
extends BaseScheduleManager implements AIScheduleManager {
private static final String GROUP = "autoinventory";
private final Log log = LogFactory.getLog(AIScheduleManagerImpl.class.getName());
private static final String JOB_PREFIX = "aiScan";
private static final String SCHEDULE_PREFIX = "interval";
private static final String PAGER_BASE = "org.hyperic.hq.autoinventory.server.session.";
private static final String HISTORY_PAGER = PAGER_BASE + "PagerProcessor_ai_history";
private static final String SCHEDULE_PAGER = PAGER_BASE + "PagerProcessor_ai_schedule";
private AIScheduleDAO aiScheduleDao;
private PlatformManager platformManager;
private AIHistoryDAO aiHistoryDao;
@Autowired
public AIScheduleManagerImpl(Scheduler scheduler, DBUtil dbUtil, AIScheduleDAO aiScheduleDao,
PlatformManager platformManager, AIHistoryDAO aiHistoryDAO) {
super(scheduler, dbUtil);
this.aiScheduleDao = aiScheduleDao;
this.platformManager = platformManager;
this.aiHistoryDao = aiHistoryDAO;
}
protected String getHistoryPagerClass() {
return HISTORY_PAGER;
}
protected String getSchedulePagerClass() {
return SCHEDULE_PAGER;
}
protected String getJobPrefix() {
return JOB_PREFIX;
}
protected String getSchedulePrefix() {
return SCHEDULE_PREFIX;
}
protected void setupJobData(JobDetail jobDetail, AuthzSubject subject, AppdefEntityID id,
ScanConfigurationCore scanConfig, String scanName, String scanDesc, String os,
ScheduleValue schedule) {
String scheduleString;
Boolean scheduled = new Boolean(schedule != null);
if (scheduled.booleanValue()) {
scheduleString = schedule.getScheduleString();
} else {
scheduleString = "Single execution";
}
// Quartz 1.5 requires Strings in the JobDataMap to be non-null
if (scanName == null) {
scanName = "";
}
if (scanDesc == null) {
scanDesc = "";
}
super.setupJobData(jobDetail, subject, id, scheduleString, scheduled, null);
JobDataMap dataMap = jobDetail.getJobDataMap();
try {
dataMap.put(AIScanJob.PROP_CONFIG, scanConfig.encode());
dataMap.put(AIScanJob.PROP_SCAN_OS, os);
dataMap.put(AIScanJob.PROP_SCANNAME, scanName);
dataMap.put(AIScanJob.PROP_SCANDESC, scanDesc);
} catch (Exception e) {
throw new SystemException(e);
}
}
/**
* Schedule an AI scan on an appdef entity (platform or group of platforms)
*
*
*/
@Transactional
public void doScheduledScan(AuthzSubject subject, AppdefEntityID id, ScanConfigurationCore scanConfig,
String scanName, String scanDesc, ScheduleValue schedule)
throws AutoinventoryException, DuplicateAIScanNameException, ScheduleWillNeverFireException {
// find the os for the platform
Platform pValue = null;
try {
pValue = platformManager.findPlatformById(id.getId());
} catch (PlatformNotFoundException e) {
throw new AutoinventoryException(e);
}
String configid = "config-" + String.valueOf(scanConfig.hashCode());
String jobName = getJobName(subject, id, configid);
String triggerName = getTriggerName(subject, id, configid);
// Setup the quartz job class that will handle this ai action.
Class<?> jobClass = id.isGroup() ? AIScanGroupJob.class : AIScanJob.class;
JobDetail jobDetail = new JobDetail(jobName, GROUP, jobClass);
setupJobData(jobDetail, subject, id, scanConfig, scanName, scanDesc, pValue.getPlatformType().getName(),
schedule);
// On-demand scans will have no schedule
if (schedule == null) {
jobDetail.setVolatility(true);
SimpleTrigger trigger = new SimpleTrigger(triggerName, GROUP);
trigger.setVolatility(true);
try {
log.info("Scheduling job for immediate execution: " + jobDetail);
scheduler.scheduleJob(jobDetail, trigger);
return;
} catch (SchedulerException e) {
log.error("Unable to schedule job: " + e.getMessage());
return;
}
}
String cronStr;
try {
cronStr = ScheduleParser.getCronString(schedule);
} catch (ScheduleParseException e) {
log.error("Unable to get cron string: " + e.getMessage());
throw new AutoinventoryException(e);
}
// Single scheduled actions do not have cron strings
if (cronStr == null) {
try {
SimpleTrigger trigger = new SimpleTrigger(triggerName, GROUP, schedule.getStart());
Date nextFire = trigger.getFireTimeAfter(new Date());
if (nextFire == null) {
throw new ScheduleWillNeverFireException();
}
scheduler.scheduleJob(jobDetail, trigger);
checkUniqueName(aiScheduleDao, scanName);
AISchedule aiLoc = aiScheduleDao.create(id, subject.getName(), scanName, scanDesc, schedule, nextFire
.getTime(), triggerName, jobName);
aiLoc.setConfig(scanConfig.serialize());
} catch (DuplicateAIScanNameException e) {
throw e;
} catch (ScheduleWillNeverFireException e) {
throw e;
} catch (Exception e) {
log.error("Unable to schedule job: " + e.getMessage());
throw new AutoinventoryException(e);
}
} else {
try {
CronTrigger trigger = new CronTrigger(triggerName, GROUP, jobName, GROUP, schedule.getStart(), schedule
.getEnd(), cronStr);
// Quartz used to throw an exception on scheduleJob if the
// job would never fire. Guess that is not the case anymore
Date nextFire = trigger.getFireTimeAfter(new Date());
if (nextFire == null) {
throw new ScheduleWillNeverFireException();
}
scheduler.scheduleJob(jobDetail, trigger);
checkUniqueName(aiScheduleDao, scanName);
AISchedule aiLoc = aiScheduleDao.create(id, subject.getName(), scanName, scanDesc, schedule, nextFire
.getTime(), triggerName, jobName);
aiLoc.setConfig(scanConfig.serialize());
} catch (DuplicateAIScanNameException e) {
throw e;
} catch (ScheduleWillNeverFireException e) {
throw e;
} catch (ParseException e) {
log.error("Unable to setup cron trigger: " + e.getMessage());
throw new AutoinventoryException(e);
} catch (Exception e) {
log.error("Unable to schedule job: " + e.getMessage());
throw new AutoinventoryException(e);
}
}
}
/**
* Get a list of scheduled scans based on appdef id
*
*
*/
@Transactional(readOnly=true)
public PageList<AIScheduleValue> findScheduledJobs(AuthzSubject subject, AppdefEntityID id, PageControl pc) throws NotFoundException {
// default the sorting to the next fire time
pc = PageControl.initDefaults(pc, SortAttribute.CONTROL_NEXTFIRE);
Collection<AISchedule> schedule;
int sortAttr = pc.getSortattribute();
switch (sortAttr) {
case SortAttribute.RESOURCE_NAME:
schedule = aiScheduleDao.findByEntityScanName(id.getType(), id.getID(), pc.isAscending());
break;
case SortAttribute.CONTROL_NEXTFIRE:
schedule = aiScheduleDao.findByEntityFireTime(id.getType(), id.getID(), pc.isAscending());
break;
default:
throw new NotFoundException("Unknown sort attribute: " + sortAttr);
}
// The pager will remove any stale data
// TODO: G
PageList<AIScheduleValue> list = this.schedulePager.seek(schedule, pc.getPagenum(), pc.getPagesize());
list.setTotalSize(schedule.size());
return list;
}
/**
*
*
*/
@Transactional(readOnly=true)
public AISchedule findScheduleByID(AuthzSubject subject, Integer id) {
return aiScheduleDao.findById(id);
}
/**
* Get a job history based on appdef id
*
*
*
*/
@Transactional(readOnly=true)
public PageList<AIHistory> findJobHistory(AuthzSubject subject, AppdefEntityID id, PageControl pc) throws NotFoundException {
// default the sorting to the date started
pc = PageControl.initDefaults(pc, SortAttribute.CONTROL_STARTED);
Collection<AIHistory> hist;
int sortAttr = pc.getSortattribute();
switch (sortAttr) {
case SortAttribute.CONTROL_STATUS:
hist = aiHistoryDao.findByEntityStatus(id.getType(), id.getID(), pc.isAscending());
break;
case SortAttribute.CONTROL_STARTED:
hist = aiHistoryDao.findByEntityStartTime(id.getType(), id.getID(), pc.isAscending());
break;
case SortAttribute.CONTROL_ELAPSED:
hist = aiHistoryDao.findByEntityDuration(id.getType(), id.getID(), pc.isAscending());
break;
case SortAttribute.CONTROL_DATESCHEDULED:
hist = aiHistoryDao.findByEntityDateScheduled(id.getType(), id.getID(), pc.isAscending());
break;
case SortAttribute.CONTROL_ENTITYNAME:
// No need to sort since all will have the same name
hist = aiHistoryDao.findByEntity(id.getType(), id.getID());
break;
default:
throw new NotFoundException("Unknown sort attribute: " + sortAttr);
}
PageList<AIHistory> list = this.historyPager.seek(hist, pc.getPagenum(), pc.getPagesize());
list.setTotalSize(hist.size());
return list;
}
/**
*
*
*/
@Transactional
public void deleteAIJob(AuthzSubject subject, Integer ids[]) throws AutoinventoryException {
for (int i = 0; i < ids.length; i++) {
try {
AISchedule aiScheduleLocal = aiScheduleDao.findById(ids[i]);
scheduler.deleteJob(aiScheduleLocal.getJobName(), GROUP);
aiScheduleDao.remove(aiScheduleLocal);
} catch (Exception e) {
throw new AutoinventoryException("Unable to remove job: " + e.getMessage());
}
}
}
@Transactional(readOnly=true)
public void checkUniqueName(AIScheduleDAO aiScheduleLocalHome, String scanName) throws DuplicateAIScanNameException {
// Ensure that the name is not a duplicate.
AISchedule aisl = aiScheduleLocalHome.findByScanName(scanName);
if (aisl != null) {
throw new DuplicateAIScanNameException(scanName);
}
return;
}
}