Yonatan Maman
Yonatan Maman

Reputation: 2508

looking for persistent timers for a spring application

I'm looking for a lib that allow me to do

  1. define a worker that will be invoked once on a specific time in the future (not need the re-schedule / cron like featrure) i.e. a Timer
  2. The worker should accept a context which withe some parameters / inputs
  3. all should be persistent in the DB (or file) the worker
  4. worker should be managed by spring -- spring should instantiate the worker so it can be injected with dependencies
  5. be able to create timers dynamically via API and not just statically via spring XML beans

nice to have:

  1. support a cluster i.e. have several nodes that can host a worker. each store jobn in the DB will cause invokaction of ONE work on one of the nods

I've examined several alternatives none meets the requirements:

when using org.springframework.scheduling.quartz.JobDetailBean makes quartz create your worker instance (and not by spring) so you can't get dependecy ijection, (which will lead me to use Service Locator which I want to avoid)

while using org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean you can't get a context. your Worker expose one public method that accepts no arguments.In addition when using MethodInvokingJobDetailFactoryBean you can't use persistence (form the Javadoc)

Note: JobDetails created via this FactoryBean are not serializable and thus not suitable for persistent job stores. You need to implement your own Quartz Job as a thin wrapper for each case where you want a persistent job to delegate to a specific service method.

I know I can impl thing myself using a DB and Spring (or even JDK) Timers but I prefer to use an a 3r party lib for that.

Any suggestions?

Upvotes: 3

Views: 4005

Answers (3)

Gustav Karlsson
Gustav Karlsson

Reputation: 151

For those interested in an alternative to Quartz, have a look at db-scheduler (https://github.com/kagkarlsson/db-scheduler). A persistent task/execution-schedule is kept in a single database table. It is guaranteed to be executed only once by a scheduler in the cluster.

  1. Yes, see code example below.

  2. Currently limited to a single string identifier for no format restriction. The scheduler will likely be extended in the future with better support for job-details/parameters.

  3. The execution-time and context is persistent in the database. Binding a task-name to a worker is done when the Scheduler starts. The worker may be instantiated by Spring as long as it implements the ExecutionHandler interface.

  4. See 3).

  5. Yes, see code example below.

Code example:

private static void springWorkerExample(DataSource dataSource, MySpringWorker mySpringWorker) {

    // instantiate and start the scheduler somewhere in your application
    final Scheduler scheduler = Scheduler
            .create(dataSource)
            .threads(2)
            .build();
    scheduler.start();

    // define a task and a handler that named task, MySpringWorker implements the ExecutionHandler interface
    final OneTimeTask oneTimeTask = ComposableTask.onetimeTask("my-onetime-task", mySpringWorker);

    // schedule a future execution for the task with a custom id (currently the only form for context supported)
    scheduler.scheduleForExecution(LocalDateTime.now().plusDays(1), oneTimeTask.instance("1001"));
}


public static class MySpringWorker implements ExecutionHandler {
    public MySpringWorker() {
        // could be instantiated by Spring
    }

    @Override
    public void execute(TaskInstance taskInstance, ExecutionContext executionContext) {
        // called when the execution-time is reached
        System.out.println("Executed task with id="+taskInstance.getId());
    }
}

Upvotes: 1

Erez Mazor
Erez Mazor

Reputation: 56

If you want to create the job details to generate triggers/job-details at runtime and still be able to use Spring DI on your beans you can refer to this blog post, it shows how to use SpringBeanJobFactory in conjunction with ObjectFactoryCreatingFactoryBean to create Quartz triggering objects at runtime with Spring injected beans.

Upvotes: 4

vagelis
vagelis

Reputation: 334

Your requirements 3 and 4 do not really make sense to me: how can you have the whole package (worker + work) serialized and have it wake up magically and do its work? Shouldn't something in your running system do this at the proper time? Shouldn't this be the worker in the first place?

My approach would be this: create a Timer that Spring can instantiate and inject dependencies to. This Timer would then load its work / tasks from persistent storage, schedule them for execution and execute them. Your class can be a wrapper around java.util.Timer and not deal with the scheduling stuff at all. You must implement the clustering-related logic yourself, so that only one Timer / Worker gets to execute the work / task.

Upvotes: 0

Related Questions