package net.floodlightcontroller.core.internal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import net.floodlightcontroller.core.IShutdownListener;
import net.floodlightcontroller.core.IShutdownService;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.module.FloodlightModuleException;
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
public class ShutdownServiceImpl implements IFloodlightModule,
IShutdownService {
private static final Logger logger =
LoggerFactory.getLogger(ShutdownServiceImpl.class);
/** Maximum amount of time for IShutdownListeners to complete before
* floodlight terminates in any case
*/
private static final int MAX_SHUTDOWN_WAIT_MS = 5_000;
private final CopyOnWriteArrayList<IShutdownListener> shutdownListeners =
new CopyOnWriteArrayList<>();
@Override
public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Collection<Class<? extends IFloodlightService>> l =
new ArrayList<>();
l.add(IShutdownService.class);
return l;
}
@Override
public Map<Class<? extends IFloodlightService>, IFloodlightService>
getServiceImpls() {
Map<Class<? extends IFloodlightService>, IFloodlightService> m =
new HashMap<>();
m.put(IShutdownService.class, this);
return m;
}
@Override
public Collection<Class<? extends IFloodlightService>>
getModuleDependencies() {
return new ArrayList<>();
}
@Override
public void init(FloodlightModuleContext context)
throws FloodlightModuleException {
// do nothing
}
@Override
public void startUp(FloodlightModuleContext context)
throws FloodlightModuleException {
// do nothing
}
@Override
public void registerShutdownListener(@Nonnull IShutdownListener listener) {
if (listener == null) {
throw new NullPointerException("listener must not be null");
}
shutdownListeners.add(listener);
}
@Override
public void terminate(@Nullable final String reason, final int exitCode) {
final String paddedReason;
if (reason == null) {
paddedReason = "";
} else {
paddedReason = " due to " + reason;
}
// A safety valve to make sure we really do indeed terminate floodlight
// We are using a Thread rather than a task to make sure nothing can
// cancel the shutdown (e.g., if an ExecutorService was already
// shutdown
Thread shutdownForHungListeners = new Thread(new Runnable() {
// Suppress findbugs warning that we call system.exit
@SuppressFBWarnings(value="DM_EXIT", justification="exit by design")
@Override
public void run() {
try {
Thread.sleep(MAX_SHUTDOWN_WAIT_MS);
} catch (InterruptedException e) {
// do nothing, we are about to exit anyways
}
logger.error("**************************************************");
logger.error("* Floodlight is terminating {}", paddedReason);
logger.error("* ShutdownListeners failed to complete in time");
logger.error("**************************************************");
System.exit(exitCode);
}
}, "ShutdownSafetyValve");
shutdownForHungListeners.start();
logger.info("Floodlight about to terminate. Calling shutdown listeners");
for (IShutdownListener listener: shutdownListeners) {
listener.floodlightIsShuttingDown();
}
if (exitCode == 0) {
logger.info("**************************************************");
logger.info("* Floodlight is terminating normally{}", paddedReason);
logger.info("**************************************************");
} else {
logger.error("**************************************************");
logger.error("* Floodlight is terminating abnormally{}", paddedReason);
logger.error("**************************************************");
}
// Game Over.
System.exit(exitCode);
}
@Override
public void terminate(final String reason, final Throwable e, final int exitCode) {
final String paddedReason;
if (reason == null) {
paddedReason = "";
} else {
paddedReason = " due to " + reason;
}
// A safety valve to make sure we really do indeed terminate floodlight
// We are using a Thread rather than a task to make sure nothing can
// cancel the shutdown (e.g., if an ExecutorService was already
// shutdown
Thread shutdownForHungListeners = new Thread(new Runnable() {
// Suppress findbugs warning that we call system.exit
@SuppressFBWarnings(value="DM_EXIT", justification="exit by design")
@Override
public void run() {
try {
Thread.sleep(MAX_SHUTDOWN_WAIT_MS);
} catch (InterruptedException e) {
// do nothing, we are about to exit anyways
}
logger.error("**************************************************");
logger.error("Floodlight is terminating{}", paddedReason, e);
logger.error("ShutdownListeners failed to complete in time");
logger.error("**************************************************");
System.exit(exitCode);
}
}, "ShutdownSafetyValve");
shutdownForHungListeners.start();
logger.info("Floodlight about to terminate. Calling shutdown listeners");
for (IShutdownListener listener: shutdownListeners) {
listener.floodlightIsShuttingDown();
}
logger.error("**************************************************");
logger.error("Floodlight is terminating abnormally{}", paddedReason, e);
logger.error("**************************************************");
// Game Over.
System.exit(exitCode);
}
}