君子博学而日参省乎己 则知明而行无过矣

博客园 首页 新随笔 联系 订阅 管理

本文的起因源于一次quartz的异常,在win2003正常运行的程序放在linux环境就抛出异常了,虽然找出异常没花我多长时间,不过由此加深了对quzrtz的了解;古人说,三折肱,为良医,说明经验对于我们平时开发的重要。

quartz是一个任务调度框架,对于开发者而言通常是透明的,如果不熟悉内部机制,碰到问题往往会束手无策;接下来本人本着开放的精神,来阐述本人对quartz的理解。

本人是采用spring对quartz封装的实现,spring的org.springframework.scheduling.quartz.SchedulerFactoryBean类用于初始化Scheduler对象并启动Scheduler对象主线程(通过实现spring的InitializingBean接口和SmartLifecycle接口)

Scheduler对象的初始化在SchedulerFactoryBean的void afterPropertiesSet()方法

步骤:

1 创建SchedulerFactory对象并初始化

2 通过第一步创建Scheduler工厂对象创建scheduler对象并初始化

3 添加配置文件中的相关监听器和触发器等

//---------------------------------------------------------------------    
    // Implementation of InitializingBean interface
    //---------------------------------------------------------------------

    public void afterPropertiesSet() throws Exception {
        //这里省略部分代码
        // Create SchedulerFactory instance.
        SchedulerFactory schedulerFactory = (SchedulerFactory)
                BeanUtils.instantiateClass(this.schedulerFactoryClass);
        //初始化配置属性
        initSchedulerFactory(schedulerFactory);

         //这里省略部分代码

        // Get Scheduler instance from SchedulerFactory.
        try {
            //实例化scheduler对象
            this.scheduler = createScheduler(schedulerFactory, this.schedulerName);
            //初始化scheduler对象的上下文
            populateSchedulerContext();

             //这里省略部分代码
        }

        finally {
           //释放资源
        }

        registerListeners();
        registerJobsAndTriggers();
    }

步骤一用于创建和初始化Scheduler工厂(SchedulerFactory这里默认为Class<?> schedulerFactoryClass = StdSchedulerFactory.class)

initSchedulerFactory(schedulerFactory)方法用于初始化StdSchedulerFactory的配置属性(这些属性用于下一步创建Scheduler对象)

/**
     * 初始化配置属性
     * Load and/or apply Quartz properties to the given SchedulerFactory.
     * @param schedulerFactory the SchedulerFactory to initialize
     */
    private void initSchedulerFactory(SchedulerFactory schedulerFactory)
            throws SchedulerException, IOException {

        //这里省略部分代码

        Properties mergedProps = new Properties();

        if (this.resourceLoader != null) {
            mergedProps.setProperty(StdSchedulerFactory.PROP_SCHED_CLASS_LOAD_HELPER_CLASS,
                    ResourceLoaderClassLoadHelper.class.getName());
        }

        if (this.taskExecutor != null) {
            mergedProps.setProperty(StdSchedulerFactory.PROP_THREAD_POOL_CLASS,
                    LocalTaskExecutorThreadPool.class.getName());
        }
        else {
            // Set necessary default properties here, as Quartz will not apply
            // its default configuration when explicitly given properties.
            mergedProps.setProperty(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, SimpleThreadPool.class.getName());
            mergedProps.setProperty(PROP_THREAD_COUNT, Integer.toString(DEFAULT_THREAD_COUNT));
        }
        
        if (this.configLocation != null) {
            if (logger.isInfoEnabled()) {
                logger.info("Loading Quartz config from [" + this.configLocation + "]");
            }
            PropertiesLoaderUtils.fillProperties(mergedProps, this.configLocation);
        }

        CollectionUtils.mergePropertiesIntoMap(this.quartzProperties, mergedProps);
        

        if (this.dataSource != null) {
            mergedProps.put(StdSchedulerFactory.PROP_JOB_STORE_CLASS, LocalDataSourceJobStore.class.getName());
        }

        // Make sure to set the scheduler name as configured in the Spring configuration.
        if (this.schedulerName != null) {
            mergedProps.put(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME, this.schedulerName);
        }        

        ((StdSchedulerFactory) schedulerFactory).initialize(mergedProps);
    }

