blob: a3f907e1850e8752193253da3c2813ef203bd1e8 [file] [log] [blame]
Yuta HIGUCHIa8016e72014-11-18 20:19:46 -08001/*
2 * Copyright 2014 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.onlab.onos.store.service.impl;
18
19import static com.google.common.base.Preconditions.checkNotNull;
20import static com.google.common.base.Predicates.notNull;
21
22import java.util.Map;
23
24import org.onlab.onos.store.serializers.StoreSerializer;
25import org.onlab.onos.store.service.DatabaseAdminService;
Yuta HIGUCHI45e5cd12014-11-24 14:57:13 -080026import org.onlab.onos.store.service.DatabaseException;
Yuta HIGUCHIa8016e72014-11-18 20:19:46 -080027import org.onlab.onos.store.service.DatabaseService;
28import org.onlab.onos.store.service.VersionedValue;
29
30import com.google.common.base.Function;
31import com.google.common.cache.CacheBuilder;
32import com.google.common.cache.CacheLoader;
33import com.google.common.cache.LoadingCache;
34import com.google.common.collect.FluentIterable;
35
36/**
37 * Map like interface wrapper around DatabaseService.
38 *
39 * @param <K> Key type of the map.
40 * The type must have toString(), which can uniquely identify the entry.
41 * @param <V> Value type
42 */
43public class CMap<K, V> {
44
45 @SuppressWarnings("unused")
46 private final DatabaseAdminService dbAdminService;
47
48 private final DatabaseService dbService;
49
50 private final String tableName;
51 private final StoreSerializer serializer;
52
53 private final LoadingCache<K, String> keyCache;
54
55 /**
56 * Creates a CMap instance.
57 * It will create the table if necessary.
58 *
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080059 * @param dbAdminService DatabaseAdminService to use for this instance
60 * @param dbService DatabaseService to use for this instance
Yuta HIGUCHIa8016e72014-11-18 20:19:46 -080061 * @param tableName table which this Map corresponds to
62 * @param serializer Value serializer
63 */
64 public CMap(DatabaseAdminService dbAdminService,
65 DatabaseService dbService,
66 String tableName,
67 StoreSerializer serializer) {
68
69 this.dbAdminService = checkNotNull(dbAdminService);
70 this.dbService = checkNotNull(dbService);
71 this.tableName = checkNotNull(tableName);
72 this.serializer = checkNotNull(serializer);
73
Yuta HIGUCHI45e5cd12014-11-24 14:57:13 -080074 boolean tableReady = false;
75 do {
76 try {
77 if (!dbAdminService.listTables().contains(tableName)) {
78 dbAdminService.createTable(tableName);
79 }
80 tableReady = true;
81 } catch (DatabaseException e) {
82 try {
83 Thread.sleep(200);
84 } catch (InterruptedException e1) {
85 throw new DatabaseException(e1);
86 }
87 }
88 } while (!tableReady);
Yuta HIGUCHIa8016e72014-11-18 20:19:46 -080089
90 keyCache = CacheBuilder.newBuilder()
91 .softValues()
92 .build(new CacheLoader<K, String>() {
93
94 @Override
95 public String load(K key) {
96 return key.toString();
97 }
98 });
99 }
100
101 protected String sK(K key) {
102 return keyCache.getUnchecked(key);
103 }
104
105 protected byte[] sV(V val) {
106 return serializer.encode(val);
107 }
108
109 protected V dV(byte[] valBytes) {
110 return serializer.decode(valBytes);
111 }
112
113 /**
114 * Puts an entry to the map, if not already present.
115 *
116 * @param key the key of the value to put if absent
117 * @param value the value to be put if previous value does not exist
118 * @return true if put was successful.
119 */
120 public boolean putIfAbsent(K key, V value) {
121 return dbService.putIfAbsent(tableName, sK(key), sV(value));
122 }
123
124 /**
125 * Removes an entry associated to specified key.
126 *
127 * @param key key of the value to remove
128 * @return previous value in the map for the key
129 */
130 public V remove(K key) {
131 VersionedValue removed = dbService.remove(tableName, sK(key));
132 if (removed == null) {
133 return null;
134 }
135 return dV(removed.value());
136 }
137
138 /**
139 * Returns the size of the map.
140 *
141 * @return size of the map
142 */
143 public long size() {
144 // TODO this is very inefficient
145 return dbService.getAll(tableName).size();
146 }
147
148 /**
149 * Returns all the values contained in the map.
150 *
151 * @return values containd in this map
152 */
153 public Iterable<V> values() {
154 Map<String, VersionedValue> all = dbService.getAll(tableName);
155 return FluentIterable.from(all.values())
156 .transform(new Function<VersionedValue, V>() {
157
158 @Override
159 public V apply(VersionedValue input) {
160 if (input == null) {
161 return null;
162 }
163 return dV(input.value());
164 }
165 })
166 .filter(notNull());
167 }
168
169 /**
170 * Gets the value in the map.
171 *
172 * @param key to get from the map
173 * @return value associated with the key, null if not such entry
174 */
175 public V get(K key) {
176 VersionedValue vv = dbService.get(tableName, sK(key));
177 if (vv == null) {
178 return null;
179 }
180 return dV(vv.value());
181 }
182
183 /**
184 * Replaces the value in the map if the value matches the expected.
185 *
186 * @param key of the entry to replace
187 * @param oldVal value expected to be in the map
188 * @param newVal value to be replaced with
189 * @return true if successfully replaced
190 */
191 public boolean replace(K key, V oldVal, V newVal) {
192 return dbService.putIfValueMatches(tableName, sK(key), sV(oldVal), sV(newVal));
193 }
194
195 /**
196 * Puts a value int the map.
197 *
198 * @param key key with which the specified value is to be associated
199 * @param value value to be associated with the specified key
200 * @return previous value or null if not such entry
201 */
202 public V put(K key, V value) {
203 VersionedValue vv = dbService.put(tableName, sK(key), sV(value));
204 if (vv == null) {
205 return null;
206 }
207 return dV(vv.value());
208 }
209}