blob: a7bedbb950f991d19727d389925258295050a220 [file] [log] [blame]
/*
* Copyright 2018-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.store.primitives.impl;
import java.util.Collection;
import java.util.Map;
import java.util.NavigableMap;
import java.util.NavigableSet;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import com.google.common.base.Throwables;
import com.google.common.collect.Maps;
import io.atomix.core.collection.impl.TranscodingAsyncDistributedCollection;
import io.atomix.core.map.impl.DelegatingAsyncDistributedNavigableMap;
import org.onosproject.store.primitives.MapUpdate;
import org.onosproject.store.primitives.TransactionId;
import org.onosproject.store.service.AsyncConsistentTreeMap;
import org.onosproject.store.service.AsyncIterator;
import org.onosproject.store.service.ConsistentMapException;
import org.onosproject.store.service.MapEvent;
import org.onosproject.store.service.MapEventListener;
import org.onosproject.store.service.TransactionLog;
import org.onosproject.store.service.Version;
import org.onosproject.store.service.Versioned;
/**
* Atomix consistent tree map.
*/
public class AtomixConsistentTreeMap<V> implements AsyncConsistentTreeMap<V> {
private final io.atomix.core.map.AsyncAtomicNavigableMap<String, V> atomixTreeMap;
private final Map<MapEventListener<String, V>, io.atomix.core.map.AtomicMapEventListener<String, V>> listenerMap =
Maps.newIdentityHashMap();
public AtomixConsistentTreeMap(io.atomix.core.map.AsyncAtomicNavigableMap<String, V> atomixTreeMap) {
this.atomixTreeMap = atomixTreeMap;
}
@Override
public String name() {
return atomixTreeMap.name();
}
@Override
public CompletableFuture<Integer> size() {
return atomixTreeMap.size();
}
@Override
public CompletableFuture<String> firstKey() {
return atomixTreeMap.firstKey();
}
@Override
public CompletableFuture<String> lastKey() {
return atomixTreeMap.lastKey();
}
@Override
public CompletableFuture<Map.Entry<String, Versioned<V>>> ceilingEntry(String key) {
return atomixTreeMap.ceilingEntry(key).thenApply(this::toVersioned);
}
@Override
public CompletableFuture<Map.Entry<String, Versioned<V>>> floorEntry(String key) {
return atomixTreeMap.floorEntry(key).thenApply(this::toVersioned);
}
@Override
public CompletableFuture<Map.Entry<String, Versioned<V>>> higherEntry(String key) {
return atomixTreeMap.higherEntry(key).thenApply(this::toVersioned);
}
@Override
public CompletableFuture<Map.Entry<String, Versioned<V>>> lowerEntry(String key) {
return atomixTreeMap.lowerEntry(key).thenApply(this::toVersioned);
}
@Override
public CompletableFuture<Map.Entry<String, Versioned<V>>> firstEntry() {
return atomixTreeMap.firstEntry().thenApply(this::toVersioned);
}
@Override
public CompletableFuture<Map.Entry<String, Versioned<V>>> lastEntry() {
return atomixTreeMap.lastEntry().thenApply(this::toVersioned);
}
@Override
public CompletableFuture<Map.Entry<String, Versioned<V>>> pollFirstEntry() {
throw new UnsupportedOperationException();
}
@Override
public CompletableFuture<Map.Entry<String, Versioned<V>>> pollLastEntry() {
throw new UnsupportedOperationException();
}
@Override
public CompletableFuture<Boolean> containsKey(String key) {
return atomixTreeMap.containsKey(key);
}
@Override
public CompletableFuture<String> lowerKey(String key) {
return atomixTreeMap.lowerKey(key);
}
@Override
public CompletableFuture<String> floorKey(String key) {
return atomixTreeMap.floorKey(key);
}
@Override
public CompletableFuture<String> ceilingKey(String key) {
return atomixTreeMap.ceilingKey(key);
}
@Override
public CompletableFuture<Versioned<V>> get(String key) {
return atomixTreeMap.get(key).thenApply(this::toVersioned);
}
@Override
public CompletableFuture<String> higherKey(String key) {
return atomixTreeMap.higherKey(key);
}
@Override
public CompletableFuture<NavigableSet<String>> navigableKeySet() {
return CompletableFuture.completedFuture(atomixTreeMap.navigableKeySet().sync());
}
@Override
public CompletableFuture<Versioned<V>> getOrDefault(String key, V defaultValue) {
return atomixTreeMap.getOrDefault(key, defaultValue).thenApply(this::toVersioned);
}
@Override
public CompletableFuture<NavigableMap<String, V>> subMap(
String upperKey, String lowerKey, boolean inclusiveUpper, boolean inclusiveLower) {
return CompletableFuture.completedFuture(
new DelegatingAsyncDistributedNavigableMap<>(
atomixTreeMap.subMap(lowerKey, inclusiveLower, upperKey, inclusiveUpper)).sync());
}
@Override
public CompletableFuture<Versioned<V>> computeIf(
String key,
Predicate<? super V> condition, BiFunction<? super String, ? super V, ? extends V> remappingFunction) {
return adapt(atomixTreeMap.computeIf(key, condition, remappingFunction)).thenApply(this::toVersioned);
}
@Override
public CompletableFuture<Versioned<V>> put(String key, V value) {
return adapt(atomixTreeMap.put(key, value)).thenApply(this::toVersioned);
}
@Override
public CompletableFuture<Versioned<V>> putAndGet(String key, V value) {
return adapt(atomixTreeMap.putAndGet(key, value)).thenApply(this::toVersioned);
}
@Override
public CompletableFuture<Versioned<V>> remove(String key) {
return adapt(atomixTreeMap.remove(key)).thenApply(this::toVersioned);
}
@Override
public CompletableFuture<Set<String>> keySet() {
return CompletableFuture.completedFuture(atomixTreeMap.keySet().sync());
}
@Override
public CompletableFuture<Set<Map.Entry<String, Versioned<V>>>> entrySet() {
return CompletableFuture.completedFuture(atomixTreeMap.entrySet().stream()
.map(this::toVersioned)
.collect(Collectors.toSet()));
}
@Override
public CompletableFuture<Versioned<V>> putIfAbsent(String key, V value) {
return adapt(atomixTreeMap.putIfAbsent(key, value)).thenApply(this::toVersioned);
}
@Override
public CompletableFuture<Boolean> remove(String key, V value) {
return adapt(atomixTreeMap.remove(key, value));
}
@Override
public CompletableFuture<Boolean> remove(String key, long version) {
return adapt(atomixTreeMap.remove(key, version));
}
@Override
public CompletableFuture<Versioned<V>> replace(String key, V value) {
return adapt(atomixTreeMap.replace(key, value)).thenApply(this::toVersioned);
}
@Override
public CompletableFuture<Boolean> replace(String key, V oldValue, V newValue) {
return adapt(atomixTreeMap.replace(key, oldValue, newValue));
}
@Override
public CompletableFuture<Boolean> replace(String key, long oldVersion, V newValue) {
return adapt(atomixTreeMap.replace(key, oldVersion, newValue));
}
@Override
public CompletableFuture<Boolean> containsValue(V value) {
return atomixTreeMap.containsValue(value);
}
@Override
public CompletableFuture<AsyncIterator<Map.Entry<String, Versioned<V>>>> iterator() {
io.atomix.core.iterator.AsyncIterator<Map.Entry<String, io.atomix.utils.time.Versioned<V>>> atomixIterator
= atomixTreeMap.entrySet().iterator();
return CompletableFuture.completedFuture(new AsyncIterator<Map.Entry<String, Versioned<V>>>() {
@Override
public CompletableFuture<Boolean> hasNext() {
return atomixIterator.hasNext();
}
@Override
public CompletableFuture<Map.Entry<String, Versioned<V>>> next() {
return atomixIterator.next()
.thenApply(entry -> Maps.immutableEntry(entry.getKey(), toVersioned(entry.getValue())));
}
});
}
@Override
public CompletableFuture<Void> clear() {
return atomixTreeMap.clear();
}
@Override
public CompletableFuture<Collection<Versioned<V>>> values() {
return CompletableFuture.completedFuture(
new TranscodingAsyncDistributedCollection<Versioned<V>, io.atomix.utils.time.Versioned<V>>(
atomixTreeMap.values(),
e -> new io.atomix.utils.time.Versioned<>(e.value(), e.version()),
e -> new Versioned<>(e.value(), e.version())).sync());
}
@Override
public synchronized CompletableFuture<Void> addListener(MapEventListener<String, V> listener, Executor executor) {
io.atomix.core.map.AtomicMapEventListener<String, V> atomixListener = event ->
listener.event(new MapEvent<String, V>(
MapEvent.Type.valueOf(event.type().name()),
name(),
event.key(),
toVersioned(event.newValue()),
toVersioned(event.oldValue())));
listenerMap.put(listener, atomixListener);
return atomixTreeMap.addListener(atomixListener, executor);
}
@Override
public CompletableFuture<Void> removeListener(MapEventListener<String, V> listener) {
io.atomix.core.map.AtomicMapEventListener<String, V> atomixListener = listenerMap.remove(listener);
if (atomixListener != null) {
return atomixTreeMap.removeListener(atomixListener);
}
return CompletableFuture.completedFuture(null);
}
@Override
public CompletableFuture<Version> begin(TransactionId transactionId) {
throw new UnsupportedOperationException();
}
@Override
public CompletableFuture<Boolean> prepare(TransactionLog<MapUpdate<String, V>> transactionLog) {
throw new UnsupportedOperationException();
}
@Override
public CompletableFuture<Boolean> prepareAndCommit(TransactionLog<MapUpdate<String, V>> transactionLog) {
throw new UnsupportedOperationException();
}
@Override
public CompletableFuture<Void> commit(TransactionId transactionId) {
throw new UnsupportedOperationException();
}
@Override
public CompletableFuture<Void> rollback(TransactionId transactionId) {
throw new UnsupportedOperationException();
}
private <T> CompletableFuture<T> adapt(CompletableFuture<T> future) {
CompletableFuture<T> newFuture = new CompletableFuture<>();
future.whenComplete((result, error) -> {
if (error == null) {
newFuture.complete(result);
} else {
Throwable cause = Throwables.getRootCause(error);
if (cause instanceof io.atomix.primitive.PrimitiveException.ConcurrentModification) {
newFuture.completeExceptionally(
new ConsistentMapException.ConcurrentModification(error.getMessage()));
} else if (cause instanceof io.atomix.primitive.PrimitiveException.Timeout) {
newFuture.completeExceptionally(new ConsistentMapException.Timeout(error.getMessage()));
} else if (cause instanceof io.atomix.primitive.PrimitiveException.Interrupted) {
newFuture.completeExceptionally(new ConsistentMapException.Interrupted());
} else if (cause instanceof io.atomix.primitive.PrimitiveException.Unavailable) {
newFuture.completeExceptionally(new ConsistentMapException.Unavailable());
} else if (cause instanceof io.atomix.primitive.PrimitiveException) {
newFuture.completeExceptionally(new ConsistentMapException(cause.getMessage()));
}
}
});
return newFuture;
}
private Versioned<V> toVersioned(io.atomix.utils.time.Versioned<V> versioned) {
return versioned != null
? new Versioned<>(versioned.value(), versioned.version(), versioned.creationTime())
: null;
}
private Map.Entry<String, Versioned<V>> toVersioned(Map.Entry<String, io.atomix.utils.time.Versioned<V>> entry) {
return entry != null ? Maps.immutableEntry(entry.getKey(), toVersioned(entry.getValue())) : null;
}
}