blob: 2b20f5337bf7be21408596cbe9938f8efd1660e4 [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;
9import java.util.Set;
10
11import net.kuujo.copycat.state.Initializer;
12import net.kuujo.copycat.state.StateContext;
13
14/**
15 * Default database state.
16 *
17 * @param <K> key type
18 * @param <V> value type
19 */
20public class DefaultDatabaseState<K, V> implements DatabaseState<K, V> {
21
22 private Long nextVersion;
23 private Map<String, Map<K, Versioned<V>>> tables;
24
25 @Initializer
26 @Override
27 public void init(StateContext<DatabaseState<K, V>> context) {
28 tables = context.get("tables");
29 if (tables == null) {
30 tables = new HashMap<>();
31 context.put("tables", tables);
32 }
33 nextVersion = context.get("nextVersion");
34 if (nextVersion == null) {
35 nextVersion = new Long(0);
36 context.put("nextVersion", nextVersion);
37 }
38 }
39
40 private Map<K, Versioned<V>> getTableMap(String tableName) {
41 Map<K, Versioned<V>> table = tables.get(tableName);
42 if (table == null) {
43 table = new HashMap<>();
44 tables.put(tableName, table);
45 }
46 return table;
47 }
48
49 @Override
50 public int size(String tableName) {
51 return getTableMap(tableName).size();
52 }
53
54 @Override
55 public boolean isEmpty(String tableName) {
56 return getTableMap(tableName).isEmpty();
57 }
58
59 @Override
60 public boolean containsKey(String tableName, K key) {
61 return getTableMap(tableName).containsKey(key);
62 }
63
64 @Override
65 public boolean containsValue(String tableName, V value) {
66 return getTableMap(tableName).values().stream().anyMatch(v -> checkEquality(v.value(), value));
67 }
68
69 @Override
70 public Versioned<V> get(String tableName, K key) {
71 return getTableMap(tableName).get(key);
72 }
73
74 @Override
75 public Versioned<V> put(String tableName, K key, V value) {
76 return getTableMap(tableName).put(key, new Versioned<>(value, ++nextVersion));
77 }
78
79 @Override
80 public Versioned<V> remove(String tableName, K key) {
81 return getTableMap(tableName).remove(key);
82 }
83
84 @Override
85 public void clear(String tableName) {
86 getTableMap(tableName).clear();
87 }
88
89 @Override
90 public Set<K> keySet(String tableName) {
91 return getTableMap(tableName).keySet();
92 }
93
94 @Override
95 public Collection<Versioned<V>> values(String tableName) {
96 return getTableMap(tableName).values();
97 }
98
99 @Override
100 public Set<Entry<K, Versioned<V>>> entrySet(String tableName) {
101 return getTableMap(tableName).entrySet();
102 }
103
104 @Override
105 public Versioned<V> putIfAbsent(String tableName, K key, V value) {
106 Versioned<V> existingValue = getTableMap(tableName).get(key);
107 return existingValue != null ? existingValue : put(tableName, key, value);
108 }
109
110 @Override
111 public boolean remove(String tableName, K key, V value) {
112 Versioned<V> existing = getTableMap(tableName).get(key);
113 if (existing != null && existing.value().equals(value)) {
114 getTableMap(tableName).remove(key);
115 return true;
116 }
117 return false;
118 }
119
120 @Override
121 public boolean remove(String tableName, K key, long version) {
122 Versioned<V> existing = getTableMap(tableName).get(key);
123 if (existing != null && existing.version() == version) {
124 remove(tableName, key);
125 return true;
126 }
127 return false;
128 }
129
130 @Override
131 public boolean replace(String tableName, K key, V oldValue, V newValue) {
132 Versioned<V> existing = getTableMap(tableName).get(key);
133 if (existing != null && existing.value().equals(oldValue)) {
134 put(tableName, key, newValue);
135 return true;
136 }
137 return false;
138 }
139
140 @Override
141 public boolean replace(String tableName, K key, long oldVersion, V newValue) {
142 Versioned<V> existing = getTableMap(tableName).get(key);
143 if (existing != null && existing.version() == oldVersion) {
144 put(tableName, key, newValue);
145 return true;
146 }
147 return false;
148 }
149
150 @Override
151 public boolean batchUpdate(List<UpdateOperation<K, V>> updates) {
152 if (updates.stream().anyMatch(update -> !checkIfUpdateIsPossible(update))) {
153 return false;
154 } else {
155 updates.stream().forEach(this::doUpdate);
156 return true;
157 }
158 }
159
160 private void doUpdate(UpdateOperation<K, V> update) {
161 String tableName = update.tableName();
162 K key = update.key();
163 switch (update.type()) {
164 case PUT:
165 put(tableName, key, update.value());
166 return;
167 case REMOVE:
168 remove(tableName, key);
169 return;
170 case PUT_IF_ABSENT:
171 putIfAbsent(tableName, key, update.value());
172 return;
173 case PUT_IF_VERSION_MATCH:
174 replace(tableName, key, update.currentValue(), update.value());
175 return;
176 case PUT_IF_VALUE_MATCH:
177 replace(tableName, key, update.currentVersion(), update.value());
178 return;
179 case REMOVE_IF_VERSION_MATCH:
180 remove(tableName, key, update.currentVersion());
181 return;
182 case REMOVE_IF_VALUE_MATCH:
183 remove(tableName, key, update.currentValue());
184 return;
185 default:
186 throw new IllegalStateException("Unsupported type: " + update.type());
187 }
188 }
189
190 private boolean checkIfUpdateIsPossible(UpdateOperation<K, V> update) {
191 Versioned<V> existingEntry = get(update.tableName(), update.key());
192 switch (update.type()) {
193 case PUT:
194 case REMOVE:
195 return true;
196 case PUT_IF_ABSENT:
197 return existingEntry == null;
198 case PUT_IF_VERSION_MATCH:
199 return existingEntry != null && existingEntry.version() == update.currentVersion();
200 case PUT_IF_VALUE_MATCH:
201 return existingEntry != null && existingEntry.value().equals(update.currentValue());
202 case REMOVE_IF_VERSION_MATCH:
203 return existingEntry == null || existingEntry.version() == update.currentVersion();
204 case REMOVE_IF_VALUE_MATCH:
205 return existingEntry == null || existingEntry.value().equals(update.currentValue());
206 default:
207 throw new IllegalStateException("Unsupported type: " + update.type());
208 }
209 }
210
211 private boolean checkEquality(V value1, V value2) {
212 if (value1 instanceof byte[]) {
213 return Arrays.equals((byte[]) value1, (byte[]) value2);
214 }
215 return value1.equals(value2);
216 }
217}