/*
* Copyright (C) 2012 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 interactivespaces.util.process.restart;
import interactivespaces.system.InteractiveSpacesEnvironment;
import interactivespaces.time.TimeProvider;
import interactivespaces.util.InteractiveSpacesUtilities;
import java.util.concurrent.ScheduledExecutorService;
/**
* A {@link RestartStrategy} which will attempt a number of restarts.
*
* @param <T>
* the type of {@link Restartable}
*
* @author Keith M. Hughes
*/
public class LimitedRetryRestartStrategy<T extends Restartable> extends BaseRestartStrategy<T> {
/**
* The number of retries to attempt.
*/
private final int numberRetries;
/**
* The delay between samples, in milliseconds.
*/
private final long sampleDelay;
/**
* The time after something starts for it to be considered a successful
* restart, in milliseconds.
*/
private final long timeDurationSuccessfulRestart;
/**
* The space environment.
*/
private final InteractiveSpacesEnvironment spaceEnvironment;
/**
* @param numberRetries
* the total number of retries which can be attempted
* @param sampleDelay
* the time between samples for restart detection, in milliseconds
* @param timeDurationSuccessfulRestart
* the time the item needs to be running before restart is considered
* a success, in milliseconds
* @param spaceEnvironment
* the space environment to pick up services from
*/
public LimitedRetryRestartStrategy(int numberRetries, long sampleDelay, long timeDurationSuccessfulRestart,
InteractiveSpacesEnvironment spaceEnvironment) {
this.numberRetries = numberRetries;
this.sampleDelay = sampleDelay;
this.timeDurationSuccessfulRestart = timeDurationSuccessfulRestart;
this.spaceEnvironment = spaceEnvironment;
}
@Override
public RestartStrategyInstance<T> newInstance(T restartable) {
LimitedRetryRestartStrategyInstance<T> instance =
new LimitedRetryRestartStrategyInstance<T>(restartable, this, numberRetries, sampleDelay,
timeDurationSuccessfulRestart, spaceEnvironment.getTimeProvider(), spaceEnvironment.getExecutorService());
instance.startRestartAttempts();
return instance;
}
/**
* A {@link RestartStrategyInstance} which will attempt a given number of
* retries.
*
* @param <U>
* the type of {@link Restartable}
*
* @author Keith M. Hughes
*/
public static class LimitedRetryRestartStrategyInstance<U extends Restartable> extends BaseRestartStrategyInstance<U> {
/**
* The number of retries left.
*/
private int numberRetriesLeft;
/**
* Last timestamp a retry was started.
*/
private long lastAttemptTime = 0;
/**
* The time provider.
*/
private final TimeProvider timeProvider;
/**
* The executor service to give threads.
*/
private final ScheduledExecutorService executorService;
/**
* Start it off as running.
*/
private volatile boolean running = true;
/**
* The delay between samples, in milliseconds.
*/
private final long sampleDelay;
/**
* The time after something starts for it to be considered a successful
* restart, in milliseconds.
*/
private final long timeDurationSuccessfulRestart;
/**
* @param restartable
* the object being restarted
* @param strategy
* the strategy the instance has supplied
* @param numberRetries
* the total number of retries which can be attempted
* @param sampleDelay
* the time between samples for restart detection, in milliseconds
* @param timeDurationSuccessfulRestart
* the time the item needs to be running before restart is
* considered a success, in milliseconds
* @param timeProvider
* the provider for time
* @param executorService
* the thread pool to use
*/
private LimitedRetryRestartStrategyInstance(U restartable, LimitedRetryRestartStrategy<U> strategy,
int numberRetries, long sampleDelay, long timeDurationSuccessfulRestart, TimeProvider timeProvider,
ScheduledExecutorService executorService) {
super(restartable, strategy, strategy.getListeners());
this.numberRetriesLeft = numberRetries;
this.sampleDelay = sampleDelay;
this.timeDurationSuccessfulRestart = timeDurationSuccessfulRestart;
this.timeProvider = timeProvider;
this.executorService = executorService;
}
/**
* Do the first restart attempt.
*/
public void startRestartAttempts() {
executorService.execute(new Runnable() {
@Override
public void run() {
attemptRestart();
while (running && !Thread.interrupted()) {
InteractiveSpacesUtilities.delay(getCurrentSampleDelay());
repeatedRestartAttempt();
}
}
});
}
/**
* Attempt a restart.
*/
private void attemptRestart() {
if (notifyRestartAttempt(getRestartable())) {
numberRetriesLeft--;
lastAttemptTime = timeProvider.getCurrentTime();
getRestartable().attemptRestart();
} else {
restartHasEnded(false);
}
}
/**
* Attempt to do another restart, but only if necessary.
*/
private void repeatedRestartAttempt() {
if (getRestartable().isRestarted()) {
long currentUptimeDuration = timeProvider.getCurrentTime() - lastAttemptTime;
if (currentUptimeDuration > timeDurationSuccessfulRestart) {
restartHasEnded(true);
}
} else {
if (numberRetriesLeft > 0) {
attemptRestart();
} else {
restartHasEnded(false);
}
}
}
/**
* The restart has ended for some reason.
*
* @param success
* {@code true} if the restart was successful
*/
private void restartHasEnded(boolean success) {
running = false;
getRestartable().restartComplete(success);
if (success) {
notifyRestartSuccess();
} else {
notifyRestartFailure();
}
}
@Override
public void quit() {
running = false;
}
@Override
public boolean isRestarting() {
return running;
}
/**
* Get the current delay which should happen between samples.
*
* @return the current delay, in milliseconds
*/
public long getCurrentSampleDelay() {
return sampleDelay;
}
}
}