0

I am designing a java web app using jersey in which i having a date time field, "future_time" in mysql db. I am also a having a status field in the same table set to pending. I want to update the status field to completed in db if the current time >= future_time. It is like I want to schedule a event.

Secondly if current_ time < future_time and if i updated the future_time value, i would i like to reschedule the previous event.

Currently, whenever i fetch status field i check if it is pending, if it is pending then i check if future_time<= current_time, if so then i update the status field in db to completed. But there is a problem in this. This will only update the status field when i fetch it. So if i fetch the status field after a long time(after future_time) , then it will be update at that instant, saving incorrect records in db.

Can anyone suggest me a better way to achieve this?

thinkgear
  • 1
  • 2

2 Answers2

0

You can use ScheduledExecutorService for scheduling the tasks at specific interval of time, after some fixed delays or at some specific point in time.

You can create the ScheduledExecutorService like:

ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();

And then use the service for scheduling the jobs like:

//scheduling at fixed time.
Future<String> resultFuture = executorService.schedule(new Callable<String>() {
    @Override
    public String call() throws Exception
    {
        //your code;
    }
}, 1, TimeUnit.SECONDS);

//scheduling at fixed intervals.
executorService.scheduleAtFixedRate(new Runnable() {
    @Override
    public void run()
    {
        //your code
    }
}, 100, 450, TimeUnit.MILLISECONDS);

//scheduling at fixed delays.
executorService.scheduleWithFixedDelay(new Runnable() {
    @Override
    public void run()
    {
        //your code
    }
}, 100, 150, TimeUnit.MILLISECONDS);

You can also use Java Timer Utility to schedule the tasks for the future but Java specification says to prefer the ScheduledExecutorService. Below is a sample code for scheduling the jobs with Timer.

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Timer;
import java.util.TimerTask;


/**
 * An example for Java Timer Utility.
 *
 * @author Karan Khanna.
 */
public class TimerExample
{

    public static void scheduleTaskForFuture(final Date date)
    {
        Timer timer = new Timer("Timer");
        TimerTask task = new TimerTask()
        {
            public void run()
            {

                System.out.println(
                    String
                        .format("Task performed on: %s and Thread's name: %s", date, Thread.currentThread().getName()));

                timer.cancel();
            }
        };
        timer.schedule(task, date);
    }



    public static void main(final String[] args)
    {
        Calendar calendar = new GregorianCalendar();
        calendar.add(Calendar.MINUTE, 1);
        TimerExample.scheduleTaskForFuture(calendar.getTime());
    }
}

You can explore the utility more to schedule repeated tasks. Also, don't forget to cancel the task once the required job is done.

You can also try other frameworks like Quartz and Spring scheduling for the purpose.

Karan Khanna
  • 1,947
  • 3
  • 21
  • 49
  • 1
    The [documentation for `Timer`](https://docs.oracle.com/javase/10/docs/api/java/util/Timer.html) recommends using the [Executor framework](https://docs.oracle.com/javase/tutorial/essential/concurrency/executors.html) instead. – Basil Bourque Oct 31 '18 at 22:07
0

Calculate elapsed time until deadline

With JDBC 4.2 and later, retrieve the date-time using the modern java.time classes.

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class );  // Likely to be a UTC value.

Get current moment.

OffsetDateTime now = OffsetDateTime.now( ZoneOffset.UTC ) ;

Calculate elapsed time.

Duration d = Duration.between( now , odt ) ;

Schedule event

Use the modern Executor framework rather than the legacy Timer class. This is especially true in a Servlet or Jakarta.ee (Java EE) environment.

Define your executor, specifically a ScheduledExecutorService. We need only a single thread for this task.

ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor()

Ask that executor to wait a certain amount of time, then run your code to check the status in the database.

myExecutorService.schedule(           // Tell the executor what you want to run, and when you want it run on your behalf in a background thread.
    new Runnable() { … } ,            // Define task to be executed as a `Runnable`.
    d.toSeconds() ,                   // Amount of time to wait until execution. Use self-documenting code rather than a “magic number” such as `86400000`. 
    TimeUnit.SECONDS                  // Specify the granularity of time used in previous pair of arguments.
)                                     // Returns a `ScheduledFuture` which you may want to cache.

Optionally, you can capture the ScheduledFuture returned by this schedule statement. You can then monitor completion status.

In your Runnable, retrieve the current status record from your database. If warranted, schedule another execution, re-calculating time to wait using a new current moment.

Servlet web container

For a Servlet web container such as Tomcat or Jetty without the full Jakarta.ee stack, the above discussion applies.

Furthermore, you will want to write a class that implements the ServletContextListener interface where you can set-up and tear-down your executor service. You need to gracefully tear-down the executor service so that its background thread does not continue merrily on its way long after your web app has shutdown.

Jakarta EE

If in a full-blown Jakarta.ee stack such as Glassfish, this work is much easier.

The executor service seen above can be automated, with set-up and tear-down performed on your behalf. Use the concurrency utilities defined in JSR 236: Concurrency Utilities for JavaTM EE. This has been covered many times, so search Stack Overflow for more info.


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.

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

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

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.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • If running this on a Servlet Web Container, is there any way to save information about the scheduled event, so that I can cancel it if necessary? Or is the only way to do this, as you mentioned, to simply check the status of the record at the time that the event is triggered? – theyuv Mar 08 '19 at 17:03
  • @theyuv Read the class JavaDoc, and notice the `schedule` method returns a [`ScheduledFuture`](https://docs.oracle.com/javase/10/docs/api/java/util/concurrent/ScheduledFuture.html), as mentioned in my Answer. Use that object to monitor completion and to cancel if need be. – Basil Bourque Mar 08 '19 at 17:27
  • Thanks. Yes, I am aware of `ScheduledFuture`. I run a web app. I schedule events days in advance. Sometimes, for unexpected reasons (eg: user initiated) I need to cancel one of these events. I don't want to persist the ScheduledFuture in memory for this long. How can I cancel a scheduled event in such as situation? – theyuv Mar 08 '19 at 17:35
  • @theyuv Save the reference to your `ScheduledFuture` object, then later call it’s `cancel` method. I do not understand your unwillingness to keep the reference to this object. Only a few bytes of memory are involved. – Basil Bourque Mar 08 '19 at 18:02
  • I didn't think it was good practice to store such things globally. Is this still the case if I have hundreds/thousands? – theyuv Mar 08 '19 at 18:15
  • 1
    Sounds fine to me. I expect the `ScheduledFuture` objects take very little memory, just a hook into the existing scheduled runnable task. Of course you should be monitoring your Servlet container at runtime and make sure the server has more than enough memory available. – Basil Bourque Mar 08 '19 at 22:30