使用 Quartz 仅运行一次作业

发布于 2024-08-30 14:09:06 字数 118 浏览 6 评论 0原文

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

这可能吗?

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.

Is this even possible?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

他不在意 2024-09-06 14:09:06

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

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

鲜肉鲜肉永远不皱 2024-09-06 14:09:06

是的,这是可能的!

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

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
长途伴 2024-09-06 14:09:06

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

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

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

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

@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.

蛮可爱 2024-09-06 14:09:06

以下是如何使用 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 教程简单触发器

Here is an example of how to run a TestJob class immediately with 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

撩发小公举 2024-09-06 14:09:06

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

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

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

TriggerBuilder.Create ()
        .StartNow ()
        .Build (); 
著墨染雨君画夕 2024-09-06 14:09:06

我不得不问自己,尝试配置一个作业并添加检查是否已经按照 Marko Lahma 的答案中的建议运行是否有意义(因为安排一个作业运行一次会导致它运行一次,每次我们启动该作业时)应用程序)。我发现 CommandLineRunner 应用程序的示例不太适合我,主要是因为我们已经有一个 ApplicationRunner,它用于其他使用 Quartz 调度/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等可以防止它被执行两次

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.

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)

I came up with the following CommandLineRunner class:

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:

@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[].

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.

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
二手情话 2024-09-06 14:09:06

另一种解决方案: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();

Another solution: There is a method .withRepeatCount(0) in 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();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文