package org.threadly.util;
import java.util.concurrent.atomic.AtomicInteger;
/**
* An abstract implementation of the {@link Service} interface.
* <p>
* This implementation is flexible, weather the internal service is scheduled on a thread pool
* runs on a unique thread, or has other means of running.
*
* @since 2.6.0
*/
public abstract class AbstractService implements Service {
private AtomicInteger state = new AtomicInteger(0); // 0 = new, 1 = started, 2 = stopped
@Override
public void start() throws IllegalStateException {
if (! startIfNotStarted()) {
throw new IllegalStateException();
}
}
@Override
public boolean startIfNotStarted() {
if (state.get() == 0 && state.compareAndSet(0, 1)) {
startupService();
return true;
} else {
return false;
}
}
/**
* Called internally when the service should perform any actions to startup. It is expected
* that this call will block until the service is running. This invocation will only occur
* once.
*/
protected abstract void startupService();
@Override
public void stop() {
if (! stopIfRunning()) {
throw new IllegalStateException();
}
}
@Override
public boolean stopIfRunning() {
if (state.get() == 1 && state.compareAndSet(1, 2)) {
shutdownService();
return true;
} else {
return false;
}
}
/**
* Called internally when the service should perform any actions to shutdown. It is expected
* that this call will block until the service is shutdown. This invocation will only occur
* once.
*/
protected abstract void shutdownService();
@Override
public boolean isRunning() {
return state.get() == 1;
}
@Override
public boolean hasStopped() {
return state.get() == 2;
}
}