package org.sdnplatform.sync.internal;
import java.util.List;
import java.util.Map.Entry;
import com.fasterxml.jackson.core.type.TypeReference;
import org.sdnplatform.sync.IClosableIterator;
import org.sdnplatform.sync.IInconsistencyResolver;
import org.sdnplatform.sync.IStoreListener;
import org.sdnplatform.sync.IVersion;
import org.sdnplatform.sync.Versioned;
import org.sdnplatform.sync.error.InconsistentDataException;
import org.sdnplatform.sync.error.SyncException;
import org.sdnplatform.sync.error.UnknownStoreException;
import org.sdnplatform.sync.internal.store.IStore;
import org.sdnplatform.sync.internal.store.MappingStoreListener;
import org.sdnplatform.sync.internal.util.Pair;
import org.sdnplatform.sync.internal.version.ChainedResolver;
import org.sdnplatform.sync.internal.version.TimeBasedInconsistencyResolver;
import org.sdnplatform.sync.internal.version.VectorClock;
import org.sdnplatform.sync.internal.version.VectorClockInconsistencyResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default implementation of a store client used for accessing a store
* locally in process.
* @author readams
*
* @param <K> the key type
* @param <V> the value type
*/
public class DefaultStoreClient<K, V> extends AbstractStoreClient<K, V> {
protected static final Logger logger =
LoggerFactory.getLogger(DefaultStoreClient.class.getName());
private IStore<K, V> delegate;
private IInconsistencyResolver<Versioned<V>> resolver;
private AbstractSyncManager syncManager;
private Class<K> keyClass;
private TypeReference<K> keyType;
@SuppressWarnings("unchecked")
public DefaultStoreClient(IStore<K, V> delegate,
IInconsistencyResolver<Versioned<V>> resolver,
AbstractSyncManager syncManager,
Class<K> keyClass,
TypeReference<K> keyType) {
super();
this.delegate = delegate;
this.syncManager = syncManager;
this.keyClass = keyClass;
this.keyType = keyType;
IInconsistencyResolver<Versioned<V>> vcir =
new VectorClockInconsistencyResolver<V>();
IInconsistencyResolver<Versioned<V>> secondary = resolver;
if (secondary == null)
secondary = new TimeBasedInconsistencyResolver<V>();
this.resolver = new ChainedResolver<Versioned<V>>(vcir, secondary);
}
// ******************
// IStoreClient<K,V>
// ******************
@Override
public Versioned<V> get(K key, Versioned<V> defaultValue)
throws SyncException {
List<Versioned<V>> raw = delegate.get(key);
return handleGet(key, defaultValue, raw);
}
@Override
public IClosableIterator<Entry<K, Versioned<V>>> entries() throws SyncException {
return new StoreClientIterator(delegate.entries());
}
@Override
public IVersion put(K key, Versioned<V> versioned)
throws SyncException {
VectorClock vc = (VectorClock)versioned.getVersion();
vc = vc.incremented(syncManager.getLocalNodeId(),
System.currentTimeMillis());
versioned = Versioned.value(versioned.getValue(), vc);
delegate.put(key, versioned);
return versioned.getVersion();
}
@Override
public void addStoreListener(IStoreListener<K> listener) {
if (listener == null)
throw new IllegalArgumentException("Must include listener");
MappingStoreListener msl =
new MappingStoreListener(keyType, keyClass, listener);
try {
syncManager.addListener(delegate.getName(), msl);
} catch (UnknownStoreException e) {
// this shouldn't happen since we already have a store client,
// unless the store has been deleted somehow
logger.error("Unexpected internal state: unknown store " +
"from store client. Could not register listener", e);
}
}
// ************************
// AbstractStoreClient<K,V>
// ************************
@Override
protected List<IVersion> getVersions(K key) throws SyncException {
return delegate.getVersions(key);
}
// *********************
// Private local methods
// *********************
protected Versioned<V> handleGet(K key,
Versioned<V> defaultValue,
List<Versioned<V>> raw)
throws InconsistentDataException {
if (raw == null) return defaultValue(defaultValue);
List<Versioned<V>> vs = resolver.resolveConflicts(raw);
return getItemOrThrow(key, defaultValue, vs);
}
protected Versioned<V> defaultValue(Versioned<V> defaultValue) {
if (defaultValue == null)
return Versioned.emptyVersioned();
return defaultValue;
}
protected Versioned<V> getItemOrThrow(K key,
Versioned<V> defaultValue,
List<Versioned<V>> items)
throws InconsistentDataException {
if(items.size() == 0)
return defaultValue(defaultValue);
else if(items.size() == 1)
return items.get(0);
else
throw new InconsistentDataException("Resolver failed to resolve" +
" conflict: " + items.size() + " unresolved items", items);
}
protected class StoreClientIterator implements
IClosableIterator<Entry<K, Versioned<V>>> {
IClosableIterator<Entry<K, List<Versioned<V>>>> delegate;
public StoreClientIterator(IClosableIterator<Entry<K,
List<Versioned<V>>>> delegate) {
super();
this.delegate = delegate;
}
@Override
public boolean hasNext() {
return delegate.hasNext();
}
@Override
public Entry<K, Versioned<V>> next() {
Entry<K, List<Versioned<V>>> n = delegate.next();
try {
return new Pair<K, Versioned<V>>(n.getKey(),
handleGet(n.getKey(), null, n.getValue()));
} catch (SyncException e) {
logger.error("Failed to construct next value", e);
return null;
}
}
@Override
public void remove() {
delegate.remove();
}
@Override
public void close() {
delegate.close();
}
}
}