blob: 837852d3f58d9266d27c05d2a332de66ed54c86d [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;
22import java.util.List;
23import java.util.Map;
24import java.util.Map.Entry;
Madan Jampani393e0f02015-02-12 07:35:39 +053025import java.util.stream.Collectors;
Madan Jampani94c23532015-02-05 17:40:01 -080026import java.util.Set;
27
Madan Jampani393e0f02015-02-12 07:35:39 +053028import org.apache.commons.lang3.tuple.Pair;
29import org.onosproject.store.service.UpdateOperation;
30import org.onosproject.store.service.Versioned;
31
32import com.google.common.collect.ImmutableList;
33import com.google.common.collect.ImmutableSet;
34
Madan Jampani94c23532015-02-05 17:40:01 -080035import net.kuujo.copycat.state.Initializer;
36import net.kuujo.copycat.state.StateContext;
37
38/**
39 * Default database state.
40 *
41 * @param <K> key type
42 * @param <V> value type
43 */
44public class DefaultDatabaseState<K, V> implements DatabaseState<K, V> {
45
46 private Long nextVersion;
47 private Map<String, Map<K, Versioned<V>>> tables;
48
49 @Initializer
50 @Override
51 public void init(StateContext<DatabaseState<K, V>> context) {
52 tables = context.get("tables");
53 if (tables == null) {
54 tables = new HashMap<>();
55 context.put("tables", tables);
56 }
57 nextVersion = context.get("nextVersion");
58 if (nextVersion == null) {
59 nextVersion = new Long(0);
60 context.put("nextVersion", nextVersion);
61 }
62 }
63
64 private Map<K, Versioned<V>> getTableMap(String tableName) {
65 Map<K, Versioned<V>> table = tables.get(tableName);
66 if (table == null) {
67 table = new HashMap<>();
68 tables.put(tableName, table);
69 }
70 return table;
71 }
72
73 @Override
74 public int size(String tableName) {
75 return getTableMap(tableName).size();
76 }
77
78 @Override
79 public boolean isEmpty(String tableName) {
80 return getTableMap(tableName).isEmpty();
81 }
82
83 @Override
84 public boolean containsKey(String tableName, K key) {
85 return getTableMap(tableName).containsKey(key);
86 }
87
88 @Override
89 public boolean containsValue(String tableName, V value) {
90 return getTableMap(tableName).values().stream().anyMatch(v -> checkEquality(v.value(), value));
91 }
92
93 @Override
94 public Versioned<V> get(String tableName, K key) {
95 return getTableMap(tableName).get(key);
96 }
97
98 @Override
99 public Versioned<V> put(String tableName, K key, V value) {
100 return getTableMap(tableName).put(key, new Versioned<>(value, ++nextVersion));
101 }
102
103 @Override
104 public Versioned<V> remove(String tableName, K key) {
105 return getTableMap(tableName).remove(key);
106 }
107
108 @Override
109 public void clear(String tableName) {
110 getTableMap(tableName).clear();
111 }
112
113 @Override
114 public Set<K> keySet(String tableName) {
Madan Jampani393e0f02015-02-12 07:35:39 +0530115 return ImmutableSet.copyOf(getTableMap(tableName).keySet());
Madan Jampani94c23532015-02-05 17:40:01 -0800116 }
117
118 @Override
119 public Collection<Versioned<V>> values(String tableName) {
Madan Jampani393e0f02015-02-12 07:35:39 +0530120 return ImmutableList.copyOf(getTableMap(tableName).values());
Madan Jampani94c23532015-02-05 17:40:01 -0800121 }
122
123 @Override
124 public Set<Entry<K, Versioned<V>>> entrySet(String tableName) {
Madan Jampani393e0f02015-02-12 07:35:39 +0530125 return ImmutableSet.copyOf(getTableMap(tableName)
126 .entrySet()
127 .stream()
128 .map(entry -> Pair.of(entry.getKey(), entry.getValue()))
129 .collect(Collectors.toSet()));
Madan Jampani94c23532015-02-05 17:40:01 -0800130 }
131
132 @Override
133 public Versioned<V> putIfAbsent(String tableName, K key, V value) {
134 Versioned<V> existingValue = getTableMap(tableName).get(key);
135 return existingValue != null ? existingValue : put(tableName, key, value);
136 }
137
138 @Override
139 public boolean remove(String tableName, K key, V value) {
140 Versioned<V> existing = getTableMap(tableName).get(key);
Madan Jampani393e0f02015-02-12 07:35:39 +0530141 if (existing != null && checkEquality(existing.value(), value)) {
Madan Jampani94c23532015-02-05 17:40:01 -0800142 getTableMap(tableName).remove(key);
143 return true;
144 }
145 return false;
146 }
147
148 @Override
149 public boolean remove(String tableName, K key, long version) {
150 Versioned<V> existing = getTableMap(tableName).get(key);
151 if (existing != null && existing.version() == version) {
152 remove(tableName, key);
153 return true;
154 }
155 return false;
156 }
157
158 @Override
159 public boolean replace(String tableName, K key, V oldValue, V newValue) {
160 Versioned<V> existing = getTableMap(tableName).get(key);
Madan Jampani393e0f02015-02-12 07:35:39 +0530161 if (existing != null && checkEquality(existing.value(), oldValue)) {
Madan Jampani94c23532015-02-05 17:40:01 -0800162 put(tableName, key, newValue);
163 return true;
164 }
165 return false;
166 }
167
168 @Override
169 public boolean replace(String tableName, K key, long oldVersion, V newValue) {
170 Versioned<V> existing = getTableMap(tableName).get(key);
171 if (existing != null && existing.version() == oldVersion) {
172 put(tableName, key, newValue);
173 return true;
174 }
175 return false;
176 }
177
178 @Override
179 public boolean batchUpdate(List<UpdateOperation<K, V>> updates) {
180 if (updates.stream().anyMatch(update -> !checkIfUpdateIsPossible(update))) {
181 return false;
182 } else {
183 updates.stream().forEach(this::doUpdate);
184 return true;
185 }
186 }
187
188 private void doUpdate(UpdateOperation<K, V> update) {
189 String tableName = update.tableName();
190 K key = update.key();
191 switch (update.type()) {
192 case PUT:
193 put(tableName, key, update.value());
194 return;
195 case REMOVE:
196 remove(tableName, key);
197 return;
198 case PUT_IF_ABSENT:
199 putIfAbsent(tableName, key, update.value());
200 return;
201 case PUT_IF_VERSION_MATCH:
202 replace(tableName, key, update.currentValue(), update.value());
203 return;
204 case PUT_IF_VALUE_MATCH:
205 replace(tableName, key, update.currentVersion(), update.value());
206 return;
207 case REMOVE_IF_VERSION_MATCH:
208 remove(tableName, key, update.currentVersion());
209 return;
210 case REMOVE_IF_VALUE_MATCH:
211 remove(tableName, key, update.currentValue());
212 return;
213 default:
214 throw new IllegalStateException("Unsupported type: " + update.type());
215 }
216 }
217
218 private boolean checkIfUpdateIsPossible(UpdateOperation<K, V> update) {
219 Versioned<V> existingEntry = get(update.tableName(), update.key());
220 switch (update.type()) {
221 case PUT:
222 case REMOVE:
223 return true;
224 case PUT_IF_ABSENT:
225 return existingEntry == null;
226 case PUT_IF_VERSION_MATCH:
227 return existingEntry != null && existingEntry.version() == update.currentVersion();
228 case PUT_IF_VALUE_MATCH:
Madan Jampani393e0f02015-02-12 07:35:39 +0530229 return existingEntry != null && checkEquality(existingEntry.value(), update.currentValue());
Madan Jampani94c23532015-02-05 17:40:01 -0800230 case REMOVE_IF_VERSION_MATCH:
231 return existingEntry == null || existingEntry.version() == update.currentVersion();
232 case REMOVE_IF_VALUE_MATCH:
Madan Jampani393e0f02015-02-12 07:35:39 +0530233 return existingEntry == null || checkEquality(existingEntry.value(), update.currentValue());
Madan Jampani94c23532015-02-05 17:40:01 -0800234 default:
235 throw new IllegalStateException("Unsupported type: " + update.type());
236 }
237 }
238
239 private boolean checkEquality(V value1, V value2) {
240 if (value1 instanceof byte[]) {
241 return Arrays.equals((byte[]) value1, (byte[]) value2);
242 }
243 return value1.equals(value2);
244 }
245}