How to configure Quartz triggers in Grails to allow different schedules for testing and production

白昼怎懂夜的黑 提交于 2019-12-10 09:53:40

问题


I am trying to adopt the Quartz plugin (:quartz:1.0.1) in Grails 2.2.4 and am trying to figure out how to allow development and testing to use a different schedule than what is desired in production without having to change the code deployed to each.

Here is my experience.

I am using a JDBC JobStore and the Quartz Monitor plugin (:quartz-monitor:1.0) too. So I have a job defined like this:

class TestJob {
    static triggers = {
        cron name: 'testTrigger', startDelay: 1000, cronExpression: '0 0/1 * * * ?'
    }
    ...
}

When I run the Grails application the trigger is set up for the job, stored in the database, and starts executing. If I go in and make an intentional change to the cron expression through the Quartz Monitor it is reflected in the database and the execution schedule.

If I now restart the application the trigger is changed back to what is defined in the job. So using this approach I am pretty much stuck with whatever is in the triggers block for the job. If I remove the triggers block from the code altogether then the database definitions go unaltered and control the schedule which seems like an improvement.

So I think it makes sense to not define any triggers in the Job, but that leaves me trying to figure out how to load the triggers up in the first place and in a way that does not overwrite intentional changes. It seems like adding a block to the config (probably in an external config file) for defining triggers would make sense. As far as I can tell I would need to write something to parse this at start-up (in BootStrap?) and apply it through the Quartz API.

Does something like this already exist that am missing in all of my documentation reading and googling? Or maybe I am wrongheaded in a more fundamental way.

Update with some implementation details

Hans gave me some ideas about what should work for my situation.

I ended up turning off the JDBC job store because I decided the config should be the authority for triggering. I put the job/trigger information into an external config file that looks like this.

quartzJobs: [
    'TestJob': [
        cronTriggers: [
            cronExpression: '0 0 7 ? * 2-6'
        ]
    ]
]

Then I have some code called in BootStrap that looks like this.

def jobs = grailsApplication.config.quartzJobs
if (jobs) {
    jobs.each { job, details ->
        List triggers = (details?.cronTriggers instanceof Map) ? [details.cronTriggers]: details.cronTriggers
        if (triggers) {
            def j = grailsApplication.mainContext.getBean(job)
            triggers.each { trigger ->
                String cronExpression = trigger.cronExpression ?: '1 1 1 1 1 ? 2099'
                j.schedule(cronExpression)
            }
        }
    }
}

回答1:


You can put the configuration in your Config.groovy or in a properties file that gets read from grails.config.locations.

And then in you BootStrap.groovy you can do:

TestJob.schedule(grailsApplication.config.cronExpression)

In this cronExpression is the name of the property you chose in the properties file.

See http://grails-plugins.github.io/grails-quartz/guide/triggers.html for the different Job.schedule() methods that are available.




回答2:


You can do the following to change the trigger based on the current environment:

static triggers = {
    if (Environment.current == Environment.TEST) {
        simple repeatInterval: 5000l
    }
    else {
        simple repeatInterval: 1000l
    }
}

Update

Hans solution is probably a bit easier but here is another one that does not re-create the trigger if it already exists. :)

I renamed the triggers block so that the plugin does not find any trigger.

package stackoverflow

class MyJob {
    static defaultTriggers = {
        cron name: 'testTrigger', startDelay: 1000, cronExpression: '0 0/1 * * * ?'
    }
....
}

Then I reused code from QuartzGrailsPlugin.groovy to create the trigger from the defaultTriggers closure and schedule it if it does not yet exist.

By looking at doWithApplicationContext in QuartzGrailsPlugin.groovy it shouldn't be too difficult to extend the code to loop over all jobs.

It is a little bit noisy though. I think it is best to hide it in a service and call that from Bootstrap instead of doing it inline.

Bootstrap.groovy

import grails.plugins.quartz.CustomTriggerFactoryBean
import grails.plugins.quartz.GrailsJobClassConstants
import grails.plugins.quartz.config.TriggersConfigBuilder
import org.quartz.JobKey
import org.quartz.Trigger
import stackoverflow.MyJob

class BootStrap {
    def quartzScheduler

    def init = { servletContext ->

        def builder = new TriggersConfigBuilder(MyJob.canonicalName)
        Map triggers = builder.build MyJob.defaultTriggers

        triggers.each { name, Expando descriptor ->
            Trigger trigger = createTrigger (descriptor, MyJob.canonicalName)

            if (! quartzScheduler.checkExists(trigger.getKey ())) {
                quartzScheduler.scheduleJob (trigger)
            }
        }
    }

    Trigger createTrigger (Expando descriptor, String jobName) {
        CustomTriggerFactoryBean factory = new CustomTriggerFactoryBean()
        factory.triggerClass = descriptor.triggerClass
        factory.triggerAttributes = descriptor.triggerAttributes
        factory.jobDetail = quartzScheduler.getJobDetail (new JobKey (jobName, GrailsJobClassConstants.DEFAULT_GROUP))
        factory.afterPropertiesSet()
        factory.object
    }
}



回答3:


It may not be quite what you're after, but you could add a conditional statement within your job's execute() method. This conditional could be used to skip execution based on the current Grails environment along with a separately-defined cron expression. As a very simple example:

import grails.util.Environment
import org.quartz.CronExpression

class TestJob {
    CronExpression testExp = new CronExpression("0 0/5 * * * ?")  // could be moved to config...
    // ...
    def execute() {
       // in non-prod environments, return immediately unless the current date & time matches our "test" cron expression
       if (Environment.current != Environment.PRODUCTION && !testExp.isSatisfiedBy(new Date()) { return }

       // ...

    }
}



回答4:


I'm sorry if too late. Answers above are correct. However I've got case when need to pull period from config.

class ImportJob {


static triggers = {
    final Long period = Long.valueOf(Holders.grailsApplication.getConfig().grails.your.config.value as String)
    simple repeatInterval: period
}

def execute() {
    log.debug "E starting processing exposures at:${new Date()}"

}


来源:https://stackoverflow.com/questions/24417215/how-to-configure-quartz-triggers-in-grails-to-allow-different-schedules-for-test

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!