步骤二用于创建scheduler对象并初始化,其中创建scheduler对象方法如下

protected Scheduler createScheduler(SchedulerFactory schedulerFactory, String schedulerName)
            throws SchedulerException {

        // Override thread context ClassLoader to work around naive Quartz ClassLoadHelper loading.
        Thread currentThread = Thread.currentThread();
        ClassLoader threadContextClassLoader = currentThread.getContextClassLoader();
        boolean overrideClassLoader = (this.resourceLoader != null &&
                !this.resourceLoader.getClassLoader().equals(threadContextClassLoader));
        if (overrideClassLoader) {
            currentThread.setContextClassLoader(this.resourceLoader.getClassLoader());
        }
        try {
            SchedulerRepository repository = SchedulerRepository.getInstance();
            synchronized (repository) {
                Scheduler existingScheduler = (schedulerName != null ? repository.lookup(schedulerName) : null);
                Scheduler newScheduler = schedulerFactory.getScheduler();
                if (newScheduler == existingScheduler) {
                    throw new IllegalStateException("Active Scheduler of name '" + schedulerName + "' already registered " +
                            "in Quartz SchedulerRepository. Cannot create a new Spring-managed Scheduler of the same name!");
                }
                if (!this.exposeSchedulerInRepository) {
                    // Need to remove it in this case, since Quartz shares the Scheduler instance by default!
                    SchedulerRepository.getInstance().remove(newScheduler.getSchedulerName());
                }
                return newScheduler;
            }
        }
        finally {
            if (overrideClassLoader) {
                // Reset original thread context ClassLoader.
                currentThread.setContextClassLoader(threadContextClassLoader);
            }
        }
    }

该方法里面首先设置当前线程的classload对象,然后调用schedulerFactory.工厂实例化scheduler对象

 /**
     * <p>
     * Returns a handle to the Scheduler produced by this factory.
     * </p>
     *
     * <p>
     * If one of the <code>initialize</code> methods has not be previously
     * called, then the default (no-arg) <code>initialize()</code> method
     * will be called by this method.
     * </p>
     */
    public Scheduler getScheduler() throws SchedulerException {
        if (cfg == null) {
            initialize();
        }

        SchedulerRepository schedRep = SchedulerRepository.getInstance();

        Scheduler sched = schedRep.lookup(getSchedulerName());

        if (sched != null) {
            if (sched.isShutdown()) {
                schedRep.remove(getSchedulerName());
            } else {
                return sched;
            }
        }

        sched = instantiate();

        return sched;
    }

