blob: e63a3d83f7e6076d27ba851f29c233ed41d84c7e [file] [log] [blame]
Madan Jampani25461112015-02-17 14:17:29 -08001/*
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
Madan Jampani94c23532015-02-05 17:40:01 -080017package org.onosproject.store.consistent.impl;
18
19import java.util.Arrays;
20import java.util.Collection;
21import java.util.HashMap;
Madan Jampania89f8f92015-04-01 14:39:54 -070022import java.util.HashSet;
Madan Jampani94c23532015-02-05 17:40:01 -080023import java.util.List;
24import java.util.Map;
25import java.util.Map.Entry;
Madan Jampani393e0f02015-02-12 07:35:39 +053026import java.util.stream.Collectors;
Madan Jampani94c23532015-02-05 17:40:01 -080027import java.util.Set;
28
Madan Jampani393e0f02015-02-12 07:35:39 +053029import org.apache.commons.lang3.tuple.Pair;
30import org.onosproject.store.service.UpdateOperation;
31import org.onosproject.store.service.Versioned;
32
33import com.google.common.collect.ImmutableList;
34import com.google.common.collect.ImmutableSet;
35
Madan Jampani94c23532015-02-05 17:40:01 -080036import net.kuujo.copycat.state.Initializer;
37import net.kuujo.copycat.state.StateContext;
38
39/**
40 * Default database state.
41 *
42 * @param <K> key type
43 * @param <V> value type
44 */
45public class DefaultDatabaseState<K, V> implements DatabaseState<K, V> {
46
47 private Long nextVersion;
48 private Map<String, Map<K, Versioned<V>>> tables;
49
50 @Initializer
51 @Override
52 public void init(StateContext<DatabaseState<K, V>> context) {
53 tables = context.get("tables");
54 if (tables == null) {
55 tables = new HashMap<>();
56 context.put("tables", tables);
57 }
58 nextVersion = context.get("nextVersion");
59 if (nextVersion == null) {
60 nextVersion = new Long(0);
61 context.put("nextVersion", nextVersion);
62 }
63 }
64
65 private Map<K, Versioned<V>> getTableMap(String tableName) {
66 Map<K, Versioned<V>> table = tables.get(tableName);
67 if (table == null) {
68 table = new HashMap<>();
69 tables.put(tableName, table);
70 }
71 return table;
72 }
73
74 @Override
Madan Jampania89f8f92015-04-01 14:39:54 -070075 public Set<String> tableNames() {
76 return new HashSet<>(tables.keySet());
77 }
78
79 @Override
Madan Jampani94c23532015-02-05 17:40:01 -080080 public int size(String tableName) {
81 return getTableMap(tableName).size();
82 }
83
84 @Override
85 public boolean isEmpty(String tableName) {
86 return getTableMap(tableName).isEmpty();
87 }
88
89 @Override
90 public boolean containsKey(String tableName, K key) {
91 return getTableMap(tableName).containsKey(key);
92 }
93
94 @Override
95 public boolean containsValue(String tableName, V value) {
96 return getTableMap(tableName).values().stream().anyMatch(v -> checkEquality(v.value(), value));
97 }
98
99 @Override
100 public Versioned<V> get(String tableName, K key) {
101 return getTableMap(tableName).get(key);
102 }
103
104 @Override
105 public Versioned<V> put(String tableName, K key, V value) {
106 return getTableMap(tableName).put(key, new Versioned<>(value, ++nextVersion));
107 }
108
109 @Override
110 public Versioned<V> remove(String tableName, K key) {
111 return getTableMap(tableName).remove(key);
112 }
113
114 @Override
115 public void clear(String tableName) {
116 getTableMap(tableName).clear();
117 }
118
119 @Override
120 public Set<K> keySet(String tableName) {
Madan Jampani393e0f02015-02-12 07:35:39 +0530121 return ImmutableSet.copyOf(getTableMap(tableName).keySet());
Madan Jampani94c23532015-02-05 17:40:01 -0800122 }
123
124 @Override
125 public Collection<Versioned<V>> values(String tableName) {
Madan Jampani393e0f02015-02-12 07:35:39 +0530126 return ImmutableList.copyOf(getTableMap(tableName).values());
Madan Jampani94c23532015-02-05 17:40:01 -0800127 }
128
129 @Override
130 public Set<Entry<K, Versioned<V>>> entrySet(String tableName) {
Madan Jampani393e0f02015-02-12 07:35:39 +0530131 return ImmutableSet.copyOf(getTableMap(tableName)
132 .entrySet()
133 .stream()
134 .map(entry -> Pair.of(entry.getKey(), entry.getValue()))
135 .collect(Collectors.toSet()));
Madan Jampani94c23532015-02-05 17:40:01 -0800136 }
137
138 @Override
139 public Versioned<V> putIfAbsent(String tableName, K key, V value) {
140 Versioned<V> existingValue = getTableMap(tableName).get(key);
141 return existingValue != null ? existingValue : put(tableName, key, value);
142 }
143
144 @Override
145 public boolean remove(String tableName, K key, V value) {
146 Versioned<V> existing = getTableMap(tableName).get(key);
Madan Jampani393e0f02015-02-12 07:35:39 +0530147 if (existing != null && checkEquality(existing.value(), value)) {
Madan Jampani94c23532015-02-05 17:40:01 -0800148 getTableMap(tableName).remove(key);
149 return true;
150 }
151 return false;
152 }
153
154 @Override
155 public boolean remove(String tableName, K key, long version) {
156 Versioned<V> existing = getTableMap(tableName).get(key);
157 if (existing != null && existing.version() == version) {
158 remove(tableName, key);
159 return true;
160 }
161 return false;
162 }
163
164 @Override
165 public boolean replace(String tableName, K key, V oldValue, V newValue) {
166 Versioned<V> existing = getTableMap(tableName).get(key);
Madan Jampani393e0f02015-02-12 07:35:39 +0530167 if (existing != null && checkEquality(existing.value(), oldValue)) {
Madan Jampani94c23532015-02-05 17:40:01 -0800168 put(tableName, key, newValue);
169 return true;
170 }
171 return false;
172 }
173
174 @Override
175 public boolean replace(String tableName, K key, long oldVersion, V newValue) {
176 Versioned<V> existing = getTableMap(tableName).get(key);
177 if (existing != null && existing.version() == oldVersion) {
178 put(tableName, key, newValue);
179 return true;
180 }
181 return false;
182 }
183
184 @Override
185 public boolean batchUpdate(List<UpdateOperation<K, V>> updates) {
186 if (updates.stream().anyMatch(update -> !checkIfUpdateIsPossible(update))) {
187 return false;
188 } else {
189 updates.stream().forEach(this::doUpdate);
190 return true;
191 }
192 }
193
194 private void doUpdate(UpdateOperation<K, V> update) {
195 String tableName = update.tableName();
196 K key = update.key();
197 switch (update.type()) {
198 case PUT:
199 put(tableName, key, update.value());
200 return;
201 case REMOVE:
202 remove(tableName, key);
203 return;
204 case PUT_IF_ABSENT:
205 putIfAbsent(tableName, key, update.value());
206 return;
207 case PUT_IF_VERSION_MATCH:
208 replace(tableName, key, update.currentValue(), update.value());
209 return;
210 case PUT_IF_VALUE_MATCH:
211 replace(tableName, key, update.currentVersion(), update.value());
212 return;
213 case REMOVE_IF_VERSION_MATCH:
214 remove(tableName, key, update.currentVersion());
215 return;
216 case REMOVE_IF_VALUE_MATCH:
217 remove(tableName, key, update.currentValue());
218 return;
219 default:
220 throw new IllegalStateException("Unsupported type: " + update.type());
221 }
222 }
223
224 private boolean checkIfUpdateIsPossible(UpdateOperation<K, V> update) {
225 Versioned<V> existingEntry = get(update.tableName(), update.key());
226 switch (update.type()) {
227 case PUT:
228 case REMOVE:
229 return true;
230 case PUT_IF_ABSENT:
231 return existingEntry == null;
232 case PUT_IF_VERSION_MATCH:
233 return existingEntry != null && existingEntry.version() == update.currentVersion();
234 case PUT_IF_VALUE_MATCH:
Madan Jampani393e0f02015-02-12 07:35:39 +0530235 return existingEntry != null && checkEquality(existingEntry.value(), update.currentValue());
Madan Jampani94c23532015-02-05 17:40:01 -0800236 case REMOVE_IF_VERSION_MATCH:
237 return existingEntry == null || existingEntry.version() == update.currentVersion();
238 case REMOVE_IF_VALUE_MATCH:
Madan Jampani393e0f02015-02-12 07:35:39 +0530239 return existingEntry == null || checkEquality(existingEntry.value(), update.currentValue());
Madan Jampani94c23532015-02-05 17:40:01 -0800240 default:
241 throw new IllegalStateException("Unsupported type: " + update.type());
242 }
243 }
244
245 private boolean checkEquality(V value1, V value2) {
246 if (value1 instanceof byte[]) {
247 return Arrays.equals((byte[]) value1, (byte[]) value2);
248 }
249 return value1.equals(value2);
250 }
251}