Rescheduling a job scheduled with scheduledExecutorService

Hi,
In my spring mvc application. I am scheduling a job using scheduledExecutorService.
here is code :
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
            SimpleJob simpleJob = new SimpleJob(guid);
            ScheduledFuture scheduledFuture = scheduledExecutorService.schedule(simpleJob,
                    Calendar.getInstance().getTimeInMillis() - calendar.getTimeInMillis()
                    , TimeUnit.MILLISECONDS);
        }

Open in new window


Take for example that above a job is scheduled after 1000000 milliseconds.
Now it may happen that my web application gets a http request saying that reschdule the job for some guid to 2000 milliseconds.
guid is a string i am passing above in the job which identifies a particular job.

Now if the job is already running or has run... i can ignore the request of recheduling.
But suppose it has yet to run.. In that case i want to rechdule it...

How to do it ?

Thanks
Rohit BajajAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Moussa MokhtariEnterpreneurCommented:
@Rohit Bajaj the class pendingTaskRunner  takes as arg a specific LocalDateTime to run a specific task
I added also the ReSchedule Option to be able to ReSchedule your task

 
import java.time.LocalDateTime;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Paradox
 */
public class Heart {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {

        LocalDateTime showdate = LocalDateTime.now().plusSeconds(30);
        
        try {
            pendingTaskRunner pending = new pendingTaskRunner(showdate, new Runnable() {
                @Override
                public void run() {
                    System.out.println("Task is done !,bye");
                }
            });
            pending.start();
            pending.reSchedule(showdate.minusSeconds(10));
        } catch (SecurityException ex) {
            Logger.getLogger(Heart.class.getName()).log(Level.SEVERE, null, ex);
        }

    }
}

Open in new window



import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Paradox
 */
public class pendingTaskRunner {

    LocalDateTime date;
    ScheduledFuture<?> pendingTask;
    Runnable func;

    public pendingTaskRunner(LocalDateTime ExcuteDate, Runnable func) {
        this.date = ExcuteDate;
        this.func = func;
    }
    public void start() {
        pendingTask = Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
            public void run() {
                LocalDateTime local = LocalDateTime.now();
                int duration = date.compareTo(local);
                System.out.println("Not Yet");
                if (duration == 0 || duration < 0 ) {
                    try {
                        func.run();
                        pendingTask.cancel(true);
                    } catch (Exception ex) {
                        Logger.getLogger(Heart.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        }, 0, 10, TimeUnit.SECONDS);
    }
     public void stop() {
         pendingTask.cancel(true);
     }
     public void reSchedule(LocalDateTime redate){
         this.date = redate;
         System.out.println("ReSchedule");
     }
}

Open in new window

dpearsonCommented:
Rohit,

I think one way to reschedule the task is to store the Future that you are returned and if you need to reschedule the task, cancel the future and submit a new task with the correct delay.

So roughly like this:

private final Map<String, ScheduledFuture> map = new HashMap<> ;

// Then when you schedule the task, hold onto the Future:
ScheduledFuture scheduledFuture = scheduledExecutorService.schedule(...) ;
map.put(guid.toString(), scheduledFuture) ;

Open in new window


Now when later you wish to reschedule:
ScheduledFuture scheduledFuture = map.get(guid.toString()) ;
if (!scheduledFuture.isDone()) {
    scheduledFuture.cancel() ;

    // Now schedule it again at the correct time
    ScheduledFuture newScheduledFuture = scheduledExecutorService.schedule(...) ;
    map.put(guid.toString(), newScheduledFuture) ;
}

Open in new window


Hope that helps,

Doug
mccarlIT Business Systems Analyst / Software DeveloperCommented:
To handle your general use case (which you've now asked several questions about) I would actually go another way, and NOT do it with anything like ScheduledExecutors, or at least not in the way you are using them.

Firstly, one of your questions mentioned that you have info stored in a table about the messages that you need to send (or something like that). Since you are already using a DB, my suggestion would be to setup a table in the DB that has date/time as a column and details of the task as another column, you could even have the GUID that you describe above as another column so that you can easily find an existing task again. Then you insert a row for each scheduled task that needs to run with the date/time column set to the time when it needs to run.

Then all you need is a basic ScheduledExecutor that runs somewhere between every second (if it really does need to be that accurate, but I doubt it) or every minute, etc. And this then checks the database table to see if there are any rows with date/time LESS THAN the current date/time, if so it does whatever the details in that row instruct it to do, ie. send a message, etc. and then deletes the row (or at least marks it as processed).


This has several advantages with requirements that you have mentioned over your many questions, ie. for rescheduling like you mention in this question, you simply update the date/time in the table row. This also works easily for having multiple tasks waiting to run at different times (or timeszones) as you just have multiple rows in the table, each with the correct SERVER date/time for the task to run, etc.

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Java

From novice to tech pro — start learning today.