// This software is released into the Public Domain. See copying.txt for details.
package org.openstreetmap.osmosis.replicationhttp.v0_6.impl;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;
import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
/**
* This class encapsulates the functionality required to manage restarts of a
* sequence client on failure.
*
* @author Brett Henderson
*/
public class SequenceClientRestartManager {
private static final Logger LOG = Logger.getLogger(SequenceClientRestartManager.class.getName());
private static final int RESTART_DELAY = 60000;
private Lock controlLock;
private Condition controlCondition;
private ClientControl control;
private boolean clientRunning;
/**
* Creates a new instance.
*/
public SequenceClientRestartManager() {
controlLock = new ReentrantLock();
controlCondition = controlLock.newCondition();
control = new ClientControl();
}
/**
* Either thread can call this method when they wish to wait until an update
* has been performed by the other thread.
*/
private void waitForUpdate() {
try {
controlCondition.await();
} catch (InterruptedException e) {
throw new OsmosisRuntimeException("Thread was interrupted.", e);
}
}
/**
* Either thread can call this method when they wish to signal the other
* thread that an update has occurred.
*/
private void signalUpdate() {
controlCondition.signal();
}
/**
* Returns a sequence client control object to be used when creating a new
* sequence client.
*
* @return The sequence client controller.
*/
public SequenceClientControl getControl() {
return control;
}
/**
* Runs the sequence client and restarts it if it fails. This sequence
* client must have been created using a control listener returned from the
* getControl method of this object.
*
* @param sequenceClient
* The sequence client to manage.
*/
public void manageClient(SequenceClient sequenceClient) {
try {
// Run the client within a loop to allow client restarts if
// problems occur.
while (true) {
// Initialise the running flag. We must do this before we
// start the client because the client thread will set it to
// false when it finishes which may occur very soon after
// starting.
clientRunning = true;
try {
sequenceClient.start();
} catch (OsmosisRuntimeException e) {
// The client startup failed, so log the exception and try
// again in the next loop.
LOG.warning("Unable to start the sequence client, will retry in " + RESTART_DELAY
+ " milliseconds.");
}
// Wait for the client to stop.
try {
controlLock.lock();
while (clientRunning) {
waitForUpdate();
}
} finally {
controlLock.unlock();
}
// Stop the client explicitly which will close any remaining
// resources.
sequenceClient.stop();
// Wait for 1 minute between connection failures.
try {
Thread.sleep(RESTART_DELAY);
} catch (InterruptedException e) {
throw new OsmosisRuntimeException("Thread sleep failed between sequence number client invocations",
e);
}
}
} finally {
sequenceClient.stop();
}
}
/**
* Internal class used to process event updates from the sequence number
* client.
*
* @author Brett Henderson
*/
private class ClientControl implements SequenceClientControl {
@Override
public void channelClosed() {
controlLock.lock();
try {
// The client has failed. We need to tell the master thread so
// that it can act accordingly (eg. restart the client).
clientRunning = false;
signalUpdate();
} finally {
controlLock.unlock();
}
}
}
}