方法Scheduler instantiate()实例化Scheduler对象,这个方法很长很长,其中关键代码如下

        // Get ThreadPool Properties
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        //实例化线程池对象
        String tpClass = cfg.getStringProperty(PROP_THREAD_POOL_CLASS, null);

        if (tpClass == null) {
            initException = new SchedulerException(
                    "ThreadPool class not specified. ",
                    SchedulerException.ERR_BAD_CONFIGURATION);
            throw initException;
        }

        try {
            tp = (ThreadPool) loadHelper.loadClass(tpClass).newInstance();
        } catch (Exception e) {
            initException = new SchedulerException("ThreadPool class '"
                    + tpClass + "' could not be instantiated.", e);
            initException
                    .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
            throw initException;
        }
        tProps = cfg.getPropertyGroup(PROP_THREAD_POOL_PREFIX, true);
        try {
            setBeanProps(tp, tProps);
        } catch (Exception e) {
            initException = new SchedulerException("ThreadPool class '"
                    + tpClass + "' props could not be configured.", e);
            initException
                    .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
            throw initException;
        }

        // Get JobStore Properties
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        //实例化JobStore对象
        String jsClass = cfg.getStringProperty(PROP_JOB_STORE_CLASS,
                RAMJobStore.class.getName());

        if (jsClass == null) {
            initException = new SchedulerException(
                    "JobStore class not specified. ",
                    SchedulerException.ERR_BAD_CONFIGURATION);
            throw initException;
        }

        try {
            js = (JobStore) loadHelper.loadClass(jsClass).newInstance();
        } catch (Exception e) {
            initException = new SchedulerException("JobStore class '" + jsClass
                    + "' could not be instantiated.", e);
            initException
                    .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
            throw initException;
        }

        SchedulerDetailsSetter.setDetails(js, schedName, schedInstId);

        tProps = cfg.getPropertyGroup(PROP_JOB_STORE_PREFIX, true, new String[] {PROP_JOB_STORE_LOCK_HANDLER_PREFIX});
        try {
            setBeanProps(js, tProps);
        } catch (Exception e) {
            initException = new SchedulerException("JobStore class '" + jsClass
                    + "' props could not be configured.", e);
            initException
                    .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
            throw initException;
        }
             //初始化DBConnectionManager dbMgr

                try {
                        PoolingConnectionProvider cp = new PoolingConnectionProvider(
                                dsDriver, dsURL, dsUser, dsPass, dsCnt,
                                dsValidation);
                        dbMgr = DBConnectionManager.getInstance();
                        dbMgr.addConnectionProvider(dsNames[i], cp);
                    } catch (SQLException sqle) {
                        initException = new SchedulerException(
                                "Could not initialize DataSource: " + dsNames[i],
                                sqle);
                        throw initException;
                    }

            //创建QuartzSchedulerResources rsrcs对象
            QuartzSchedulerResources rsrcs = new QuartzSchedulerResources();
            rsrcs.setName(schedName);
            rsrcs.setThreadName(threadName);
            rsrcs.setInstanceId(schedInstId);
            //设置JobRunShellFactory
            rsrcs.setJobRunShellFactory(jrsf);
            rsrcs.setMakeSchedulerThreadDaemon(makeSchedulerThreadDaemon);
            rsrcs.setThreadsInheritInitializersClassLoadContext(threadsInheritInitalizersClassLoader);
            rsrcs.setRunUpdateCheck(!skipUpdateCheck);
            rsrcs.setInterruptJobsOnShutdown(interruptJobsOnShutdown);
            rsrcs.setInterruptJobsOnShutdownWithWait(interruptJobsOnShutdownWithWait);
            rsrcs.setJMXExport(jmxExport);
            rsrcs.setJMXObjectName(jmxObjectName);
    
            
            SchedulerDetailsSetter.setDetails(tp, schedName, schedInstId);
            //设置ThreadPool
            rsrcs.setThreadPool(tp);
            //创建并启动工作线程
            tp.initialize();
            tpInited = true;
            //设置JobStore
            rsrcs.setJobStore(js);
    
            schedCtxt = new SchedulingContext();
            schedCtxt.setInstanceId(rsrcs.getInstanceId());
    
            qs = new QuartzScheduler(rsrcs, schedCtxt, idleWaitTime, dbFailureRetry);
            qsInited = true;
    
            // Create Scheduler ref...
            Scheduler scheduler = instantiate(rsrcs, qs);

            // fire up job store, and runshell factory
    
            js.setInstanceId(schedInstId);
            js.setInstanceName(schedName);
            js.initialize(loadHelper, qs.getSchedulerSignaler());
            
            jrsf.initialize(scheduler, schedCtxt);
            
            qs.initialize();    
        
    
            // prevents the repository from being garbage collected
            qs.addNoGCObject(schedRep);
            // prevents the db manager from being garbage collected
            if (dbMgr != null) {
                qs.addNoGCObject(dbMgr);
            }
    
            schedRep.bind(scheduler);

该方法最终通过调用Scheduler instantiate(QuartzSchedulerResources rsrcs, QuartzScheduler qs)方法创建对象

