/**
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.waveprotocol.wave.client.scheduler;
import org.waveprotocol.wave.client.scheduler.Scheduler.IncrementalTask;
import org.waveprotocol.wave.model.util.Preconditions;
/**
* Closure of a schedule command that provides idempotent rescheduling.
*
* NOTE(user): All the use cases of rescheduling I've seen want idempotent
* behaviour. Perhaps that should be the default semantics for the scheduler?
*
*/
public final class IdempotentScheduler implements IncrementalTask {
/**
* A debug interface for measuring the efficiency with which the wrapped task
* executes.
*/
public interface EfficiencyRecorder {
void startWhole();
void endWhole();
void startUnit();
void endUnit();
}
/**
* A no-op implementation of the recorder.
*/
@SuppressWarnings("unused")
private final static EfficiencyRecorder NO_RECORDING = new EfficiencyRecorder() {
@Override
public void startWhole() {
}
@Override
public void endWhole() {
}
@Override
public void startUnit() {
}
@Override
public void endUnit() {
}
};
private final EfficiencyRecorder recorder;
private final TimerService scheduler;
private final IncrementalTask task;
private final int interval;
public static class Builder {
private EfficiencyRecorder recorder = NO_RECORDING;
private TimerService timer;
private int interval = 0;
public Builder with(EfficiencyRecorder recorder) {
this.recorder = recorder;
return this;
}
public Builder with(TimerService timer) {
this.timer = timer;
return this;
}
public Builder with(int interval) {
this.interval = interval;
return this;
}
public IdempotentScheduler build(IncrementalTask task) {
Preconditions.checkNotNull(task, "task must not be null");
// NOTE(user): this must be lazily created to avoid test dependency on SchedulerInstance.
if (timer == null) {
timer = SchedulerInstance.getLowPriorityTimer();
}
return new IdempotentScheduler(timer, task, recorder, interval);
}
}
private IdempotentScheduler(TimerService scheduler, IncrementalTask task,
EfficiencyRecorder recorder, int interval) {
this.scheduler = scheduler;
this.task = task;
this.recorder = recorder;
this.interval = interval;
}
public static Builder builder() {
return new Builder();
}
/**
* Cancels this task.
*/
public void cancel() {
if (scheduler.isScheduled(this)) {
scheduler.cancel(this);
recorder.endWhole();
}
}
/**
* Schedules this task, if not already scheduled.
*/
public void schedule() {
if (!scheduler.isScheduled(this)) {
scheduler.scheduleRepeating(this, interval, interval);
recorder.startWhole();
}
}
@Override
public boolean execute() {
recorder.startUnit();
boolean shouldContinue = task.execute();
recorder.endUnit();
if (!shouldContinue) {
recorder.endWhole();
}
return shouldContinue;
}
@Override
public String toString() {
return "IdempotentScheduler - wrapping: " + task.toString();
}
}