blob: 315d570a6950b1606e9810e4ef20ef604a8f1adf [file] [log] [blame]
Thomas Vachuska96d55b12015-05-11 08:52:03 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Thomas Vachuska96d55b12015-05-11 08:52:03 -07003 *
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 */
Thomas Vachuska4998caa2015-08-26 13:28:38 -070016package org.onosproject.store.config.impl;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070017
Thomas Vachuska0a400ea2015-09-04 11:25:03 -070018import com.fasterxml.jackson.databind.JsonNode;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070019import com.fasterxml.jackson.databind.ObjectMapper;
Jonathan Hart111b42b2015-07-14 13:28:05 -070020import com.fasterxml.jackson.databind.node.ArrayNode;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070021import com.fasterxml.jackson.databind.node.BooleanNode;
22import com.fasterxml.jackson.databind.node.DoubleNode;
Ayaka Koshibe1a002512015-09-03 13:09:23 -070023import com.fasterxml.jackson.databind.node.IntNode;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070024import com.fasterxml.jackson.databind.node.JsonNodeFactory;
25import com.fasterxml.jackson.databind.node.LongNode;
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -080026import com.fasterxml.jackson.databind.node.NullNode;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070027import com.fasterxml.jackson.databind.node.ObjectNode;
28import com.fasterxml.jackson.databind.node.ShortNode;
29import com.fasterxml.jackson.databind.node.TextNode;
30import com.google.common.collect.ImmutableSet;
31import com.google.common.collect.Maps;
32import org.apache.felix.scr.annotations.Activate;
33import org.apache.felix.scr.annotations.Component;
34import org.apache.felix.scr.annotations.Deactivate;
35import org.apache.felix.scr.annotations.Reference;
36import org.apache.felix.scr.annotations.ReferenceCardinality;
37import org.apache.felix.scr.annotations.Service;
38import org.onlab.util.KryoNamespace;
Ray Milkeya4122362015-08-18 15:19:08 -070039import org.onosproject.net.config.Config;
40import org.onosproject.net.config.ConfigApplyDelegate;
41import org.onosproject.net.config.ConfigFactory;
Jonathan Hart54b83e82016-03-26 20:37:20 -070042import org.onosproject.net.config.InvalidConfigException;
Ray Milkeya4122362015-08-18 15:19:08 -070043import org.onosproject.net.config.NetworkConfigEvent;
44import org.onosproject.net.config.NetworkConfigStore;
45import org.onosproject.net.config.NetworkConfigStoreDelegate;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070046import org.onosproject.store.AbstractStore;
47import org.onosproject.store.serializers.KryoNamespaces;
48import org.onosproject.store.service.ConsistentMap;
49import org.onosproject.store.service.MapEvent;
50import org.onosproject.store.service.MapEventListener;
51import org.onosproject.store.service.Serializer;
52import org.onosproject.store.service.StorageService;
53import org.onosproject.store.service.Versioned;
54import org.slf4j.Logger;
55import org.slf4j.LoggerFactory;
56
57import java.util.LinkedHashMap;
58import java.util.Map;
59import java.util.Objects;
60import java.util.Set;
61
Thomas Vachuskace0bbb32015-11-18 16:56:10 -080062import static com.google.common.base.Preconditions.checkArgument;
Jonathan Hartb11c4d02016-03-23 09:05:44 -070063import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_ADDED;
64import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_REGISTERED;
65import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_REMOVED;
66import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_UNREGISTERED;
67import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_UPDATED;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070068
69/**
70 * Implementation of a distributed network configuration store.
71 */
72@Component(immediate = true)
73@Service
74public class DistributedNetworkConfigStore
75 extends AbstractStore<NetworkConfigEvent, NetworkConfigStoreDelegate>
76 implements NetworkConfigStore {
77
78 private final Logger log = LoggerFactory.getLogger(getClass());
79
Thomas Vachuskace0bbb32015-11-18 16:56:10 -080080 private static final String INVALID_CONFIG_JSON =
81 "JSON node does not contain valid configuration";
Jonathan Hartb11c4d02016-03-23 09:05:44 -070082 private static final String INVALID_JSON_LIST =
83 "JSON node is not a list for list type config";
84 private static final String INVALID_JSON_OBJECT =
85 "JSON node is not an object for object type config";
Thomas Vachuskace0bbb32015-11-18 16:56:10 -080086
Thomas Vachuska96d55b12015-05-11 08:52:03 -070087 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88 protected StorageService storageService;
89
Thomas Vachuska0a400ea2015-09-04 11:25:03 -070090 private ConsistentMap<ConfigKey, JsonNode> configs;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070091
92 private final Map<String, ConfigFactory> factoriesByConfig = Maps.newConcurrentMap();
93 private final ObjectMapper mapper = new ObjectMapper();
94 private final ConfigApplyDelegate applyDelegate = new InternalApplyDelegate();
Thomas Vachuska0a400ea2015-09-04 11:25:03 -070095 private final MapEventListener<ConfigKey, JsonNode> listener = new InternalMapListener();
Thomas Vachuska96d55b12015-05-11 08:52:03 -070096
97 @Activate
98 public void activate() {
99 KryoNamespace.Builder kryoBuilder = new KryoNamespace.Builder()
100 .register(KryoNamespaces.API)
Jonathan Hart111b42b2015-07-14 13:28:05 -0700101 .register(ConfigKey.class, ObjectNode.class, ArrayNode.class,
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700102 JsonNodeFactory.class, LinkedHashMap.class,
103 TextNode.class, BooleanNode.class,
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800104 LongNode.class, DoubleNode.class, ShortNode.class, IntNode.class,
105 NullNode.class);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700106
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700107 configs = storageService.<ConfigKey, JsonNode>consistentMapBuilder()
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700108 .withSerializer(Serializer.using(kryoBuilder.build()))
109 .withName("onos-network-configs")
Madan Jampani3d6a2f62015-08-12 07:19:07 -0700110 .withRelaxedReadConsistency()
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700111 .build();
112 configs.addListener(listener);
113 log.info("Started");
114 }
115
116 @Deactivate
117 public void deactivate() {
118 configs.removeListener(listener);
119 log.info("Stopped");
120 }
121
122 @Override
123 public void addConfigFactory(ConfigFactory configFactory) {
124 factoriesByConfig.put(configFactory.configClass().getName(), configFactory);
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800125 processPendingConfigs(configFactory);
Thomas Vachuskae6360222015-07-21 10:10:36 -0700126 notifyDelegate(new NetworkConfigEvent(CONFIG_REGISTERED, configFactory.configKey(),
127 configFactory.configClass()));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700128 }
129
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800130 // Sweep through any pending configurations, validate them and then prune them.
131 private void processPendingConfigs(ConfigFactory configFactory) {
Thomas Vachuska096bcc82016-03-07 21:30:29 -0800132 ImmutableSet.copyOf(configs.keySet()).forEach(k -> {
133 if (Objects.equals(k.configKey, configFactory.configKey()) &&
134 isAssignableFrom(configFactory, k)) {
Thomas Vachuska95148362016-07-13 14:23:27 -0700135 // Prune whether valid or not
136 Versioned<JsonNode> versioned = configs.remove(k);
137 // Allow for the value to be processed by another node already
138 if (versioned != null) {
139 validateConfig(k, configFactory, versioned.value());
140 }
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800141 }
142 });
Thomas Vachuska096bcc82016-03-07 21:30:29 -0800143 }
144
145 @SuppressWarnings("unchecked")
146 private boolean isAssignableFrom(ConfigFactory configFactory, ConfigKey k) {
147 return configFactory.subjectFactory().subjectClass().isAssignableFrom(k.subject.getClass());
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800148 }
149
150 @SuppressWarnings("unchecked")
151 private void validateConfig(ConfigKey key, ConfigFactory configFactory, JsonNode json) {
HIGUCHI Yutaca2208d2016-02-18 15:03:08 -0800152 Object subject;
153 if (key.subject instanceof String) {
154 subject = configFactory.subjectFactory().createSubject((String) key.subject);
155 } else {
156 subject = key.subject;
157 }
158 Config config = createConfig(subject, configFactory.configClass(), json);
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800159 try {
160 checkArgument(config.isValid(), INVALID_CONFIG_JSON);
HIGUCHI Yutaca2208d2016-02-18 15:03:08 -0800161 configs.putAndGet(key(subject, configFactory.configClass()), json);
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800162 } catch (Exception e) {
163 log.warn("Failed to validate pending {} configuration for {}: {}",
HIGUCHI Yutaca2208d2016-02-18 15:03:08 -0800164 key.configKey, key.subject, json);
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800165 }
166 }
167
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700168 @Override
169 public void removeConfigFactory(ConfigFactory configFactory) {
170 factoriesByConfig.remove(configFactory.configClass().getName());
Jonathan Hart73518ac2016-05-20 08:00:22 -0700171 processExistingConfigs(configFactory);
Thomas Vachuskae6360222015-07-21 10:10:36 -0700172 notifyDelegate(new NetworkConfigEvent(CONFIG_UNREGISTERED, configFactory.configKey(),
173 configFactory.configClass()));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700174 }
175
Jonathan Hart73518ac2016-05-20 08:00:22 -0700176 // Sweep through any configurations for the config factory, set back to pending state.
177 private void processExistingConfigs(ConfigFactory configFactory) {
178 ImmutableSet.copyOf(configs.keySet()).forEach(k -> {
179 if (Objects.equals(configFactory.configClass().getName(), k.configClass)) {
Ray Milkey38809282016-07-07 14:36:23 -0700180 Versioned<JsonNode> remove = configs.remove(k);
181 if (remove != null) {
182 JsonNode json = remove.value();
183 configs.put(key(k.subject, configFactory.configKey()), json);
184 log.debug("Set config pending: {}, {}", k.subject, k.configClass);
185 }
Jonathan Hart73518ac2016-05-20 08:00:22 -0700186 }
187 });
188 }
189
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700190 @Override
191 @SuppressWarnings("unchecked")
192 public <S, C extends Config<S>> ConfigFactory<S, C> getConfigFactory(Class<C> configClass) {
HIGUCHI Yutaca2208d2016-02-18 15:03:08 -0800193 return factoriesByConfig.get(configClass.getName());
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700194 }
195
196 @Override
197 @SuppressWarnings("unchecked")
198 public <S> Set<S> getSubjects(Class<S> subjectClass) {
199 ImmutableSet.Builder<S> builder = ImmutableSet.builder();
200 configs.keySet().forEach(k -> {
201 if (subjectClass.isInstance(k.subject)) {
202 builder.add((S) k.subject);
203 }
204 });
205 return builder.build();
206 }
207
208 @Override
209 @SuppressWarnings("unchecked")
210 public <S, C extends Config<S>> Set<S> getSubjects(Class<S> subjectClass, Class<C> configClass) {
211 ImmutableSet.Builder<S> builder = ImmutableSet.builder();
212 String cName = configClass.getName();
213 configs.keySet().forEach(k -> {
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800214 if (subjectClass.isInstance(k.subject) && Objects.equals(cName, k.configClass)) {
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700215 builder.add((S) k.subject);
216 }
217 });
218 return builder.build();
219 }
220
221 @Override
222 @SuppressWarnings("unchecked")
223 public <S> Set<Class<? extends Config<S>>> getConfigClasses(S subject) {
224 ImmutableSet.Builder<Class<? extends Config<S>>> builder = ImmutableSet.builder();
225 configs.keySet().forEach(k -> {
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800226 if (Objects.equals(subject, k.subject) && k.configClass != null && delegate != null) {
Jonathan Hart80fe4422016-05-24 18:47:37 -0700227 ConfigFactory<S, ? extends Config<S>> configFactory = factoriesByConfig.get(k.configClass);
228 if (configFactory == null) {
229 log.error("Found config but no config factory: subject={}, configClass={}",
230 subject, k.configClass);
231 }
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700232 builder.add(factoriesByConfig.get(k.configClass).configClass());
233 }
234 });
235 return builder.build();
236 }
237
238 @Override
239 public <S, T extends Config<S>> T getConfig(S subject, Class<T> configClass) {
Madan Jampanic6371882016-06-03 21:30:17 -0700240 Versioned<JsonNode> json = configs.get(key(subject, configClass));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700241 return json != null ? createConfig(subject, configClass, json.value()) : null;
242 }
243
244
245 @Override
246 public <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700247 ConfigFactory<S, C> factory = getConfigFactory(configClass);
248 Versioned<JsonNode> json = configs.computeIfAbsent(key(subject, configClass),
249 k -> factory.isList() ?
250 mapper.createArrayNode() :
251 mapper.createObjectNode());
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700252 return createConfig(subject, configClass, json.value());
253 }
254
255 @Override
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700256 public <S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass, JsonNode json) {
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800257 // Create the configuration and validate it.
258 C config = createConfig(subject, configClass, json);
Jonathan Hart54b83e82016-03-26 20:37:20 -0700259
260 try {
261 checkArgument(config.isValid(), INVALID_CONFIG_JSON);
262 } catch (RuntimeException e) {
263 ConfigFactory<S, C> configFactory = getConfigFactory(configClass);
264 String subjectKey = configFactory.subjectFactory().subjectClassKey();
265 String subjectString = configFactory.subjectFactory().subjectKey(config.subject());
266 String configKey = config.key();
267
268 throw new InvalidConfigException(subjectKey, subjectString, configKey, e);
269 }
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800270
271 // Insert the validated configuration and get it back.
272 Versioned<JsonNode> versioned = configs.putAndGet(key(subject, configClass), json);
273
274 // Re-create the config if for some reason what we attempted to put
275 // was supplanted by someone else already.
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800276 return versioned.value() == json ? config : createConfig(subject, configClass, versioned.value());
277 }
278
279 @Override
280 public <S> void queueConfig(S subject, String configKey, JsonNode json) {
281 configs.put(key(subject, configKey), json);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700282 }
283
284 @Override
285 public <S, C extends Config<S>> void clearConfig(S subject, Class<C> configClass) {
286 configs.remove(key(subject, configClass));
287 }
288
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800289 @Override
290 public <S> void clearQueuedConfig(S subject, String configKey) {
291 configs.remove(key(subject, configKey));
292 }
293
Deepa Vaddireddy0c49b602016-06-02 12:19:07 +0530294 @Override
295 public <S> void clearConfig(S subject) {
296 ImmutableSet.copyOf(configs.keySet()).forEach(k -> {
297 if (Objects.equals(subject, k.subject) && delegate != null) {
298 configs.remove(k);
299 }
300 });
301 }
302
303 @Override
304 public <S> void clearConfig() {
305 ImmutableSet.copyOf(configs.keySet()).forEach(k -> {
306 if (delegate != null) {
307 configs.remove(k);
308 }
309 });
310 }
311
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700312 /**
313 * Produces a config from the specified subject, config class and raw JSON.
314 *
315 * @param subject config subject
316 * @param configClass config class
317 * @param json raw JSON data
318 * @return config object or null of no factory found or if the specified
319 * JSON is null
320 */
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700321 private <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass,
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700322 JsonNode json) {
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700323 return createConfig(subject, configClass, json, false);
324 }
325
326 /**
327 * Produces a config from the specified subject, config class and raw JSON.
328 *
329 * The config can optionally be detached, which means it does not contain a
330 * reference to an apply delegate. This means a detached config can not be
331 * applied. This should be used only for passing the config object in the
332 * NetworkConfigEvent.
333 *
334 * @param subject config subject
335 * @param configClass config class
336 * @param json raw JSON data
337 * @param detached whether the config should be detached, that is, should
338 * be created without setting an apply delegate.
339 * @return config object or null of no factory found or if the specified
340 * JSON is null
341 */
342 @SuppressWarnings("unchecked")
343 private <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass,
344 JsonNode json, boolean detached) {
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700345 if (json != null) {
346 ConfigFactory<S, C> factory = factoriesByConfig.get(configClass.getName());
347 if (factory != null) {
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700348 validateJsonType(json, factory);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700349 C config = factory.createConfig();
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700350 config.init(subject, factory.configKey(), json, mapper,
351 detached ? null : applyDelegate);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700352 return config;
353 }
354 }
355 return null;
356 }
357
Charles Chan023a8982016-02-04 11:00:41 -0800358 /**
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700359 * Validates that the type of the JSON node is appropriate for the type of
360 * configuration. A list type configuration must be created with an
361 * ArrayNode, and an object type configuration must be created with an
362 * ObjectNode.
Charles Chan023a8982016-02-04 11:00:41 -0800363 *
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700364 * @param json JSON node to check
365 * @param factory config factory of configuration
366 * @param <S> subject
367 * @param <C> configuration
368 * @return true if the JSON node type is appropriate for the configuration
Charles Chan023a8982016-02-04 11:00:41 -0800369 */
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700370 private <S, C extends Config<S>> boolean validateJsonType(JsonNode json,
371 ConfigFactory<S, C> factory) {
372 if (factory.isList() && !(json instanceof ArrayNode)) {
373 throw new IllegalArgumentException(INVALID_JSON_LIST);
Charles Chan023a8982016-02-04 11:00:41 -0800374 }
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700375 if (!factory.isList() && !(json instanceof ObjectNode)) {
376 throw new IllegalArgumentException(INVALID_JSON_OBJECT);
377 }
378
379 return true;
Charles Chan023a8982016-02-04 11:00:41 -0800380 }
381
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700382
383 // Auxiliary delegate to receive notifications about changes applied to
384 // the network configuration - by the apps.
385 private class InternalApplyDelegate implements ConfigApplyDelegate {
386 @Override
387 public void onApply(Config config) {
388 configs.put(key(config.subject(), config.getClass()), config.node());
389 }
390 }
391
392 // Produces a key for uniquely tracking a subject config.
393 private static ConfigKey key(Object subject, Class<?> configClass) {
394 return new ConfigKey(subject, configClass);
395 }
396
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800397 // Produces a key for uniquely tracking a subject config.
398 private static ConfigKey key(Object subject, String configKey) {
399 return new ConfigKey(subject, configKey);
400 }
401
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700402 // Auxiliary key to track subject configurations.
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800403 // Keys with non-null configKey are pending configurations.
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700404 private static final class ConfigKey {
405 final Object subject;
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800406 final String configKey;
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700407 final String configClass;
408
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800409 // Create a key for pending configuration class
410 private ConfigKey(Object subject, String configKey) {
411 this.subject = subject;
412 this.configKey = configKey;
413 this.configClass = null;
414 }
415
416 // Create a key for registered class configuration
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700417 private ConfigKey(Object subject, Class<?> configClass) {
418 this.subject = subject;
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800419 this.configKey = null;
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700420 this.configClass = configClass.getName();
421 }
422
423 @Override
424 public int hashCode() {
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800425 return Objects.hash(subject, configKey, configClass);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700426 }
427
428 @Override
429 public boolean equals(Object obj) {
430 if (this == obj) {
431 return true;
432 }
433 if (obj instanceof ConfigKey) {
434 final ConfigKey other = (ConfigKey) obj;
435 return Objects.equals(this.subject, other.subject)
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800436 && Objects.equals(this.configKey, other.configKey)
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700437 && Objects.equals(this.configClass, other.configClass);
438 }
439 return false;
440 }
441 }
442
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700443 private class InternalMapListener implements MapEventListener<ConfigKey, JsonNode> {
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700444 @Override
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700445 public void event(MapEvent<ConfigKey, JsonNode> event) {
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800446 // Do not delegate pending configs.
447 if (event.key().configClass == null) {
448 return;
449 }
450
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700451 ConfigFactory factory = factoriesByConfig.get(event.key().configClass);
452 if (factory != null) {
Charles Chan023a8982016-02-04 11:00:41 -0800453 Object subject = event.key().subject;
454 Class configClass = factory.configClass();
455 Versioned<JsonNode> newValue = event.newValue();
456 Versioned<JsonNode> oldValue = event.oldValue();
457
458 Config config = (newValue != null) ?
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700459 createConfig(subject, configClass, newValue.value(), true) :
460 null;
Charles Chan023a8982016-02-04 11:00:41 -0800461 Config prevConfig = (oldValue != null) ?
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700462 createConfig(subject, configClass, oldValue.value(), true) :
463 null;
Charles Chan023a8982016-02-04 11:00:41 -0800464
465 NetworkConfigEvent.Type type;
466 switch (event.type()) {
467 case INSERT:
468 type = CONFIG_ADDED;
469 break;
470 case UPDATE:
471 type = CONFIG_UPDATED;
472 break;
473 case REMOVE:
474 default:
475 type = CONFIG_REMOVED;
476 break;
477 }
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700478 notifyDelegate(new NetworkConfigEvent(type, event.key().subject,
Charles Chan023a8982016-02-04 11:00:41 -0800479 config, prevConfig, factory.configClass()));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700480 }
481 }
482 }
483}