/**
* Abiquo community edition
* cloud management application for hybrid clouds
* Copyright (C) 2008-2010 - Abiquo Holdings S.L.
*
* This application is free software; you can redistribute it and/or
* modify it under the terms of the GNU LESSER GENERAL PUBLIC
* LICENSE as published by the Free Software Foundation under
* version 3 of the License
*
* This software 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
* LESSER GENERAL PUBLIC LICENSE v.3 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
package com.abiquo.abicloud.taskservice.impl.quartz;
import static com.abiquo.abicloud.taskservice.utils.TaskUtils.getName;
import static com.abiquo.abicloud.taskservice.utils.TaskUtils.validateTask;
import java.text.ParseException;
import java.util.Calendar;
import java.util.concurrent.TimeUnit;
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.abiquo.abicloud.taskservice.TaskService;
import com.abiquo.abicloud.taskservice.exception.TaskServiceException;
import com.abiquo.abicloud.taskservice.impl.AbstractTaskService;
import com.abiquo.abicloud.taskservice.impl.quartz.config.InMemoryConfiguration;
import com.abiquo.abicloud.taskservice.impl.quartz.config.QuartzConfiguration;
import com.abiquo.abicloud.taskservice.model.Task;
/**
* Quartz implementation of the {@link TaskService}.
*
* @author ibarrera
*/
public class QuartzTaskService extends AbstractTaskService
{
/**
* The logger.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(QuartzTaskService.class);
/**
* Default group name for task service triggers.
*/
private static final String DEFAULT_GROUP_NAME = "abicloud";
/**
* The configuration for the service.
*/
private QuartzConfiguration config;
/**
* The factory used to obtain {@link Scheduler}.
*/
private SchedulerFactory schedulerFactory;
/**
* The current {@link Scheduler}.
*/
private Scheduler scheduler;
/**
* Creates a new {@link TaskService} with the default configuration.
*
* @throws TaskServiceException If the service cannot be initialized.
*/
public QuartzTaskService() throws TaskServiceException
{
// By default, use a InMemoryConfiguration
this(new InMemoryConfiguration());
}
/**
* Creates a new {@link TaskService} with the specified configuration.
*
* @param config The service configuration.
* @throws TaskServiceException If the service cannot be initialized.
*/
public QuartzTaskService(final QuartzConfiguration config) throws TaskServiceException
{
super();
this.config = config;
// Create scheduler
try
{
schedulerFactory = new StdSchedulerFactory(config.getConfiguration());
scheduler = schedulerFactory.getScheduler();
}
catch (SchedulerException ex)
{
throw new TaskServiceException("Could not initialize "
+ this.getClass().getSimpleName(), ex);
}
// Start the scheduler
try
{
scheduler.start();
}
catch (SchedulerException ex)
{
throw new TaskServiceException("Could not start " + this.getClass().getSimpleName(), ex);
}
LOGGER.info("{} started", this.getClass().getSimpleName());
}
@Override
public void schedule(final Class< ? > taskClass) throws TaskServiceException
{
// Check if is a valid task class
validateTask(taskClass);
// Read task configuration
Task taskConfig = taskClass.getAnnotation(Task.class);
String taskName = getName(taskClass, taskConfig.name(), "Job");
String triggerName = getName(taskClass, taskConfig.name(), "Trigger");
LOGGER.info("Adding task {} to {}", taskName, this.getClass().getSimpleName());
// Create JobDetail Object
JobDetail jobDetail = new JobDetail(taskName, DEFAULT_GROUP_NAME, QuartzTask.class);
jobDetail.getJobDataMap().put(QuartzTask.TASK_CLASS_ATTRIBUTE, taskClass);
// Create Trigger
Trigger trigger = null;
if (!"".equals(taskConfig.cron()))
{
// Create a new cron-based trigger
trigger = createCronTrigger(triggerName, taskConfig.cron(), taskClass);
}
else
{
// Create a new period-based trigger
trigger =
createPeriodicTrigger(triggerName, taskConfig.interval(), taskConfig.startDelay(),
taskConfig.timeUnit());
}
// Schedule job
try
{
scheduler.scheduleJob(jobDetail, trigger);
}
catch (SchedulerException ex)
{
throw new TaskServiceException("Could not schedule task: " + taskClass.getName(), ex);
}
}
@Override
public void unschedule(final Class< ? > taskClass) throws TaskServiceException
{
// Check if is a valid task class
validateTask(taskClass);
// Read task configuration
Task taskConfig = taskClass.getAnnotation(Task.class);
String taskName = getName(taskClass, taskConfig.name(), "Job");
String triggerName = getName(taskClass, taskConfig.name(), "Trigger");
LOGGER.info("Removing task {} from {}", taskName, this.getClass().getSimpleName());
try
{
scheduler.unscheduleJob(triggerName, DEFAULT_GROUP_NAME);
}
catch (SchedulerException ex)
{
throw new TaskServiceException("Could not unschedule task: " + taskClass.getName(), ex);
}
}
@Override
public void shutdown() throws TaskServiceException
{
LOGGER.info("Shutting down {}", this.getClass().getSimpleName());
try
{
scheduler.shutdown();
}
catch (SchedulerException ex)
{
throw new TaskServiceException("Could not shutdown scheduler", ex);
}
}
/**
* Creates a new period-based trigger.
*
* @param triggerName The name of the trigger.
* @param repeatInterval The periodicity in <b>seconds</b>.
* @param startDelay The start delay in <b>seconds</b>.
* @param timeUnit The type of periodicyty.
* @return The period-based trigger.
* @throws TaskServiceException If task cannot be scheduled.
*/
protected Trigger createPeriodicTrigger(final String triggerName, final Integer repeatInterval,
final Integer startDelay, final TimeUnit timeUnit) throws TaskServiceException
{
// Compute start time
Calendar startTime = Calendar.getInstance();
int delay = (startDelay == null) ? 0 : startDelay;
int repeatFactor = 0;
if (timeUnit == TimeUnit.SECONDS)
{
repeatFactor = 1000;
startTime.add(Calendar.SECOND, delay);
}
else
{
repeatFactor = 60 * 1000;
startTime.add(Calendar.MINUTE, delay);
}
startTime.add((timeUnit == TimeUnit.MINUTES) ? Calendar.MINUTE : Calendar.SECOND, delay);
// Create trigger
SimpleTrigger trigger = new SimpleTrigger(triggerName, DEFAULT_GROUP_NAME);
trigger.setStartTime(startTime.getTime());
trigger.setRepeatInterval(repeatInterval * repeatFactor);
trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
return trigger;
}
/**
* Creates a new cron-based trigger.
*
* @param triggerName The name of the trigger.
* @param cronExpression A cron expresion that configures the task execution.
* @param taskClass The class of the target task.
* @return Creates The cron-based trigger.
* @throws TaskServiceException If task cannot be scheduled.
*/
protected Trigger createCronTrigger(final String triggerName, final String cronExpression,
final Class< ? > taskClass) throws TaskServiceException
{
try
{
return new CronTrigger(triggerName, DEFAULT_GROUP_NAME, cronExpression);
}
catch (ParseException ex)
{
throw new TaskServiceException("Invalid cron expression for task: "
+ taskClass.getName(), ex);
}
}
/**
* Gets the config.
*
* @return the config
*/
public QuartzConfiguration getConfig()
{
return config;
}
/**
* Sets the config.
*
* @param config the config to set
*/
public void setConfig(final QuartzConfiguration config)
{
this.config = config;
}
}