Integration of Quartz and Spring: Initial Analysis of Scheduler FactoryBean

Posted by karimali831 on Thu, 01 Aug 2019 06:38:48 +0200

Sharing Knowledge and Delivering Happiness

 

Preface

Quartz is an open source timing scheduling framework that supports cluster deployment. We can use it through its Java API, or we can configure and manage it through Spring, or we can use both ways together. This paper focuses on the initialization process of integrating Quartz 2.2.3 with Spring 4.3.0.RELEASE.

When integrating Scheduler FactoryBean with Spring, you usually need to add Scheduler FactoryBean to the Spring configuration file, for example:

<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="overwriteExistingJobs" value="true"/>
        <property name="configLocation" value="classpath:quartz.properties"/>
    </bean>

Let's look at Scheduler FactoryBean's class definition:

public class SchedulerFactoryBean extends SchedulerAccessor implements FactoryBean<Scheduler>, BeanNameAware,
        ApplicationContextAware, InitializingBean, DisposableBean, SmartLifecycle {

}


It can be seen that it implements common interfaces such as FactoryBean, Bean NameAware, Application ContextAware, Initializing Bean, Disposable Bean, etc. This paper does not elaborate on the specific meaning of these interfaces. What we do not understand is that we can specially study Spring's principle and source code implementation.

According to Spring's principle, if the Bean itself implements the Initializing Bean interface, then after loading the parsing Bean Definition in Spring and initializing the Bean, we will call Scheduler FactoryBean's afterPropertiesSet method, which will only pick out the key code for analysis.

Initialize Scheduler Factory, which is first initialized in the afterProperties Set. The code is as follows:

 // Create SchedulerFactory instance...
SchedulerFactory schedulerFactory = BeanUtils.instantiateClass(this.schedulerFactoryClass);
        initSchedulerFactory(schedulerFactory);

The default value of the property schedulerFactoryClass is StdSchedulerFactory.class, so StdSchedulerFactory is initialized by default. Users can also use Spring's configuration file to modify the value of schedulerFactoryClass to implement other Schedulery interfaces (such as RemoteScheduler or inherit the children of RemoteMBeanScheduler). Class). After instantiating SchedulerFactory using Spring's BeanUtils tool class, the initSchedulerFactory method (see Listing 1) is called to initialize SchedulerFactory.

Listing 1

Initialize Scheduler Factory

private void initSchedulerFactory(SchedulerFactory schedulerFactory) throws SchedulerException, IOException {
        if (!(schedulerFactory instanceof StdSchedulerFactory)) {
            if (this.configLocation != null || this.quartzProperties != null ||
                    this.taskExecutor != null || this.dataSource != null) {
                throw new IllegalArgumentException(
                        "StdSchedulerFactory required for applying Quartz properties: " + schedulerFactory);
            }
            // Otherwise assume that no initialization is necessary...
            return;
        }
 
        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);
    }


Reading the initSchedulerFactory method carefully, you can understand its initialization process as follows:

  • For other Scheduler Factories that are not Std Scheduler Factory, the parameters need to be checked.
  • Set the built-in properties and store them in the mergedProps dictionary. These attributes include:
  • org.quartz.scheduler.classLoadHelper.class: For loading Spring resources when integrating Quartz with Spring;
  • org.quartz.threadPool.class: thread pool that executes Task in Quartz;
  • org.quartz.threadPool.threadCount: The number of threads that execute Task in Quartz.

Loading the properties in the property file specified by the configLocation property and merging them into mergedProps indicates that the configuration in the property file can override the built-in property parameters. Set other properties to mergedProps:

  • org.quartz.jobStore.class: Class for job persistent storage with a value of LocalDataSourceJobStore;
  • org.quartz.scheduler.instanceName: The value is set in the Spring configuration file;
  • Initialize further by calling the initialize method of StdSchedulerFactory, essentially by creating Properties Parser to wrap mergedProps (see Listing 2).

Listing 2

The initialize implementation of Std Scheduler Factory

 public void initialize(Properties props) throws SchedulerException {
        if (propSrc == null) {
            propSrc = "an externally provided properties instance.";
        }
 
        this.cfg = new PropertiesParser(props);
    }


After the initialization of SchdulerFactory, the initialized action will also execute the code in Listing 3. The steps are summarized as follows:

  • Use ThreadLocal technology to hold resourceLoader, taskExecutor, dataSource, non-Transactional DataSource;
  • Call the createScheduler method to create a scheduler (for details, read the article "Integration of Quartz and Spring - Creating a Scheduler";
  • Call populate SchedulerContext to specify the attributes of the Scheduler Context and its Spring ApplicationContext;
  • Set job factory class JobFactory for scheduler.
  • Call the registerListeners method to register listeners for scheduling, jobs, triggers, etc. (see Listing 4).
  • Call the registerJobs AndTriggers method to register jobs and triggers (details will be covered separately in another blog post);

Listing 3

Initialized actions

 this.scheduler = createScheduler(schedulerFactory, this.schedulerName);
    populateSchedulerContext();
 
    if (!this.jobFactorySet && !(this.scheduler instanceof RemoteScheduler)) {
        // Use AdaptableJobFactory as default for a local Scheduler, unless when
        // explicitly given a null value through the "jobFactory" bean property.
        this.jobFactory = new AdaptableJobFactory();
    }
    if (this.jobFactory != null) {
        if (this.jobFactory instanceof SchedulerContextAware) {
            ((SchedulerContextAware) this.jobFactory).setSchedulerContext(this.scheduler.getContext());
        }
        this.scheduler.setJobFactory(this.jobFactory);
    }
     //Omit secondary code
    registerListeners();
    registerJobsAndTriggers();

Listing 4

Registered listener

 protected void registerListeners() throws SchedulerException {
        ListenerManager listenerManager = getScheduler().getListenerManager();
        if (this.schedulerListeners != null) {
            for (SchedulerListener listener : this.schedulerListeners) {
                listenerManager.addSchedulerListener(listener);
            }
        }
        if (this.globalJobListeners != null) {
            for (JobListener listener : this.globalJobListeners) {
                listenerManager.addJobListener(listener);
            }
        }
        if (this.globalTriggerListeners != null) {
            for (TriggerListener listener : this.globalTriggerListeners) {
                listenerManager.addTriggerListener(listener);
            }
        }
    }

summary

For developers familiar with Java, any Java object (basic object and composite object) needs to be initialized before it can be used. Quartz, as a large component, is itself an object. From the class definition of Scheduler FactoryBean, we can see that it makes full use of Spring's various extension interfaces to facilitate the use of Spring's rich functionality in the scheduling context. During the initialization of Scheduler Factory, we see that Scheduler Factory Bean supports multiple injection attributes, which can override the built-in property settings, and users can configure them according to their own needs. Additionally, the configLocation attribute specifies the attribute file, which can configure the attribute in a separate attribute file. When there are many attributes to configure, it can avoid the bloated xml configuration. Adding listeners for scheduling, jobs, triggers, etc. to facilitate interested components to perform some operations when the above content changes. This approach can also decouple the relationship between other components and Scheduler FactoryBean.

 

 

 

 

 

-----------

As the writing time is in a hurry, if not, please leave a message to correct Haihan.

Learn from each other and make progress together

Topics: Spring Attribute Java xml