org.jboss.processFlow.knowledgeService.QuartzSchedulerService.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.processFlow.knowledgeService.QuartzSchedulerService.java

Source

/*
 * Copyright 2012 JBoss Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.processFlow.knowledgeService;

import java.util.Collection;
import java.util.Date;

import javax.naming.Context;
import javax.naming.InitialContext;

import org.apache.commons.lang.StringUtils;
import org.drools.common.InternalWorkingMemory;
import org.drools.common.Scheduler.ActivationTimerJobContext;
import org.drools.impl.StatefulKnowledgeSessionImpl;
import org.drools.time.AcceptsTimerJobFactoryManager;
import org.drools.time.InternalSchedulerService;
import org.drools.time.Job;
import org.drools.time.JobContext;
import org.drools.time.JobHandle;
import org.drools.time.TimerService;
import org.drools.time.Trigger;
import org.drools.time.impl.CronExpression;
import org.drools.time.impl.CronTrigger;
import org.drools.time.impl.DefaultJobHandle;
import org.drools.time.impl.DefaultTimerJobFactoryManager;
import org.drools.time.impl.IntervalTrigger;
import org.drools.time.impl.TimerJobFactoryManager;
import org.drools.time.impl.TimerJobInstance;
import org.drools.time.SessionClock;
import org.jbpm.process.instance.timer.TimerManager.ProcessJobContext;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.jboss.processFlow.knowledgeService.IBaseKnowledgeSession;
import org.jboss.processFlow.util.GlobalQuartzJobHandle;

/**
 * Quartz based <code>GlobalSchedulerService</code> that is configured according
 * to Quartz rules and allows to store jobs in data base. With that it survives 
 * server crashes and operates as soon as service is initialized without session 
 * being active.
 *
 */
