package org.jboss.seam.async;
import static org.jboss.seam.annotations.Install.BUILT_IN;
import java.io.InputStream;
import java.rmi.server.UID;
import java.text.ParseException;
import java.util.Date;
import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.Destroy;
import org.jboss.seam.annotations.Install;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.Startup;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.intercept.InvocationContext;
import org.jboss.seam.log.LogProvider;
import org.jboss.seam.log.Logging;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
/**
* Dispatcher implementation that uses the Quartz library.
*
* @author Michael Yuan
*
*/
@Startup
@Scope(ScopeType.APPLICATION)
@Name("org.jboss.seam.async.dispatcher")
@Install(value=false, precedence=BUILT_IN)
@BypassInterceptors
public class QuartzDispatcher extends AbstractDispatcher<QuartzTriggerHandle, Schedule>
{
private static final LogProvider log = Logging.getLogProvider(QuartzDispatcher.class);
private Scheduler scheduler;
@Create
public void initScheduler() throws SchedulerException
{
StdSchedulerFactory schedulerFactory = new StdSchedulerFactory();
//TODO: magical properties files are *not* the way to config Seam apps!
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("seam.quartz.properties");
if (is != null)
{
schedulerFactory.initialize(is);
log.debug("Found seam.quartz.properties file. Using it for Quartz config.");
}
else
{
schedulerFactory.initialize();
log.debug("No seam.quartz.properties file. Using in-memory job store.");
}
scheduler = schedulerFactory.getScheduler();
scheduler.start();
}
public QuartzTriggerHandle scheduleAsynchronousEvent(String type, Object... parameters)
{
String jobName = nextUniqueName();
String triggerName = nextUniqueName();
JobDetail jobDetail = new JobDetail(jobName, null, QuartzJob.class);
jobDetail.getJobDataMap().put("async", new AsynchronousEvent(type, parameters));
SimpleTrigger trigger = new SimpleTrigger(triggerName, null);
try
{
scheduler.scheduleJob(jobDetail, trigger);
return new QuartzTriggerHandle(triggerName);
}
catch (Exception se)
{
log.error("Cannot Schedule a Quartz Job", se);
throw new RuntimeException(se);
}
}
public QuartzTriggerHandle scheduleTimedEvent(String type, Schedule schedule, Object... parameters)
{
return scheduleWithQuartzServiceAndWrapExceptions( schedule, new AsynchronousEvent(type, parameters) );
}
public QuartzTriggerHandle scheduleInvocation(InvocationContext invocation, Component component)
{
return scheduleWithQuartzServiceAndWrapExceptions( createSchedule(invocation), new AsynchronousInvocation(invocation, component) );
}
private static Date calculateDelayedDate (long delay)
{
Date now = new Date ();
now.setTime(now.getTime() + delay);
return now;
}
private QuartzTriggerHandle scheduleWithQuartzServiceAndWrapExceptions(Schedule schedule, Asynchronous async)
{
try
{
return scheduleWithQuartzService(schedule, async);
}
catch (ParseException pe)
{
throw new RuntimeException(pe);
}
catch (SchedulerException se)
{
throw new RuntimeException(se);
}
}
private QuartzTriggerHandle scheduleWithQuartzService(Schedule schedule, Asynchronous async) throws SchedulerException, ParseException
{
String jobName = nextUniqueName();
String triggerName = nextUniqueName();
JobDetail jobDetail = new JobDetail(jobName, null, QuartzJob.class);
jobDetail.getJobDataMap().put("async", async);
if (schedule instanceof CronSchedule)
{
CronSchedule cronSchedule = (CronSchedule) schedule;
CronTrigger trigger = new CronTrigger (triggerName, null);
trigger.setCronExpression(cronSchedule.getCron());
trigger.setEndTime(cronSchedule.getFinalExpiration());
if ( cronSchedule.getExpiration()!=null )
{
trigger.setStartTime (cronSchedule.getExpiration());
}
else if ( cronSchedule.getDuration()!=null )
{
trigger.setStartTime (calculateDelayedDate(cronSchedule.getDuration()));
}
scheduler.scheduleJob( jobDetail, trigger );
}
else if (schedule instanceof TimerSchedule)
{
TimerSchedule timerSchedule = (TimerSchedule) schedule;
if (timerSchedule.getIntervalDuration() != null)
{
if ( timerSchedule.getExpiration()!=null )
{
SimpleTrigger trigger = new SimpleTrigger(triggerName, null,
timerSchedule.getExpiration(),
timerSchedule.getFinalExpiration(),
SimpleTrigger.REPEAT_INDEFINITELY,
timerSchedule.getIntervalDuration());
scheduler.scheduleJob( jobDetail, trigger );
}
else if ( timerSchedule.getDuration()!=null )
{
SimpleTrigger trigger = new SimpleTrigger(triggerName, null,
calculateDelayedDate(timerSchedule.getDuration()),
timerSchedule.getFinalExpiration(), SimpleTrigger.REPEAT_INDEFINITELY,
timerSchedule.getIntervalDuration());
scheduler.scheduleJob( jobDetail, trigger );
}
else
{
SimpleTrigger trigger = new SimpleTrigger(triggerName, null, new Date(),
timerSchedule.getFinalExpiration(),
SimpleTrigger.REPEAT_INDEFINITELY,
timerSchedule.getIntervalDuration());
scheduler.scheduleJob( jobDetail, trigger );
}
}
else
{
if ( schedule.getExpiration()!=null )
{
SimpleTrigger trigger = new SimpleTrigger (triggerName, null, schedule.getExpiration());
scheduler.scheduleJob(jobDetail, trigger);
}
else if ( schedule.getDuration()!=null )
{
SimpleTrigger trigger = new SimpleTrigger (triggerName, null,
calculateDelayedDate(schedule.getDuration()));
scheduler.scheduleJob(jobDetail, trigger);
}
else
{
SimpleTrigger trigger = new SimpleTrigger(triggerName, null);
scheduler.scheduleJob(jobDetail, trigger);
}
}
}
else
{
throw new IllegalArgumentException("unrecognized schedule type");
}
return new QuartzTriggerHandle(triggerName);
}
private String nextUniqueName ()
{
return (new UID()).toString();
}
@Destroy
public void destroy() throws SchedulerException
{
scheduler.shutdown();
}
public static class QuartzJob implements Job
{
private Asynchronous async;
public QuartzJob() { }
public void execute(JobExecutionContext context)
throws JobExecutionException
{
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
async = (Asynchronous)dataMap.get("async");
QuartzTriggerHandle handle = new QuartzTriggerHandle(context.getTrigger().getName());
try
{
async.execute(handle);
}
catch (Exception e)
{
async.handleException(e, handle);
}
}
}
public Scheduler getScheduler()
{
return scheduler;
}
public static QuartzDispatcher instance()
{
return (QuartzDispatcher) AbstractDispatcher.instance();
}
}