我正在尝试在Grails 2.2.4中采用Quartz插件(:quartz:1.0.1),并试图弄清楚如何允许开发和测试使用与生产中所需的不同的计划而不必更改部署到每个代码的代码。
以下是我的经验。
我正在使用JDBC JobStore和Quartz Monitor插件(:quartz-monitor:1.0)。所以我的工作定义如下:
class TestJob {
static triggers = {
cron name: 'testTrigger', startDelay: 1000, cronExpression: '0 0/1 * * * ?'
}
...
}
当我运行Grails应用程序时,会为作业设置触发器,存储在数据库中,然后开始执行。如果我进入并通过Quartz Monitor对cron表达式进行有意更改,则会反映在数据库和执行计划中。
如果我现在重新启动应用程序,则触发器将更改回作业中定义的内容。因此,使用这种方法,我几乎坚持使用触发器块中的任何内容。如果我完全从代码中删除触发器块,那么数据库定义就会保持不变并控制计划,这似乎是一种改进。
所以我认为在Job中没有定义任何触发器是有意义的,但这让我试图弄清楚如何首先加载触发器并且不会覆盖有意的更改。似乎在配置中添加一个块(可能在外部配置文件中)来定义触发器是有意义的。据我所知,我需要编写一些东西来解析它在启动时(在BootStrap?中)并通过Quartz API应用它。
我的所有文档阅读和谷歌搜索都缺少这样的东西吗?或许我可能会以更为根本的方式处于错误状态。
使用一些实施细节进行更新
汉斯给了我一些关于什么应该适用于我的情况的想法。
我最终关闭了JDBC作业存储,因为我认为配置应该是触发的权限。我将作业/触发器信息放入一个看起来像这样的外部配置文件。
quartzJobs: [
'TestJob': [
cronTriggers: [
cronExpression: '0 0 7 ? * 2-6'
]
]
]
然后我在BootStrap中调用了一些看起来像这样的代码。
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)
}
}
}
}
答案 0 :(得分:3)
您可以将配置放在Config.groovy
或从grails.config.locations
读取的属性文件中。
然后在你BootStrap.groovy
你可以这样做:
TestJob.schedule(grailsApplication.config.cronExpression)
在此cronExpression
中是您在属性文件中选择的属性的名称。
有关可用的不同Job.schedule()
方法,请参阅http://grails-plugins.github.io/grails-quartz/guide/triggers.html。
答案 1 :(得分:2)
您可以执行以下操作以根据当前环境更改触发器:
static triggers = {
if (Environment.current == Environment.TEST) {
simple repeatInterval: 5000l
}
else {
simple repeatInterval: 1000l
}
}
<强>更新强>
汉斯解决方案可能有点容易,但是如果它已经存在,那么另一个不会重新创建触发器。 :)
我重命名了triggers
块,以便插件找不到任何触发器。
package stackoverflow
class MyJob {
static defaultTriggers = {
cron name: 'testTrigger', startDelay: 1000, cronExpression: '0 0/1 * * * ?'
}
....
}
然后我重用QuartzGrailsPlugin.groovy
中的代码从defaultTriggers
闭包创建触发器,如果它还不存在则安排它。
通过查看doWithApplicationContext
中的QuartzGrailsPlugin.groovy
,扩展代码以循环遍历所有作业应该不会太困难。
虽然有点吵。我认为最好在服务中隐藏并从Bootstrap
调用它而不是内联。
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
}
}
答案 2 :(得分:1)
它可能不是您所追求的,但您可以在作业的execute()
方法中添加条件语句。此条件可用于基于当前Grails环境以及单独定义的cron表达式跳过执行。作为一个非常简单的例子:
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 }
// ...
}
}
答案 3 :(得分:0)
对不起,为时已晚。上面的答案是正确的。但是我有需要从配置中拉取句点的情况。
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()}"
}