R Dub
R Dub

Reputation: 688

Java - Timer class that accepts a time source

I need a replacement of java.util.Timer or java.util.concurrent.ScheduledExecutorService that accepts a time source so that it can be driven off of system time or some other reference. I use this timer to periodically execute a method, but need the period to be driven by system time when the application is in "live" mode and some other source when the application is in "playback" mode.

I did see that there are some 3rd party libraries such as guava stopwatch with functionality similar to what I am asking, but I prefer something native to Java due to the policies of the information system I am working on.

I may end up having to write my own, but am asking here since this seems like a need that many would have... Either when working with applications that have alternate concepts of time passage or for testing of units with embedded timers.

Upvotes: 0

Views: 378

Answers (2)

R Dub
R Dub

Reputation: 688

In case anyone else needs it, here is the solution I ended up with.

public interface TimeSource {
    long currentTimeMillis();
}

public class AlternateTimeSource implements TimeSource {

    long currentTime = 0;       

    public AlternateTimeSource() {
    }

    public setTime(long time) {
        if (time > currentTime) currentTime = time;
    }

    @Override
    public long currentTimeMillis() {
        return currentTime;
    }
}

public class SystemTimeSource implements TimeSource {

    @Override
    public long currentTimeMillis() {
        return System.currentTimeMillis();
    }
}

class SourcedTimerThread extends Thread {

    TimeSource reference_;
    TimerTask task_;
    long period_;
    long executionTime_;
    boolean done = false;

    SourcedTimerThread(TimeSource reference,TimerTask task,long delay,long period) {
        reference_     = reference;
        task_          = task;
        period_        = period >= 0 ? period : 0;
        executionTime_ = reference_.currentTimeMillis() + (delay >= 0 ? delay : 0);
        setName("SourcedTimerThread_"+reference_.getClass().getSimpleName());
        start();
    }

    public void cancel() {
        done = true;
        interrupt();
    }

    public void run() {

        long currentTime;

        while (!done) {
            try {
                currentTime = reference_.currentTimeMillis();
                if (executionTime_>currentTime) { 
                    // Task hasn't yet fired; wait
                    sleep(1);                   }
                else {
                    task_.run();
                    if (period_ > 0) {
                        // Repeating task, reschedule
                        executionTime_ = ((period_ < 0) ? currentTime-period_ : executionTime_ + period_);
                    }
                    else {
                        // Non-repeating task, done
                        break;
                    }
                }
            } catch (InterruptedException e) {
            }
        }
    }
}

Usage:

// Build source
TimeSource source = null;
if (alternate) {
    source = new AlternateTimeSource();
}
else {
    source = new SystemTimeSource();
}

// Configure timer
SourcedTimerThread t = new SourcedTimerThread(source,new TimerTask() {
    @Override
    void run() {
        System.out.println("Yay!");
    }
}, delayMs, periodMs);

// Drive timer if necessary
if (alternate) {
    // Drive timer based on alternate source such as data
    source.setTime(dataRecord.getTime());
}

// When ready to cleanup
t.cancel();

Upvotes: 0

Rahul
Rahul

Reputation: 21114

You can implement the java.util.concurrent.ScheduledExecutorService with a custom time source, by doing the following:

Compute the offset or the delta between the custom time source and system time (at initialization), and hold on to the delta in your implementation of ScheduledExecutorService. When schedule (and similar methods in the interface) are called, use the same offset that you computed at initialization and apply those offsets on the TimeUnits being provided. That way - you can rely on the existing implementation of the scheduler.

Upvotes: 2

Related Questions