Sai shanthan
Sai shanthan

Reputation: 21

Scheduling a job based on inserted time

I have a use case where, I will insert a delivery and it's delivery time in database. Exact at the delivery time the delivery(which is a string) has to be printed to console. Any pointers how to achieve this?

Upvotes: 0

Views: 949

Answers (3)

Basil Bourque
Basil Bourque

Reputation: 340040

ScheduledExecutorService with java.time

The Answer by user2880879 using ScheduledExecutorService would be my chosen approach. But that Answer uses the terrible date-time classes Calendar & Date which were supplanted years ago by the modern java.time classes.

Instant now = Instant.now() ;

Retrieve delivery moment from database column of a data type akin to the SQL-standard type TIMESTAMP WITH TIME ZONE. JDBC 4.2 requires support for OffsetDateTime.

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;

Check that you got a non-null value.

Objects.requireNonNull( odt ) ;  // Throws exception if null.

Check that your retrieved moment is in the future.

if( ! odt.toInstant().isAfter( now ) ) { … handle error }

Tip: You might also want to be sure the future delivery time as at least a certain amount of time in the future, time enough for this code to run and the executor service to execute.

Extract an Instant from that OffsetDateTime.

Instant then = odt.toInstant() ;

Calculate elapsed time as a Duration.

Duration duration = Duration.between( now , then ) ;

Define a task, as a Runnable or Callable.

Runnable runnable = () -> { 
    System.out.println( "Delivery made at: " + Instant.now() ) ;
};

Somewhere earlier in your app you would have instantiated a ScheduledExecutorService, and kept a reference. Using the Executors utility class is the usual way to get an executor service.

For this purpose, we likely want a single-threaded service. You can optionally create a service backed by a pool of multiple threads.

ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor() ;

Schedule our task to run after our duration of time elapses.

ses.schedule( runnable , duration.toSeconds() , TimeUnit.SECONDS ) ;

Tips:

  • Be sure to always gracefully shut down your executor service. Otherwise, the thread(s) backing that service may continue zombie-like long after they are needed, or even after your app has quit.
  • Wrap the work done inside your Runnable with a try-catch. Any exception bubbling up will silently prevent any more tasks scheduled on that executor service from ever executing.

Jakarta Concurrency

If using a Jakarta compliant server supporting the Jakarta Concurrency specification, then this work is even easier. That spec relieves you of the chore of starting, managing, and shutting down your executor service.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes. Hibernate 5 & JPA 2.2 support java.time.

Where to obtain the java.time classes?

Upvotes: 1

TongChen
TongChen

Reputation: 1430

Assume a microservices who has more than one instance responsible for print delivery at delivery time and you want only one instance work at the same time:

First method

The answer from @Jalil.Jarjanazy is can help you.code may be like this:

define job:

public class PrintDeliveryJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // print job
    }
}

define save delivery method:

public void saveDelivery(String deliveryName, Date deliveryDate) throws Exception {
    saveDeliveryToDataBase(deliveryName, deliveryDate);

    JobDetail jobDetail = JobBuilder.newJob()
        .ofType(PrintDeliveryJob.class)
        .storeDurably().withIdentity(JobKey.jobKey(deliveryName))
        .withDescription("Invoke " + deliveryName + " Job service...").build();
    // you need to calculate the expression,it may be a hard work

    CronExpression cronExpression = null;
    Trigger trigger = TriggerBuilder.newTrigger()
        .withIdentity(TriggerKey.triggerKey(deliveryName))
        .withDescription(deliveryName + " trigger")
        .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionFireAndProceed()
            .inTimeZone(TimeZone.getDefault()))
        .forJob(jobDetail)
        .build();
    scheduler.scheduleJob(jobDetail, trigger);
}


public void saveDeliveryToDataBase(String deliveryName, Date deliveryDate) {
    // save to your data base like mysql、mongodb or other data base
}

Second method

1、define a job who get data from data base and check if need to print delivery or not.

2、use quartz define a schedule task execute the job one times per second.

Upvotes: 0

user2880879
user2880879

Reputation: 297

Or you can use JDK's ScheduledExecutorService

    ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    Calendar futureTime = Calendar.getInstance();
    //Here set time when you want to perform logic , display message etc.
    futureTime.add(Calendar.SECOND, 5);
    Date now = new Date();
    long runafter = now.getTime() - futureTime.getTime().getTime();
    // Within this you can write logic to perform action on that future time
    Runnable runnable = () -> System.out.println("Hello, execute your logic here");
    //This is one of schedule , will execute your logic defined in Runnable 
    scheduler.schedule(runnable, runafter, MICROSECONDS);

Upvotes: 0

Related Questions