blob: dd80db612d1bf6a2cb70ae495b729fd13a3f0b28 [file] [log] [blame]
Madan Jampani7c521002015-03-23 12:23:01 -07001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.store.consistent.impl;
18
19import static com.google.common.base.Preconditions.*;
20
21import java.util.Collection;
22import java.util.Map;
23import java.util.Map.Entry;
24import java.util.concurrent.CompletableFuture;
25import java.util.stream.Collectors;
26import java.util.Set;
27
28import org.apache.commons.lang3.tuple.Pair;
29import org.onlab.util.HexString;
30import org.onosproject.store.service.AsyncConsistentMap;
Madan Jampanibff6d8f2015-03-31 16:53:47 -070031import org.onosproject.store.service.ConsistentMapException;
Madan Jampani7c521002015-03-23 12:23:01 -070032import org.onosproject.store.service.Serializer;
33import org.onosproject.store.service.Versioned;
34
35import com.google.common.cache.CacheBuilder;
36import com.google.common.cache.CacheLoader;
37import com.google.common.cache.LoadingCache;
38
39/**
40 * AsyncConsistentMap implementation that is backed by a Raft consensus
41 * based database.
42 *
43 * @param <K> type of key.
44 * @param <V> type of value.
45 */
46public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> {
47
48 private final String name;
Madan Jampanif1b8e172015-03-23 11:42:02 -070049 private final Database database;
Madan Jampani7c521002015-03-23 12:23:01 -070050 private final Serializer serializer;
Madan Jampani02b7fb82015-05-01 13:01:20 -070051 private final boolean readOnly;
Madan Jampani7c521002015-03-23 12:23:01 -070052
53 private static final String ERROR_NULL_KEY = "Key cannot be null";
54 private static final String ERROR_NULL_VALUE = "Null values are not allowed";
55
56 private final LoadingCache<K, String> keyCache = CacheBuilder.newBuilder()
57 .softValues()
58 .build(new CacheLoader<K, String>() {
59
60 @Override
61 public String load(K key) {
62 return HexString.toHexString(serializer.encode(key));
63 }
64 });
65
66 protected K dK(String key) {
67 return serializer.decode(HexString.fromHexString(key));
68 }
69
70 public DefaultAsyncConsistentMap(String name,
Madan Jampanif1b8e172015-03-23 11:42:02 -070071 Database database,
Madan Jampani02b7fb82015-05-01 13:01:20 -070072 Serializer serializer,
73 boolean readOnly) {
Madan Jampani7c521002015-03-23 12:23:01 -070074 this.name = checkNotNull(name, "map name cannot be null");
Madan Jampanif1b8e172015-03-23 11:42:02 -070075 this.database = checkNotNull(database, "database cannot be null");
Madan Jampani7c521002015-03-23 12:23:01 -070076 this.serializer = checkNotNull(serializer, "serializer cannot be null");
Madan Jampani02b7fb82015-05-01 13:01:20 -070077 this.readOnly = readOnly;
Madan Jampani7c521002015-03-23 12:23:01 -070078 }
79
80 @Override
81 public CompletableFuture<Integer> size() {
Madan Jampanif1b8e172015-03-23 11:42:02 -070082 return database.size(name);
Madan Jampani7c521002015-03-23 12:23:01 -070083 }
84
85 @Override
86 public CompletableFuture<Boolean> isEmpty() {
Madan Jampanif1b8e172015-03-23 11:42:02 -070087 return database.isEmpty(name);
Madan Jampani7c521002015-03-23 12:23:01 -070088 }
89
90 @Override
91 public CompletableFuture<Boolean> containsKey(K key) {
92 checkNotNull(key, ERROR_NULL_KEY);
Madan Jampanif1b8e172015-03-23 11:42:02 -070093 return database.containsKey(name, keyCache.getUnchecked(key));
Madan Jampani7c521002015-03-23 12:23:01 -070094 }
95
96 @Override
97 public CompletableFuture<Boolean> containsValue(V value) {
98 checkNotNull(value, ERROR_NULL_VALUE);
Madan Jampanif1b8e172015-03-23 11:42:02 -070099 return database.containsValue(name, serializer.encode(value));
Madan Jampani7c521002015-03-23 12:23:01 -0700100 }
101
102 @Override
103 public CompletableFuture<Versioned<V>> get(K key) {
104 checkNotNull(key, ERROR_NULL_KEY);
Madan Jampanif1b8e172015-03-23 11:42:02 -0700105 return database.get(name, keyCache.getUnchecked(key))
Madan Jampani7c521002015-03-23 12:23:01 -0700106 .thenApply(v -> v != null
107 ? new Versioned<>(serializer.decode(v.value()), v.version(), v.creationTime()) : null);
108 }
109
110 @Override
111 public CompletableFuture<Versioned<V>> put(K key, V value) {
112 checkNotNull(key, ERROR_NULL_KEY);
113 checkNotNull(value, ERROR_NULL_VALUE);
Madan Jampani02b7fb82015-05-01 13:01:20 -0700114 checkIfUnmodifiable();
Madan Jampanif1b8e172015-03-23 11:42:02 -0700115 return database.put(name, keyCache.getUnchecked(key), serializer.encode(value))
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700116 .thenApply(this::unwrapResult)
Madan Jampani7c521002015-03-23 12:23:01 -0700117 .thenApply(v -> v != null
118 ? new Versioned<>(serializer.decode(v.value()), v.version(), v.creationTime()) : null);
119 }
120
121 @Override
122 public CompletableFuture<Versioned<V>> remove(K key) {
123 checkNotNull(key, ERROR_NULL_KEY);
Madan Jampani02b7fb82015-05-01 13:01:20 -0700124 checkIfUnmodifiable();
Madan Jampanif1b8e172015-03-23 11:42:02 -0700125 return database.remove(name, keyCache.getUnchecked(key))
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700126 .thenApply(this::unwrapResult)
Madan Jampani7c521002015-03-23 12:23:01 -0700127 .thenApply(v -> v != null
128 ? new Versioned<>(serializer.decode(v.value()), v.version(), v.creationTime()) : null);
129 }
130
131 @Override
132 public CompletableFuture<Void> clear() {
Madan Jampani02b7fb82015-05-01 13:01:20 -0700133 checkIfUnmodifiable();
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700134 return database.clear(name).thenApply(this::unwrapResult);
Madan Jampani7c521002015-03-23 12:23:01 -0700135 }
136
137 @Override
138 public CompletableFuture<Set<K>> keySet() {
Madan Jampanif1b8e172015-03-23 11:42:02 -0700139 return database.keySet(name)
Madan Jampani7c521002015-03-23 12:23:01 -0700140 .thenApply(s -> s
141 .stream()
142 .map(this::dK)
143 .collect(Collectors.toSet()));
144 }
145
146 @Override
147 public CompletableFuture<Collection<Versioned<V>>> values() {
Madan Jampanif1b8e172015-03-23 11:42:02 -0700148 return database.values(name).thenApply(c -> c
Madan Jampani7c521002015-03-23 12:23:01 -0700149 .stream()
150 .map(v -> new Versioned<V>(serializer.decode(v.value()), v.version(), v.creationTime()))
151 .collect(Collectors.toList()));
152 }
153
154 @Override
155 public CompletableFuture<Set<Entry<K, Versioned<V>>>> entrySet() {
Madan Jampanif1b8e172015-03-23 11:42:02 -0700156 return database.entrySet(name).thenApply(s -> s
Madan Jampani7c521002015-03-23 12:23:01 -0700157 .stream()
158 .map(this::fromRawEntry)
159 .collect(Collectors.toSet()));
160 }
161
162 @Override
163 public CompletableFuture<Versioned<V>> putIfAbsent(K key, V value) {
164 checkNotNull(key, ERROR_NULL_KEY);
165 checkNotNull(value, ERROR_NULL_VALUE);
Madan Jampani02b7fb82015-05-01 13:01:20 -0700166 checkIfUnmodifiable();
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700167 return database.putIfAbsent(name,
168 keyCache.getUnchecked(key),
169 serializer.encode(value))
170 .thenApply(this::unwrapResult)
171 .thenApply(v -> v != null ?
172 new Versioned<>(serializer.decode(v.value()), v.version(), v.creationTime()) : null);
Madan Jampani7c521002015-03-23 12:23:01 -0700173 }
174
175 @Override
176 public CompletableFuture<Boolean> remove(K key, V value) {
177 checkNotNull(key, ERROR_NULL_KEY);
178 checkNotNull(value, ERROR_NULL_VALUE);
Madan Jampani02b7fb82015-05-01 13:01:20 -0700179 checkIfUnmodifiable();
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700180 return database.remove(name, keyCache.getUnchecked(key), serializer.encode(value))
181 .thenApply(this::unwrapResult);
Madan Jampani7c521002015-03-23 12:23:01 -0700182 }
183
184 @Override
185 public CompletableFuture<Boolean> remove(K key, long version) {
186 checkNotNull(key, ERROR_NULL_KEY);
Madan Jampani02b7fb82015-05-01 13:01:20 -0700187 checkIfUnmodifiable();
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700188 return database.remove(name, keyCache.getUnchecked(key), version)
189 .thenApply(this::unwrapResult);
Madan Jampani7c521002015-03-23 12:23:01 -0700190
191 }
192
193 @Override
194 public CompletableFuture<Boolean> replace(K key, V oldValue, V newValue) {
195 checkNotNull(key, ERROR_NULL_KEY);
196 checkNotNull(newValue, ERROR_NULL_VALUE);
Madan Jampani02b7fb82015-05-01 13:01:20 -0700197 checkIfUnmodifiable();
Madan Jampani7c521002015-03-23 12:23:01 -0700198 byte[] existing = oldValue != null ? serializer.encode(oldValue) : null;
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700199 return database.replace(name, keyCache.getUnchecked(key), existing, serializer.encode(newValue))
200 .thenApply(this::unwrapResult);
Madan Jampani7c521002015-03-23 12:23:01 -0700201 }
202
203 @Override
204 public CompletableFuture<Boolean> replace(K key, long oldVersion, V newValue) {
205 checkNotNull(key, ERROR_NULL_KEY);
206 checkNotNull(newValue, ERROR_NULL_VALUE);
Madan Jampani02b7fb82015-05-01 13:01:20 -0700207 checkIfUnmodifiable();
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700208 return database.replace(name, keyCache.getUnchecked(key), oldVersion, serializer.encode(newValue))
209 .thenApply(this::unwrapResult);
Madan Jampani7c521002015-03-23 12:23:01 -0700210 }
211
212 private Map.Entry<K, Versioned<V>> fromRawEntry(Map.Entry<String, Versioned<byte[]>> e) {
213 return Pair.of(
214 dK(e.getKey()),
215 new Versioned<>(
216 serializer.decode(e.getValue().value()),
217 e.getValue().version(),
218 e.getValue().creationTime()));
219 }
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700220
221 private <T> T unwrapResult(Result<T> result) {
222 if (result.status() == Result.Status.LOCKED) {
223 throw new ConsistentMapException.ConcurrentModification();
224 } else if (result.success()) {
225 return result.value();
226 } else {
227 throw new IllegalStateException("Must not be here");
228 }
229 }
Madan Jampani02b7fb82015-05-01 13:01:20 -0700230
231 private void checkIfUnmodifiable() {
232 if (readOnly) {
233 throw new UnsupportedOperationException();
234 }
235 }
Madan Jampani7c521002015-03-23 12:23:01 -0700236}