仅使用Quartz运行一次作业

问题描述 投票:6回答:7

有没有办法在Java中使用Quartz只运行一次作业?我知道在这种情况下使用Quartz是没有意义的。但事实是,我有多个工作,他们多次运行。所以,我正在使用Quartz。

这甚至可能吗?

java quartz-scheduler
7个回答
12
投票

您应该使用在特定时间触发而不重复的SimpleTrigger。 TriggerUtils有许多方便的方法来创建这些东西。


4
投票

在quartz> 2.0中,您可以让调度程序在工作完成后取消计划任何作业:

@Override
protected void execute(JobExecutionContext context)
            throws JobExecutionException {
    ...
    // process execution
    ...
    context.getScheduler().unscheduleJob(triggerKey);
    ...
}

其中triggerKey是仅运行一次的作业的ID。在此之后,不再调用该作业。


4
投票

是的,这是可能的!

JobKey jobKey = new JobKey("testJob");
JobDetail job = newJob(TestJob.class)
            .withIdentity(jobKey)
            .storeDurably()
            .build();
scheduler.addJob(job, true);
scheduler.triggerJob(jobKey); //trigger a job inmediately

3
投票

以下是如何使用Quartz 2.x立即运行TestJob类的示例:

public JobKey runJob(String jobName)
{
    // if you don't call startAt() then the current time (immediately) is assumed.
    Trigger runOnceTrigger = TriggerBuilder.newTrigger().build();
    JobKey jobKey = new JobKey(jobName);
    JobDetail job = JobBuilder.newJob(TestJob.class).withIdentity(jobKey).build();
    scheduler.scheduleJob(job, runOnceTrigger);
    return jobKey;
}

另见Quartz Enterprise Job Scheduler TutorialsSimpleTriggers


2
投票

我不确定在Mono和Java中有多少相似的Quartz,但这似乎在.Net中有效

TriggerBuilder.Create ()
        .StartNow ()
        .Build (); 

1
投票

我不得不问自己是否有意义尝试配置一个工作并添加检查是否已按照Marko Lahma的回答中的建议运行(因为安排一个工作运行一次导致它运行一次,每次我们开始应用程序)。我发现CommandLineRunner应用程序的例子并不适用于我,主要是因为我们已经有一个ApplicationRunner用于其他使用Quartz scheduling / cron的作业。我不满意Quartz使用SimpleTrigger初始化这个工作,所以我必须找到别的东西。

使用以下文章中的一些想法:

我能够将一个工作实现拼凑在一起,这使我可以执行以下操作:

  • 通过Quartz在计时器上运行现有作业
  • 以编程方式运行一次新作业(单次使用SimpleTrigger的Quartz作业不满足我的要求,因为它会在每次应用程序加载时运行一次)

我想出了以下CommandLineRunner类:

public class BatchCommandLineRunner implements CommandLineRunner {

@Autowired
private Scheduler scheduler;

private static final Logger LOGGER = LoggerFactory.getLogger(BatchCommandLineRunner.class);

public void run(final String... args) throws SchedulerException {

    LOGGER.info("BatchCommandLineRunner: running with args -> " + Arrays.toString(args));

    for (final String jobName : args) {

        final JobKey jobKey = findJobKey(jobName);
        if (jobKey != null) {

            LOGGER.info("Triggering job for: " + jobName);
            scheduler.triggerJob(jobKey);

        } else {

            LOGGER.info("No job found for jobName: " + jobName);
        }

    }
}

private JobKey findJobKey(final String jobNameToFind) throws SchedulerException {

    for (final JobKey jobKey : scheduler.getJobKeys(GroupMatcher.jobGroupEquals("DEFAULT"))) {

        final String jobName = jobKey.getName();

        if (jobName.equals(jobNameToFind)) {

            return jobKey;
        }
    }
    return null;
}
}

在我的一个配置类中,我添加了一个CommandLineRunner bean,它调用我创建的自定义CommandLineRunner:

@Configuration
public class BatchConfiguration {

    private static final Logger LOGGER = LoggerFactory.getLogger(BatchConfiguration.class);

    @Bean
    public BatchCommandLineRunner batchCommandLineRunner() {

        return new BatchCommandLineRunner();
    }

    @Bean
    public CommandLineRunner runCommandLineArgs(final ApplicationArguments applicationArguments) throws Exception {

        final List<String> jobNames = applicationArguments.getOptionValues("jobName");

        LOGGER.info("runCommandLineArgs: running the following jobs -> " + ArrayUtils.toString(jobNames));

        batchCommandLineRunner().run(jobNames.toArray(ArrayUtils.EMPTY_STRING_ARRAY));

        return null;
    }
}

稍后,我可以通过CLI启动这些作业,而不会影响我当前的Quartz预定作业,只要没有人通过CLI多次运行命令,它就永远不会再运行。因为我接受ApplicationArguments,然后将它们转换为String [],我必须做一些类型的杂耍。

最后,我可以这样称呼它:

java -jar <your_application>.jar --jobName=<QuartzRegisteredJobDetailFactoryBean>

结果是只有在我调用它时才初始化作业,并且它被排除在我用于其他作业的CronTriggerFactoryBean触发器之外。

这里有几个假设,所以我将总结一下:

  • 该作业必须注册为JobDetailFactoryBean(例如:scheduler.setJobDetails(...)
  • 一切都与CronTriggerFactoryBean的工作基本相同,除了缺少scheduler.setTriggers(...)电话
  • Spring知道在应用程序启动后执行CommandLineRunner类
  • 我将传递给应用程序的参数硬编码为“jobName”
  • 我为所有工作假设了一个名为“DEFAULT”的组名;如果要使用不同的组,则需要在获取JobKey时进行调整,JobKey用于实际运行作业
  • 没有什么可以阻止这个作业通过CLI多次运行,但它是使用SimpleTrigger方法在每个应用程序加载时触发的,所以这对我来说更好;如果这是不可接受的,可能使用StepListener和ExitStatus等可以防止它被执行两次

0
投票

另一种解决方案:SimpleSchedulerBuilder中有一个方法.withRepeatCount(0)

public final int TEN_SECONDS = 10;
Trigger trigger = newTrigger()
    .withIdentity("myJob", "myJobGroup")
    .startAt(new Date(System.currentMillis()+TEN_SECONDS*1000)
    .withSchedule(SimpleScheduleBuilder.simpleSchedule()
      .withRepeatCount(0)
      .withIntervalInMinutes(1))
    .build();
© www.soinside.com 2019 - 2024. All rights reserved.