package net.floodlightcontroller.debugcounter;
import net.floodlightcontroller.core.IShutdownListener;
import net.floodlightcontroller.core.IShutdownService;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.concurrent.GuardedBy;
import java.util.*;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class DebugCounterServiceImpl implements IFloodlightModule, IDebugCounterService {
protected static final Logger logger =
LoggerFactory.getLogger(DebugCounterServiceImpl.class);
/**
* The tree of counters.
*/
private final CounterNode root = CounterNode.newTree();
/**
* protects the counter hierarchy tree. The writeLock is required to
* change to hierarchy, i.e., adding nodes. The readLock is required
* to query the counters or to reset them.
*/
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
static void verifyStringSanity(String str, String name) {
if (str == null) {
if (name == null) {
throw new NullPointerException();
} else {
throw new NullPointerException(name + " must not be null");
}
}
if (str.isEmpty()) {
if (name == null) {
throw new IllegalArgumentException();
} else {
throw new IllegalArgumentException(name + " must not be empty");
}
}
}
static void verifyModuleNameSanity(String moduleName) {
verifyStringSanity(moduleName, "moduleName");
if (moduleName.contains("/")) {
throw new IllegalArgumentException("moduleName must not contain /");
}
}
@Override
public boolean registerModule(String moduleName) {
verifyModuleNameSanity(moduleName);
lock.writeLock().lock();
try {
return root.addModule(moduleName);
} finally {
lock.writeLock().unlock();
}
}
@Override
public IDebugCounter registerCounter(String moduleName,
String counterHierarchy,
String counterDescription,
MetaData... metaData) {
verifyModuleNameSanity(moduleName);
verifyStringSanity(counterHierarchy, "counterHierarchy");
if (counterDescription == null) {
try {
throw new Exception("counterDescription must not be null");
} catch (Exception e) {
e.printStackTrace();
logger.error(e.getMessage());
}
}
if (metaData == null) {
// somebody passing in a null array. sigh.
throw new NullPointerException("metaData must not be null");
}
DebugCounterImpl counter =
new DebugCounterImpl(moduleName, counterHierarchy,
counterDescription,
Arrays.asList(metaData));
lock.writeLock().lock();
try {
/* addCounter(counter) will return null if counter is accepted as a new counter
* or it will return a reference to the existing DebugCounterImpl if the counter
* is already present.
*/
DebugCounterImpl oldCounter = root.addCounter(counter);
if (oldCounter != null && logger.isDebugEnabled()) {
logger.debug("Counter {} {} already registered. Resetting hierarchy",
moduleName, counterHierarchy);
}
/* If addCounter(counter) returned null, counter is the new counter, else if
* addCounter(counter) returned a non-null reference, then the reference is
* the existing counter, which has just been reset and should be reused.
*/
counter = (oldCounter == null ? counter : oldCounter);
} finally {
lock.writeLock().unlock();
}
return counter;
}
@GuardedBy("lock.readLock")
private boolean resetInternal(List<String> hierarchyElements) {
CounterNode node = root.lookup(hierarchyElements);
if (node == null) {
return false;
}
node.resetHierarchy();
return true;
}
@GuardedBy("lock.readLock")
private boolean removeInternal(List<String> hierarchyElements) {
CounterNode node = root.lookup(hierarchyElements); // returns e.g. root/module-name/counter-node-to-remove
if (node == null) {
return false;
}
root.remove(hierarchyElements);
return true;
}
@Override
public boolean resetCounterHierarchy(String moduleName,
String counterHierarchy) {
verifyModuleNameSanity(moduleName);
verifyStringSanity(counterHierarchy, "counterHierarchy");
lock.readLock().lock();
try {
return resetInternal(CounterNode.getHierarchyElements(moduleName, counterHierarchy));
} finally {
lock.readLock().unlock();
}
}
@Override
public void resetAllCounters() {
lock.readLock().lock();
try {
root.resetHierarchy();
} finally {
lock.readLock().unlock();
}
}
@Override
public boolean resetAllModuleCounters(String moduleName) {
verifyModuleNameSanity(moduleName);
lock.readLock().lock();
try {
return resetInternal(Collections.singletonList(moduleName));
} finally {
lock.readLock().unlock();
}
}
@Override
public boolean removeCounterHierarchy(String moduleName,
String counterHierarchy) {
verifyModuleNameSanity(moduleName);
verifyStringSanity(counterHierarchy, "counterHierarchy");
lock.readLock().lock();
try {
return removeInternal(CounterNode.getHierarchyElements(moduleName, counterHierarchy));
} finally {
lock.readLock().unlock();
}
}
@GuardedBy("lock.readLock")
private List<DebugCounterResource> getCountersFromNode(CounterNode node) {
if (node == null) {
return Collections.emptyList();
}
List<DebugCounterResource> ret = new ArrayList<>();
for (DebugCounterImpl counter: node.getCountersInHierarchy()) {
ret.add(new DebugCounterResource(counter));
}
return ret;
}
@Override
public List<DebugCounterResource>
getCounterHierarchy(String moduleName, String counterHierarchy) {
verifyModuleNameSanity(moduleName);
verifyStringSanity(counterHierarchy, "counterHierarchy");
List<String> hierarchyElements =
CounterNode.getHierarchyElements(moduleName, counterHierarchy);
lock.readLock().lock();
try {
return getCountersFromNode(root.lookup(hierarchyElements));
} finally {
lock.readLock().unlock();
}
}
@Override
public List<DebugCounterResource> getAllCounterValues() {
lock.readLock().lock();
try {
return getCountersFromNode(root);
} finally {
lock.readLock().unlock();
}
}
@Override
public List<DebugCounterResource> getModuleCounterValues(String moduleName) {
verifyModuleNameSanity(moduleName);
List<String> hierarchyElements = Collections.singletonList(moduleName);
lock.readLock().lock();
try {
return getCountersFromNode(root.lookup(hierarchyElements));
} finally {
lock.readLock().unlock();
}
}
private class ShutdownListenenerDelegate implements IShutdownListener {
@Override
public void floodlightIsShuttingDown() {
for (DebugCounterResource counter: getAllCounterValues()) {
logger.info("Module {} counterHierarchy {} value " + counter.getCounterValue(),
counter.getModuleName(),
counter.getCounterHierarchy());
}
}
}
//*******************************
// IFloodlightModule
//*******************************
@Override
public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Collection<Class<? extends IFloodlightService>> l =
new ArrayList<Class<? extends IFloodlightService>>();
l.add(IDebugCounterService.class);
return l;
}
@Override
public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
Map<Class<? extends IFloodlightService>, IFloodlightService> m =
new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
m.put(IDebugCounterService.class, this);
return m;
}
@Override
public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
ArrayList<Class<? extends IFloodlightService>> deps =
new ArrayList<Class<? extends IFloodlightService>>();
deps.add(IShutdownService.class);
return deps;
}
@Override
public void init(FloodlightModuleContext context) {
}
@Override
public void startUp(FloodlightModuleContext context) {
IShutdownService shutdownService =
context.getServiceImpl(IShutdownService.class);
shutdownService.registerShutdownListener(new ShutdownListenenerDelegate());
}
}