Java 使用 Quartz 只运行一次作业

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2710555/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 11:08:51  来源:igfitidea点击:

Running a Job only once Using Quartz

javaquartz-scheduler

提问by Kaddy

Is there a way I could run a job only once using Quartz in Java? I understand it does not make sense to use Quartz in this case. But, the thing is, I have multiple jobs and they are run multiple times. So, I am using Quartz.

有没有办法在 Java 中使用 Quartz 只运行一次作业?我知道在这种情况下使用 Quartz 没有意义。但是,问题是,我有多个工作,并且它们运行了多次。所以,我正在使用石英。

Is this even possible?

这甚至可能吗?

采纳答案by Marko Lahma

You should use SimpleTrigger that fires at specific time and without repeating. TriggerUtils has many handy methods for creating these kind of things.

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

回答by jelies

In quartz > 2.0, you can get the scheduler to unschedule any job after work is done:

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

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

where triggerKey is the ID of the job to run only once. After this, the job wouldn't be called anymore.

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

回答by Puchacz

I'm not sure how much similar is Quartz in Mono and Java but this seems working in .Net

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

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

回答by splash

Here is an example of how to run a TestJobclass immediately with Quartz 2.x:

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

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;
}

see also Quartz Enterprise Job Scheduler TutorialsSimpleTriggers

另见Quartz Enterprise Job Scheduler 教程SimpleTriggers

回答by Rodrigo Villalba Zayas

Yes, it's possible!

是的,这是可能的!

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

回答by Robert

Another solution: There is a method .withRepeatCount(0)in SimpleSchedulerBuilder:

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

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();

回答by Andrew Cotton

I had to ask myself if it made sense to try to configure a job and add checks if it had been run already as suggested in Marko Lahma's answer (since scheduling a job to run once results in it being run once, every time we start the app). I found examples of CommandLineRunner apps which didn't quite work for me, mostly because we already had an ApplicationRunner which was used for other jobs which use Quartz scheduling / cron. I wasn't happy with having Quartz initialize this job using a SimpleTrigger, so I had to find something else.

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

Using some ideas from the following articles:

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

I was able to piece together a working implementation which allows me to do the following:

我能够拼凑一个工作实现,它允许我执行以下操作:

  • run existing jobs via Quartz, on a timer
  • run new job, one time programmatically (single use Quartz job using the SimpleTrigger didn't satisfy my requirements, since it would be run once on every application load)
  • 在计时器上通过 Quartz 运行现有作业
  • 以编程方式运行一次新作业(使用 SimpleTrigger 的单次使用 Quartz 作业不能满足我的要求,因为它会在每次应用程序加载时运行一次)

I came up with the following CommandLineRunner class:

我想出了以下 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;
}
}

In one of my configuration classes I added a CommandLineRunner bean which calls the custom CommandLineRunner I created:

在我的一个配置类中,我添加了一个 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;
    }
}

Later, I am able to initiate these jobs via the CLI without affecting my current Quartz scheduled jobs, and as long as no one runs the command via CLI multiple times, it will never be run again. I have to do some juggling of types since I accept ApplicationArguments, and then convert them into String[].

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

Finally, I am able to call it like this:

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

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

The result is that the job is initialized only when I call it, and it is excluded from my CronTriggerFactoryBean triggers which I used for my other jobs.

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

There are several assumptions being made here, so I'll try to summarize:

这里有几个假设,所以我将尝试总结:

  • the job must be registered as a JobDetailFactoryBean (e.g.: scheduler.setJobDetails(...))
  • everything is essentially the same as a job with CronTriggerFactoryBean, excepting the lacking scheduler.setTriggers(...)call
  • Spring knows to execute the CommandLineRunner classes after the application has booted
  • I hardcoded the parameter being passed into the application to "jobName"
  • I assumed a group name of "DEFAULT" for all jobs; if you want to use differing groups this would need to be adjusted when fetching JobKey, which is used to actually run the job
  • there is nothing which prevents this job from being run multiple times via CLI, but it was triggered on every application load using SimpleTrigger approach, so this is better for me; if this is not acceptable, perhaps using StepListener and ExitStatus, etc. can prevent it from being executed twice
  • 这项工作必须注册为一个JobDetailFactoryBean(如:scheduler.setJobDetails(...)
  • 除了缺少scheduler.setTriggers(...)调用之外,一切都与 CronTriggerFactoryBean 的工作基本相同
  • Spring 知道在应用程序启动后执行 CommandLineRunner 类
  • 我将传递到应用程序的参数硬编码为“jobName”
  • 我假设所有作业的组名称为“DEFAULT”;如果你想使用不同的组,这需要在获取 JobKey 时进行调整,它用于实际运行作业
  • 没有什么可以阻止此作业通过 CLI 多次运行,但它是使用 SimpleTrigger 方法在每次应用程序加载时触发的,所以这对我来说更好;如果这是不能接受的,也许使用 StepListener 和 ExitStatus 等可以防止它被执行两次