blob: 452f80e2f35445bb5e109280e75bcd20dc7203d5 [file] [log] [blame]
Madan Jampani94c23532015-02-05 17:40:01 -08001package org.onosproject.store.consistent.impl;
2
3import java.util.Arrays;
4import java.util.Collection;
5import java.util.HashMap;
6import java.util.List;
7import java.util.Map;
8import java.util.Map.Entry;
Madan Jampani393e0f02015-02-12 07:35:39 +05309import java.util.stream.Collectors;
Madan Jampani94c23532015-02-05 17:40:01 -080010import java.util.Set;
11
Madan Jampani393e0f02015-02-12 07:35:39 +053012import org.apache.commons.lang3.tuple.Pair;
13import org.onosproject.store.service.UpdateOperation;
14import org.onosproject.store.service.Versioned;
15
16import com.google.common.collect.ImmutableList;
17import com.google.common.collect.ImmutableSet;
18
Madan Jampani94c23532015-02-05 17:40:01 -080019import net.kuujo.copycat.state.Initializer;
20import net.kuujo.copycat.state.StateContext;
21
22/**
23 * Default database state.
24 *
25 * @param <K> key type
26 * @param <V> value type
27 */
28public class DefaultDatabaseState<K, V> implements DatabaseState<K, V> {
29
30 private Long nextVersion;
31 private Map<String, Map<K, Versioned<V>>> tables;
32
33 @Initializer
34 @Override
35 public void init(StateContext<DatabaseState<K, V>> context) {
36 tables = context.get("tables");
37 if (tables == null) {
38 tables = new HashMap<>();
39 context.put("tables", tables);
40 }
41 nextVersion = context.get("nextVersion");
42 if (nextVersion == null) {
43 nextVersion = new Long(0);
44 context.put("nextVersion", nextVersion);
45 }
46 }
47
48 private Map<K, Versioned<V>> getTableMap(String tableName) {
49 Map<K, Versioned<V>> table = tables.get(tableName);
50 if (table == null) {
51 table = new HashMap<>();
52 tables.put(tableName, table);
53 }
54 return table;
55 }
56
57 @Override
58 public int size(String tableName) {
59 return getTableMap(tableName).size();
60 }
61
62 @Override
63 public boolean isEmpty(String tableName) {
64 return getTableMap(tableName).isEmpty();
65 }
66
67 @Override
68 public boolean containsKey(String tableName, K key) {
69 return getTableMap(tableName).containsKey(key);
70 }
71
72 @Override
73 public boolean containsValue(String tableName, V value) {
74 return getTableMap(tableName).values().stream().anyMatch(v -> checkEquality(v.value(), value));
75 }
76
77 @Override
78 public Versioned<V> get(String tableName, K key) {
79 return getTableMap(tableName).get(key);
80 }
81
82 @Override
83 public Versioned<V> put(String tableName, K key, V value) {
84 return getTableMap(tableName).put(key, new Versioned<>(value, ++nextVersion));
85 }
86
87 @Override
88 public Versioned<V> remove(String tableName, K key) {
89 return getTableMap(tableName).remove(key);
90 }
91
92 @Override
93 public void clear(String tableName) {
94 getTableMap(tableName).clear();
95 }
96
97 @Override
98 public Set<K> keySet(String tableName) {
Madan Jampani393e0f02015-02-12 07:35:39 +053099 return ImmutableSet.copyOf(getTableMap(tableName).keySet());
Madan Jampani94c23532015-02-05 17:40:01 -0800100 }
101
102 @Override
103 public Collection<Versioned<V>> values(String tableName) {
Madan Jampani393e0f02015-02-12 07:35:39 +0530104 return ImmutableList.copyOf(getTableMap(tableName).values());
Madan Jampani94c23532015-02-05 17:40:01 -0800105 }
106
107 @Override
108 public Set<Entry<K, Versioned<V>>> entrySet(String tableName) {
Madan Jampani393e0f02015-02-12 07:35:39 +0530109 return ImmutableSet.copyOf(getTableMap(tableName)
110 .entrySet()
111 .stream()
112 .map(entry -> Pair.of(entry.getKey(), entry.getValue()))
113 .collect(Collectors.toSet()));
Madan Jampani94c23532015-02-05 17:40:01 -0800114 }
115
116 @Override
117 public Versioned<V> putIfAbsent(String tableName, K key, V value) {
118 Versioned<V> existingValue = getTableMap(tableName).get(key);
119 return existingValue != null ? existingValue : put(tableName, key, value);
120 }
121
122 @Override
123 public boolean remove(String tableName, K key, V value) {
124 Versioned<V> existing = getTableMap(tableName).get(key);
Madan Jampani393e0f02015-02-12 07:35:39 +0530125 if (existing != null && checkEquality(existing.value(), value)) {
Madan Jampani94c23532015-02-05 17:40:01 -0800126 getTableMap(tableName).remove(key);
127 return true;
128 }
129 return false;
130 }
131
132 @Override
133 public boolean remove(String tableName, K key, long version) {
134 Versioned<V> existing = getTableMap(tableName).get(key);
135 if (existing != null && existing.version() == version) {
136 remove(tableName, key);
137 return true;
138 }
139 return false;
140 }
141
142 @Override
143 public boolean replace(String tableName, K key, V oldValue, V newValue) {
144 Versioned<V> existing = getTableMap(tableName).get(key);
Madan Jampani393e0f02015-02-12 07:35:39 +0530145 if (existing != null && checkEquality(existing.value(), oldValue)) {
Madan Jampani94c23532015-02-05 17:40:01 -0800146 put(tableName, key, newValue);
147 return true;
148 }
149 return false;
150 }
151
152 @Override
153 public boolean replace(String tableName, K key, long oldVersion, V newValue) {
154 Versioned<V> existing = getTableMap(tableName).get(key);
155 if (existing != null && existing.version() == oldVersion) {
156 put(tableName, key, newValue);
157 return true;
158 }
159 return false;
160 }
161
162 @Override
163 public boolean batchUpdate(List<UpdateOperation<K, V>> updates) {
164 if (updates.stream().anyMatch(update -> !checkIfUpdateIsPossible(update))) {
165 return false;
166 } else {
167 updates.stream().forEach(this::doUpdate);
168 return true;
169 }
170 }
171
172 private void doUpdate(UpdateOperation<K, V> update) {
173 String tableName = update.tableName();
174 K key = update.key();
175 switch (update.type()) {
176 case PUT:
177 put(tableName, key, update.value());
178 return;
179 case REMOVE:
180 remove(tableName, key);
181 return;
182 case PUT_IF_ABSENT:
183 putIfAbsent(tableName, key, update.value());
184 return;
185 case PUT_IF_VERSION_MATCH:
186 replace(tableName, key, update.currentValue(), update.value());
187 return;
188 case PUT_IF_VALUE_MATCH:
189 replace(tableName, key, update.currentVersion(), update.value());
190 return;
191 case REMOVE_IF_VERSION_MATCH:
192 remove(tableName, key, update.currentVersion());
193 return;
194 case REMOVE_IF_VALUE_MATCH:
195 remove(tableName, key, update.currentValue());
196 return;
197 default:
198 throw new IllegalStateException("Unsupported type: " + update.type());
199 }
200 }
201
202 private boolean checkIfUpdateIsPossible(UpdateOperation<K, V> update) {
203 Versioned<V> existingEntry = get(update.tableName(), update.key());
204 switch (update.type()) {
205 case PUT:
206 case REMOVE:
207 return true;
208 case PUT_IF_ABSENT:
209 return existingEntry == null;
210 case PUT_IF_VERSION_MATCH:
211 return existingEntry != null && existingEntry.version() == update.currentVersion();
212 case PUT_IF_VALUE_MATCH:
Madan Jampani393e0f02015-02-12 07:35:39 +0530213 return existingEntry != null && checkEquality(existingEntry.value(), update.currentValue());
Madan Jampani94c23532015-02-05 17:40:01 -0800214 case REMOVE_IF_VERSION_MATCH:
215 return existingEntry == null || existingEntry.version() == update.currentVersion();
216 case REMOVE_IF_VALUE_MATCH:
Madan Jampani393e0f02015-02-12 07:35:39 +0530217 return existingEntry == null || checkEquality(existingEntry.value(), update.currentValue());
Madan Jampani94c23532015-02-05 17:40:01 -0800218 default:
219 throw new IllegalStateException("Unsupported type: " + update.type());
220 }
221 }
222
223 private boolean checkEquality(V value1, V value2) {
224 if (value1 instanceof byte[]) {
225 return Arrays.equals((byte[]) value1, (byte[]) value2);
226 }
227 return value1.equals(value2);
228 }
229}