protected Scheduler instantiate(QuartzSchedulerResources rsrcs, QuartzScheduler qs) {
        SchedulingContext schedCtxt = new SchedulingContext();
        schedCtxt.setInstanceId(rsrcs.getInstanceId());

        Scheduler scheduler = new StdScheduler(qs, schedCtxt);
        return scheduler;
    }

通过StdScheduler的构造函数传入QuartzScheduler sched, SchedulingContext schedCtxt参数对象

这里关键是QuartzScheduler sched参数对象是作为StdScheduler对象的代理身份的,我们调用的StdScheduler对象方法都是间接执行QuartzScheduler sched的相关方法

(修正:StdScheduler对象为QuartzScheduler sched参数对象代理,QuartzScheduler sched参数对象为真实对象)

QuartzScheduler对象的构造方法QuartzSchedulerResources resources参数保存了相关资源的引用,具体QuartzScheduler对象具体逻辑下文再分析

现在回到SchedulerFactoryBean的void afterPropertiesSet()方法,populateSchedulerContext()用于初始化scheduler对象的上下文,这里是保存相关对象的引用,在任务执行方法里面方便调用相关引用对象的方法

/**
     * Expose the specified context attributes and/or the current
     * ApplicationContext in the Quartz SchedulerContext.
     */
    private void populateSchedulerContext() throws SchedulerException {
        // Put specified objects into Scheduler context.
        if (this.schedulerContextMap != null) {
            this.scheduler.getContext().putAll(this.schedulerContextMap);
        }

        // Register ApplicationContext in Scheduler context.
        if (this.applicationContextSchedulerContextKey != null) {
            if (this.applicationContext == null) {
                throw new IllegalStateException(
                    "SchedulerFactoryBean needs to be set up in an ApplicationContext " +
                    "to be able to handle an 'applicationContextSchedulerContextKey'");
            }
            this.scheduler.getContext().put(this.applicationContextSchedulerContextKey, this.applicationContext);
        }
    }

步骤三添加监听器和触发器,这里不具体分析在创建创建Scheduler对象后,接下来就是调用它的启动方法(主线程方法)了

/**
     * Start the Quartz Scheduler, respecting the "startupDelay" setting.
     * @param scheduler the Scheduler to start
     * @param startupDelay the number of seconds to wait before starting
     * the Scheduler asynchronously
     */
    protected void startScheduler(final Scheduler scheduler, final int startupDelay) throws SchedulerException {
        if (startupDelay <= 0) {
            logger.info("Starting Quartz Scheduler now");
            scheduler.start();
        }
        else {
            if (logger.isInfoEnabled()) {
                logger.info("Will start Quartz Scheduler [" + scheduler.getSchedulerName() +
                        "] in " + startupDelay + " seconds");
            }
            Thread schedulerThread = new Thread() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(startupDelay * 1000);
                    }
                    catch (InterruptedException ex) {
                        // simply proceed
                    }
                    if (logger.isInfoEnabled()) {
                        logger.info("Starting Quartz Scheduler now, after delay of " + startupDelay + " seconds");
                    }
                    try {
                        scheduler.start();
                    }
                    catch (SchedulerException ex) {
                        throw new SchedulingException("Could not start Quartz Scheduler after delay", ex);
                    }
                }
            };
            schedulerThread.setName("Quartz Scheduler [" + scheduler.getSchedulerName() + "]");
            schedulerThread.setDaemon(true);
            schedulerThread.start();
        }
    }

首先是延迟指定时间,然后在线程对象里面调用主线程方法

--------------------------------------------------------------------------- 

本系列quartz源码解析系本人原创

作者 博客园 刺猬的温驯 

邮箱 chenying998179(爬虫绕道)163.com

本文链接 http://www.cnblogs.com/chenying99/p/3151850.html

本文版权归作者所有,未经作者同意,严禁转载及用作商业传播,否则将追究法律责任。

posted on 2013-06-24 06:33  刺猬的温驯  阅读(7179)  评论(3编辑  收藏  举报