package org.sdnplatform.sync.internal.store;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import net.floodlightcontroller.core.annotations.LogMessageCategory;
import net.floodlightcontroller.core.annotations.LogMessageDoc;
import net.floodlightcontroller.debugcounter.IDebugCounter;
import net.floodlightcontroller.debugcounter.IDebugCounterService;
import org.sdnplatform.sync.IClosableIterator;
import org.sdnplatform.sync.IVersion;
import org.sdnplatform.sync.Versioned;
import org.sdnplatform.sync.IStoreListener.UpdateType;
import org.sdnplatform.sync.error.SyncException;
import org.sdnplatform.sync.internal.SyncManager;
import org.sdnplatform.sync.internal.util.ByteArray;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A storage engine that proxies to another storage engine and notifies
* registered listeners of changes
* @author readams
*/
@LogMessageCategory("State Synchronization")
public class ListenerStorageEngine
implements IStorageEngine<ByteArray, byte[]> {
protected static Logger logger =
LoggerFactory.getLogger(ListenerStorageEngine.class);
/**
* Listeners for this store
*/
protected List<MappingStoreListener> listeners =
new ArrayList<MappingStoreListener>();
/**
* The local storage for this storage engine
*/
protected IStorageEngine<ByteArray, byte[]> localStorage;
/**
* Debug counter service
*/
protected IDebugCounterService debugCounter;
/**
* Allocate new {@link ListenerStorageEngine}
* @param localStorage the delegate store
* @param debugCounter debug counter service
*/
public ListenerStorageEngine(IStorageEngine<ByteArray,
byte[]> localStorage,
IDebugCounterService debugCounter) {
this.localStorage = localStorage;
this.debugCounter = debugCounter;
}
// *************************
// StorageEngine<Key,byte[]>
// *************************
@Override
public List<Versioned<byte[]>> get(ByteArray key) throws SyncException {
updateCounter(SyncManager.counterGets);
return localStorage.get(key);
}
@Override
public IClosableIterator<Entry<ByteArray,List<Versioned<byte[]>>>> entries() {
updateCounter(SyncManager.counterIterators);
return localStorage.entries();
}
@Override
public void put(ByteArray key, Versioned<byte[]> value)
throws SyncException {
updateCounter(SyncManager.counterPuts);
localStorage.put(key, value);
notifyListeners(key, UpdateType.LOCAL);
}
@Override
public IClosableIterator<ByteArray> keys() {
return localStorage.keys();
}
@Override
public void truncate() throws SyncException {
localStorage.truncate();
}
@Override
public String getName() {
return localStorage.getName();
}
@Override
public void close() throws SyncException {
localStorage.close();
}
@Override
public List<IVersion> getVersions(ByteArray key) throws SyncException {
return localStorage.getVersions(key);
}
@Override
public boolean writeSyncValue(ByteArray key,
Iterable<Versioned<byte[]>> values) {
boolean r = localStorage.writeSyncValue(key, values);
if (r) notifyListeners(key, UpdateType.REMOTE);
return r;
}
@Override
public void cleanupTask() throws SyncException {
localStorage.cleanupTask();
}
@Override
public boolean isPersistent() {
return localStorage.isPersistent();
}
@Override
public void setTombstoneInterval(int interval) {
localStorage.setTombstoneInterval(interval);
}
// *********************
// ListenerStorageEngine
// *********************
public void addListener(MappingStoreListener listener) {
listeners.add(listener);
}
protected void notifyListeners(ByteArray key, UpdateType type) {
notifyListeners(Collections.singleton(key).iterator(), type);
}
@LogMessageDoc(level="ERROR",
message="An error occurred in a sync listener",
explanation="An unexpected error occured in a handler for " +
"an update to shared state.")
protected void notifyListeners(Iterator<ByteArray> keys, UpdateType type) {
for (MappingStoreListener msl : listeners) {
try {
msl.notify(keys, type);
} catch (Exception e) {
logger.error("An error occurred in a sync listener", e);
}
}
}
protected void updateCounter(IDebugCounter counter) {
if (debugCounter != null) {
counter.updateCounterWithFlush();
}
}
}