package org.jboss.soa.esb.services.jbpm.integration.timer;
import java.util.Iterator;
import java.util.List;
import org.apache.log4j.Logger;
import org.hibernate.Hibernate;
import org.jbpm.JbpmConfiguration;
import org.jbpm.JbpmContext;
import org.jbpm.JbpmException;
import org.jbpm.db.JobSession;
import org.jbpm.graph.exe.ProcessInstance;
import org.jbpm.graph.exe.Token;
import org.jbpm.job.Job;
import org.jbpm.job.Timer;
import org.jbpm.scheduler.SchedulerService;
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.Trigger;
/**
* Implementation of the jBPM scheduler service targeting quartz.
*
* @author <a href='kevin.conner@jboss.com'>Kevin Conner</a>
*/
public class QuartzSchedulerService implements SchedulerService
{
/**
* Serial Version UID for this class.
*/
private static final long serialVersionUID = -5573189923313779260L;
/**
* Quartz group name for jBPM tasks.
*/
private static final String JBPM_GROUP = "jBPMGroup" ;
/**
* Name of the timer id within the quartz map.
*/
private static final String TIMER_ID = "id" ;
/**
* Name of the max refire count within the quartz map.
*/
private static final String MAX_REFIRE_COUNT = "maxRefireCount" ;
/**
* The associated quartz scheduler.
*/
private final Scheduler scheduler ;
/**
* The maximum refire count.
*/
private final int maxRefireCount ;
/**
* The associated jBPM job session.
*/
private final JobSession jobSession ;
/**
* Create the scheduler service.
* @param jbpmContext The associated jBPM context.
* @param scheduler The quartz scheduler.
*/
public QuartzSchedulerService(final JbpmContext jbpmContext, final Scheduler scheduler, final int maxRefireCount)
{
this.scheduler = scheduler ;
this.maxRefireCount = maxRefireCount ;
jobSession = jbpmContext.getJobSession();
}
/**
* Create a timer.
* @param timer the timer to create.
*/
public void createTimer(final Timer timer)
{
// Force initialisation of any proxy, JBESB-2720
Hibernate.initialize(timer) ;
jobSession.saveJob(timer) ;
final String name = getTimerName(timer) ;
final Trigger trigger = new SimpleTrigger(name, JBPM_GROUP, timer.getDueDate()) ;
final JobDetail jobDetail = new JobDetail(name, JBPM_GROUP, QuartzSchedulerServiceJob.class) ;
final JobDataMap jobDataMap = new JobDataMap() ;
jobDataMap.put(TIMER_ID, new Long(timer.getId())) ;
jobDataMap.put(MAX_REFIRE_COUNT, Integer.valueOf(maxRefireCount)) ;
jobDetail.setJobDataMap(jobDataMap) ;
try
{
scheduler.scheduleJob(jobDetail, trigger) ;
}
catch (final SchedulerException se)
{
throw new JbpmException("Failed to schedule quartz job", se) ;
}
}
/**
* Delete a specific timer.
* @param timer The timer to delete.
*/
public void deleteTimer(final Timer timer)
{
deleteJob(timer) ;
jobSession.deleteJob(timer) ;
}
/**
* Delete a specific timer given a name and associated jBPM token.
* @param timerName The name of the timer to delete.
* @param token The token associated with the timer.
*/
public void deleteTimersByName(final String timerName, final Token token)
{
final List<Job> jobs = jobSession.findJobsByToken(token) ;
if (jobs != null)
{
for(Job job: jobs)
{
if (job instanceof Timer)
{
final Timer timer = (Timer)job ;
if (timerName.equals(timer.getName()))
{
deleteJob(timer) ;
}
}
}
}
jobSession.deleteTimersByName(timerName, token) ;
}
/**
* Delete timers associated with a jBPM process instance.
* @param processInstance The process instance associated with the timers.
*/
public void deleteTimersByProcessInstance(final ProcessInstance processInstance)
{
final List<?> tokens = processInstance.findAllTokens() ;
if (tokens != null)
{
final Iterator<?> tokenIter = tokens.iterator() ;
while(tokenIter.hasNext())
{
final Token token = (Token)tokenIter.next();
final List<Job> jobs = jobSession.findJobsByToken(token) ;
if (jobs != null)
{
for(Job job: jobs)
{
if (job instanceof Timer)
{
final Timer timer = (Timer)job ;
deleteJob(timer) ;
}
}
}
}
}
jobSession.deleteJobsForProcessInstance(processInstance) ;
}
/**
* Close the scheduler service.
*/
public void close()
{
}
/**
* Generate a name for the timer.
* @param timer the timer.
*/
private String getTimerName(final Timer timer)
{
return Long.toString(timer.getId()) ;
}
/**
* Delete a quartz job associated with a timer.
* @param The timer to delete.
*/
private void deleteJob(final Timer timer)
{
try
{
scheduler.deleteJob(getTimerName(timer), JBPM_GROUP) ;
}
catch (final SchedulerException se)
{
throw new JbpmException("Failed to delete quartz job", se) ;
}
}
/**
* The quartz job associated with executing a jBPM timer.
* @author kevin
*/
public static final class QuartzSchedulerServiceJob implements org.quartz.Job
{
/**
* The logger for this class.
*/
private static final Logger LOGGER = Logger.getLogger(QuartzSchedulerServiceJob.class) ;
/**
* Execute the quartz job.
* @param context The quartz execution context.
*/
public void execute(final JobExecutionContext context)
throws JobExecutionException
{
final JobDetail jobDetail = context.getJobDetail() ;
if (jobDetail != null)
{
final JobDataMap jobDataMap = jobDetail.getJobDataMap() ;
if (jobDataMap != null)
{
final Long jobId = (Long) jobDataMap.get(TIMER_ID) ;
if (jobId != null)
{
final JbpmContext jbpmContext = JbpmConfiguration.getInstance().createJbpmContext() ;
Job job = null ;
try
{
job = jbpmContext.getJobSession().loadJob(jobId) ;
if (job != null)
{
jbpmContext.getServices().getMessageService().send(job) ;
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Firing job " + jobId) ;
}
return ;
}
}
catch (final RuntimeException re)
{
final Integer maxRefireCount = (Integer) jobDataMap.get(MAX_REFIRE_COUNT) ;
if ((maxRefireCount != null) && (maxRefireCount > context.getRefireCount()))
{
final JobExecutionException jobEx = new JobExecutionException(re, true);
jobEx.setErrorCode(JobExecutionException.ERR_JOB_EXECUTION_THREW_EXCEPTION);
throw jobEx ;
}
else
{
if (job != null)
{
// zero the retries to remove from the list of outstanding jobs.
job.setRetries(0) ;
}
throw re ;
}
}
finally
{
jbpmContext.close() ;
}
}
}
}
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Failed to locate job from context " + context) ;
}
}
}
}