blob: dc4d2e477aab67cbab58f387516aafa60958b890 [file] [log] [blame]
Thomas Vachuska96d55b12015-05-11 08:52:03 -07001/*
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 */
16package org.onosproject.incubator.store.config.impl;
17
18import com.fasterxml.jackson.databind.ObjectMapper;
19import com.fasterxml.jackson.databind.node.BooleanNode;
20import com.fasterxml.jackson.databind.node.DoubleNode;
21import com.fasterxml.jackson.databind.node.JsonNodeFactory;
22import com.fasterxml.jackson.databind.node.LongNode;
23import com.fasterxml.jackson.databind.node.ObjectNode;
24import com.fasterxml.jackson.databind.node.ShortNode;
25import com.fasterxml.jackson.databind.node.TextNode;
26import com.google.common.collect.ImmutableSet;
27import com.google.common.collect.Maps;
28import org.apache.felix.scr.annotations.Activate;
29import org.apache.felix.scr.annotations.Component;
30import org.apache.felix.scr.annotations.Deactivate;
31import org.apache.felix.scr.annotations.Reference;
32import org.apache.felix.scr.annotations.ReferenceCardinality;
33import org.apache.felix.scr.annotations.Service;
34import org.onlab.util.KryoNamespace;
35import org.onosproject.incubator.net.config.Config;
36import org.onosproject.incubator.net.config.ConfigApplyDelegate;
37import org.onosproject.incubator.net.config.ConfigFactory;
38import org.onosproject.incubator.net.config.NetworkConfigEvent;
39import org.onosproject.incubator.net.config.NetworkConfigStore;
40import org.onosproject.incubator.net.config.NetworkConfigStoreDelegate;
41import org.onosproject.store.AbstractStore;
42import org.onosproject.store.serializers.KryoNamespaces;
43import org.onosproject.store.service.ConsistentMap;
44import org.onosproject.store.service.MapEvent;
45import org.onosproject.store.service.MapEventListener;
46import org.onosproject.store.service.Serializer;
47import org.onosproject.store.service.StorageService;
48import org.onosproject.store.service.Versioned;
49import org.slf4j.Logger;
50import org.slf4j.LoggerFactory;
51
52import java.util.LinkedHashMap;
53import java.util.Map;
54import java.util.Objects;
55import java.util.Set;
56
57import static org.onosproject.incubator.net.config.NetworkConfigEvent.Type.*;
58
59/**
60 * Implementation of a distributed network configuration store.
61 */
62@Component(immediate = true)
63@Service
64public class DistributedNetworkConfigStore
65 extends AbstractStore<NetworkConfigEvent, NetworkConfigStoreDelegate>
66 implements NetworkConfigStore {
67
68 private final Logger log = LoggerFactory.getLogger(getClass());
69
70 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
71 protected StorageService storageService;
72
73 private ConsistentMap<ConfigKey, ObjectNode> configs;
74
75 private final Map<String, ConfigFactory> factoriesByConfig = Maps.newConcurrentMap();
76 private final ObjectMapper mapper = new ObjectMapper();
77 private final ConfigApplyDelegate applyDelegate = new InternalApplyDelegate();
78 private final MapEventListener<ConfigKey, ObjectNode> listener = new InternalMapListener();
79
80 @Activate
81 public void activate() {
82 KryoNamespace.Builder kryoBuilder = new KryoNamespace.Builder()
83 .register(KryoNamespaces.API)
84 .register(ConfigKey.class, ObjectNode.class,
85 JsonNodeFactory.class, LinkedHashMap.class,
86 TextNode.class, BooleanNode.class,
87 LongNode.class, DoubleNode.class, ShortNode.class);
88
89 configs = storageService.<ConfigKey, ObjectNode>consistentMapBuilder()
90 .withSerializer(Serializer.using(kryoBuilder.build()))
91 .withName("onos-network-configs")
92 .build();
93 configs.addListener(listener);
94 log.info("Started");
95 }
96
97 @Deactivate
98 public void deactivate() {
99 configs.removeListener(listener);
100 log.info("Stopped");
101 }
102
103 @Override
104 public void addConfigFactory(ConfigFactory configFactory) {
105 factoriesByConfig.put(configFactory.configClass().getName(), configFactory);
Thomas Vachuskae6360222015-07-21 10:10:36 -0700106 notifyDelegate(new NetworkConfigEvent(CONFIG_REGISTERED, configFactory.configKey(),
107 configFactory.configClass()));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700108 }
109
110 @Override
111 public void removeConfigFactory(ConfigFactory configFactory) {
112 factoriesByConfig.remove(configFactory.configClass().getName());
Thomas Vachuskae6360222015-07-21 10:10:36 -0700113 notifyDelegate(new NetworkConfigEvent(CONFIG_UNREGISTERED, configFactory.configKey(),
114 configFactory.configClass()));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700115 }
116
117 @Override
118 @SuppressWarnings("unchecked")
119 public <S, C extends Config<S>> ConfigFactory<S, C> getConfigFactory(Class<C> configClass) {
120 return (ConfigFactory<S, C>) factoriesByConfig.get(configClass.getName());
121 }
122
123 @Override
124 @SuppressWarnings("unchecked")
125 public <S> Set<S> getSubjects(Class<S> subjectClass) {
126 ImmutableSet.Builder<S> builder = ImmutableSet.builder();
127 configs.keySet().forEach(k -> {
128 if (subjectClass.isInstance(k.subject)) {
129 builder.add((S) k.subject);
130 }
131 });
132 return builder.build();
133 }
134
135 @Override
136 @SuppressWarnings("unchecked")
137 public <S, C extends Config<S>> Set<S> getSubjects(Class<S> subjectClass, Class<C> configClass) {
138 ImmutableSet.Builder<S> builder = ImmutableSet.builder();
139 String cName = configClass.getName();
140 configs.keySet().forEach(k -> {
141 if (subjectClass.isInstance(k.subject) && cName.equals(k.configClass)) {
142 builder.add((S) k.subject);
143 }
144 });
145 return builder.build();
146 }
147
148 @Override
149 @SuppressWarnings("unchecked")
150 public <S> Set<Class<? extends Config<S>>> getConfigClasses(S subject) {
151 ImmutableSet.Builder<Class<? extends Config<S>>> builder = ImmutableSet.builder();
152 configs.keySet().forEach(k -> {
153 if (Objects.equals(subject, k.subject) && delegate != null) {
154 builder.add(factoriesByConfig.get(k.configClass).configClass());
155 }
156 });
157 return builder.build();
158 }
159
160 @Override
161 public <S, T extends Config<S>> T getConfig(S subject, Class<T> configClass) {
162 Versioned<ObjectNode> json = configs.get(key(subject, configClass));
163 return json != null ? createConfig(subject, configClass, json.value()) : null;
164 }
165
166
167 @Override
168 public <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass) {
169 Versioned<ObjectNode> json = configs.computeIfAbsent(key(subject, configClass),
170 k -> mapper.createObjectNode());
171 return createConfig(subject, configClass, json.value());
172 }
173
174 @Override
175 public <S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass, ObjectNode json) {
176 return createConfig(subject, configClass,
177 configs.putAndGet(key(subject, configClass), json).value());
178 }
179
180 @Override
181 public <S, C extends Config<S>> void clearConfig(S subject, Class<C> configClass) {
182 configs.remove(key(subject, configClass));
183 }
184
185 /**
186 * Produces a config from the specified subject, config class and raw JSON.
187 *
188 * @param subject config subject
189 * @param configClass config class
190 * @param json raw JSON data
191 * @return config object or null of no factory found or if the specified
192 * JSON is null
193 */
194 @SuppressWarnings("unchecked")
195 private <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass,
196 ObjectNode json) {
197 if (json != null) {
198 ConfigFactory<S, C> factory = factoriesByConfig.get(configClass.getName());
199 if (factory != null) {
200 C config = factory.createConfig();
201 config.init(subject, factory.configKey(), json, mapper, applyDelegate);
202 return config;
203 }
204 }
205 return null;
206 }
207
208
209 // Auxiliary delegate to receive notifications about changes applied to
210 // the network configuration - by the apps.
211 private class InternalApplyDelegate implements ConfigApplyDelegate {
212 @Override
213 public void onApply(Config config) {
214 configs.put(key(config.subject(), config.getClass()), config.node());
215 }
216 }
217
218 // Produces a key for uniquely tracking a subject config.
219 private static ConfigKey key(Object subject, Class<?> configClass) {
220 return new ConfigKey(subject, configClass);
221 }
222
223 // Auxiliary key to track subject configurations.
224 private static final class ConfigKey {
225 final Object subject;
226 final String configClass;
227
228 private ConfigKey(Object subject, Class<?> configClass) {
229 this.subject = subject;
230 this.configClass = configClass.getName();
231 }
232
233 @Override
234 public int hashCode() {
235 return Objects.hash(subject, configClass);
236 }
237
238 @Override
239 public boolean equals(Object obj) {
240 if (this == obj) {
241 return true;
242 }
243 if (obj instanceof ConfigKey) {
244 final ConfigKey other = (ConfigKey) obj;
245 return Objects.equals(this.subject, other.subject)
246 && Objects.equals(this.configClass, other.configClass);
247 }
248 return false;
249 }
250 }
251
252 private class InternalMapListener implements MapEventListener<ConfigKey, ObjectNode> {
253 @Override
254 public void event(MapEvent<ConfigKey, ObjectNode> event) {
255 NetworkConfigEvent.Type type;
256 switch (event.type()) {
257 case INSERT:
258 type = CONFIG_ADDED;
259 break;
260 case UPDATE:
261 type = CONFIG_UPDATED;
262 break;
263 case REMOVE:
264 default:
265 type = CONFIG_REMOVED;
266 break;
267 }
268 ConfigFactory factory = factoriesByConfig.get(event.key().configClass);
269 if (factory != null) {
270 notifyDelegate(new NetworkConfigEvent(type, event.key().subject,
271 factory.configClass()));
272 }
273 }
274 }
275}