public class QuartzSchedulerService
        implements TimerService, InternalSchedulerService, AcceptsTimerJobFactoryManager, SessionClock {

    public static final String TIMER_JOB_HANDLE = "timerJobHandle";
    public static final String JOB_GROUP = "jbpm";
    public static final String PROCESS_JOB = "ProcessJob";
    public static final String ACTIVATION_TIMER_JOB = "ActivationTimerJob";
    public static final String RULE_NAME_START_PREFIX = "RuleFlow-Start-";

    private static final Logger log = LoggerFactory.getLogger(QuartzSchedulerService.class);

    // Quartz Scheduler
    private static Scheduler scheduler;

    // For purposes of PER_PROCESS_INSTANCE architecture, will never populate the drools TimerJobFactoryManager
    // subsequently, the drools session will no longer include the job trigger
    // the job trigger will always be managed by this implementation's Quartz scheduler
    private static TimerJobFactoryManager jobFactoryManager = DefaultTimerJobFactoryManager.instance;

    // used to signal the process instance
    private static IBaseKnowledgeSession kSessionProxy;

    public static void start() throws Exception {
        Context jndiContext;
        jndiContext = new InitialContext();
        kSessionProxy = (IBaseKnowledgeSession) jndiContext.lookup(IBaseKnowledgeSession.BASE_JNDI);
        scheduler = StdSchedulerFactory.getDefaultScheduler();
        scheduler.start();
    }

    public static void stop() throws Exception {
        scheduler.shutdown();
        scheduler = null;
    }

    public JobHandle scheduleJob(Job job, JobContext ctx, Trigger droolsTrig) {
        GlobalQuartzJobHandle quartzJobHandle = null;
        if (ctx instanceof ProcessJobContext) {
            // seems to be used with timers using drools timer expression 
            ProcessJobContext processCtx = (ProcessJobContext) ctx;
            StatefulKnowledgeSessionImpl wM = (StatefulKnowledgeSessionImpl) processCtx.getKnowledgeRuntime();
            String jobname = PROCESS_JOB + "-" + processCtx.getProcessInstanceId() + "-"
                    + processCtx.getTimer().getId();
            quartzJobHandle = new GlobalQuartzJobHandle(jobname, JOB_GROUP, wM.getId());
        } else if (ctx instanceof ActivationTimerJobContext) {
            // seems to be used with timers using cron expression
            InternalWorkingMemory wM = (InternalWorkingMemory) ((ActivationTimerJobContext) ctx).getAgenda()
                    .getWorkingMemory();
            //String processId = ((ActivationTimerJobContext) ctx).getScheduledAgendaItem().
            String ruleName = ((ActivationTimerJobContext) ctx).getScheduledAgendaItem().getRule().getName();
            String processId = StringUtils.substringAfter(ruleName, RULE_NAME_START_PREFIX);
            String jobname = ACTIVATION_TIMER_JOB + "-" + processId + "-" + wM.getId();
            quartzJobHandle = new GlobalQuartzJobHandle(jobname, JOB_GROUP, wM.getId());
        } else {
            throw new RuntimeException("scheduleJob() unknown jobContext = " + ctx);
        }

        try {
            org.quartz.Trigger triggerObj = configureJobHandleAndQuartzTrigger(quartzJobHandle, droolsTrig);

            // Define a quartz job detail instance and add jobHandle to its Map
            JobDetail jdetail = new JobDetail(quartzJobHandle.getJobName(), quartzJobHandle.getJobGroup(),
                    QuartzJob.class);
            jdetail.getJobDataMap().put(TIMER_JOB_HANDLE, quartzJobHandle);

            if (null == scheduler.getJobDetail(quartzJobHandle.getJobName(), quartzJobHandle.getJobGroup())) {
                scheduler.scheduleJob(jdetail, triggerObj);
            } else {
                // need to add the job again to replace existing especially important if jobs are persisted in db
                log.warn(
                        "scheduleJob() uh-oh!!!! this job was already scheduled : " + quartzJobHandle.getJobName());
                scheduler.addJob(jdetail, true);
                triggerObj.setJobName(quartzJobHandle.getJobName());
                triggerObj.setJobGroup(quartzJobHandle.getJobGroup());
                scheduler.rescheduleJob(quartzJobHandle.getJobName() + "_trigger", quartzJobHandle.getJobGroup(),
                        triggerObj);
            }

            // drools legacy:   triggers are not to be stored in drools sessions
            //TimerJobInstance jobInstance = this.jobFactoryManager.createTimerJobInstance( job, ctx, trigger, quartzJobHandle, this);
            //this.jobFactoryManager.addTimerJobInstance( timerJobInstance );

            // drools legacy:   return a DefaultJobHandle
            return new DefaultJobHandle(0L);
        } catch (Exception x) {
            throw new RuntimeException(x);
        }
    }

    private static org.quartz.Trigger configureJobHandleAndQuartzTrigger(GlobalQuartzJobHandle jHandle,
            org.drools.time.Trigger droolsTrig) throws Exception {
        org.quartz.Trigger quartzTrig = null;
        String jName = jHandle.getJobName() + "_trigger";
        String jGroup = jHandle.getJobGroup();
        if (droolsTrig instanceof IntervalTrigger) {
            IntervalTrigger iTrig = (IntervalTrigger) droolsTrig;
            Date firstFire = iTrig.hasNextFireTime();

            // drools repeatLimit = quartz repeatCount
            int repeatCount = iTrig.getRepeatLimit();

            long interval = iTrig.getPeriod();
            if (interval <= 0)
                repeatCount = 0;
            quartzTrig = new SimpleTrigger(jName, jGroup, firstFire, iTrig.getEndTime(), repeatCount, interval);
            jHandle.setInterval(interval);
            jHandle.setTimerExpression(iTrig.toString());
        } else if (droolsTrig instanceof CronTrigger) {
            CronTrigger cTrigger = (CronTrigger) droolsTrig;
            CronExpression cExpression = cTrigger.getCronEx();
            //Date onlyFire = cTrigger.getNextFireTime();
            //quartzTrig = new SimpleTrigger(jName, jGroup, onlyFire);  // fires one time and does not repeat
            quartzTrig = new org.quartz.CronTrigger(jName, jGroup, cExpression.getCronExpression());
        } else {
            throw new RuntimeException(
                    "configureJobHandleAndQuartzTrigger() need to implement appropriate handling of the following type of trigger : "
                            + droolsTrig.getClass().toString());
        }
        return quartzTrig;
    }

    public static boolean deleteJob(GlobalQuartzJobHandle jobHandle) {
        try {
            return scheduler.deleteJob(jobHandle.getJobName(), jobHandle.getJobGroup());
        } catch (Exception x) {
            throw new RuntimeException(x);
        }
    }

    public static String getCurrentTimerJobsAsJson(String jobGroup) throws SchedulerException {
        StringBuilder sBuilder = new StringBuilder("{\n\t\"jobs\":[\n\t");
        String[] jobNames = scheduler.getJobNames(jobGroup);
        if (jobNames.length > 0) {
            int x = 0;
            for (String name : jobNames) {
                if (x > 0)
                    sBuilder.append(",\n\t");
                // show the initial jobDetail for now.  could expand by also listing current triggers
                JobDetail jDetail = scheduler.getJobDetail(name, jobGroup);
                GlobalQuartzJobHandle jHandle = (GlobalQuartzJobHandle) jDetail.getJobDataMap()
                        .get(TIMER_JOB_HANDLE);
                sBuilder.append("\t{\"jName\":\"");
                sBuilder.append(name);
                sBuilder.append("\":\"timer\":\"");
                sBuilder.append(jHandle.getTimerExpression());
                sBuilder.append("\"");
                x++;
            }
        }
        sBuilder.append("\n\t]\n}");
        return sBuilder.toString();
    }

    public static int purgeCurrentTimerJobs(String jobGroup) throws SchedulerException {
        String[] jobNames = scheduler.getJobNames(jobGroup);
        for (String name : jobNames) {
            scheduler.deleteJob(name, jobGroup);
        }
        return jobNames.length;
    }

    public static class QuartzJob implements org.quartz.Job {

        public QuartzJob() {
        }

        public void execute(JobExecutionContext qContext) throws JobExecutionException {
            GlobalQuartzJobHandle jHandle = (GlobalQuartzJobHandle) (qContext.getMergedJobDataMap()
                    .get(QuartzSchedulerService.TIMER_JOB_HANDLE));
            if (qContext.getNextFireTime() == null)
                jHandle.setInterval(0L);
            int pState = kSessionProxy.processJobExecutionContext(qContext);
            /*  could delete job in quartz scheduler;  but does not seem to be a use case where this is required.
             *  quartz will flush its scheduler of a job once that job no longer has a nextFireTime
            if(ProcessInstance.STATE_COMPLETED == pState || jHandle.getInterval() == 0){
            boolean dSuccess = deleteJob(jHandle);
            log.info("execute() just signaled.  pState = "+pState+" : jobName = "+jHandle.getJobName()+" : successful deletion = "+dSuccess);
            }else {
            log.info("execute() just signaled.  pState = "+pState+" : jobName = "+jHandle.getJobName());
            }
            */
        }
    }

    /*  ***********************                    LEGACY  FUNCTIONS        *******************************/
    public boolean removeJob(JobHandle jobHandle) {
        /*  NOTE:  BRMS5.3.1, as part of disposing of a session, this function will be invoked.
        Subsequently, timer will never fire.
        Will move this functionality to a 'deleteJob' function that is invoked when needed
         */
        return true;
    }

    public void shutdown() {
        //log.warn("shutdown() function not valid");
    }

    public void internalSchedule(TimerJobInstance timerJobInstance) {
        // not needed, all scheduling functionality is handled in:  scheduleJob(...)
    }

    public void forceShutdown() {
        //log.warn("forceShutdown() function not valid");
    }

    public synchronized void initScheduler() {
        // will handle this in start() function
    }

    /* 
    public JobHandle buildJobHandleForContext(NamedJobContext ctx) {
    return null;
    }
    */

    @Override
    public long getCurrentTime() {
        return System.currentTimeMillis();
    }

    @Override
    public long getTimeToNextJob() {
        return 0;
    }

    @Override
    public Collection<TimerJobInstance> getTimerJobInstances() {
        return jobFactoryManager.getTimerJobInstances();
    }

    @Override
    public TimerJobFactoryManager getTimerJobFactoryManager() {
        return this.jobFactoryManager;
    }

    @Override
    public void setTimerJobFactoryManager(TimerJobFactoryManager arg0) {
        this.jobFactoryManager = arg0;
    }
}