blob: bac245eb705670826c0136b058520f761ed2f959 [file] [log] [blame]
Thomas Vachuska96d55b12015-05-11 08:52:03 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
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;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070032import org.onlab.util.KryoNamespace;
Ray Milkeya4122362015-08-18 15:19:08 -070033import org.onosproject.net.config.Config;
34import org.onosproject.net.config.ConfigApplyDelegate;
35import org.onosproject.net.config.ConfigFactory;
Jonathan Hart54b83e82016-03-26 20:37:20 -070036import org.onosproject.net.config.InvalidConfigException;
Ray Milkeya4122362015-08-18 15:19:08 -070037import org.onosproject.net.config.NetworkConfigEvent;
38import org.onosproject.net.config.NetworkConfigStore;
39import org.onosproject.net.config.NetworkConfigStoreDelegate;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070040import org.onosproject.store.AbstractStore;
41import org.onosproject.store.serializers.KryoNamespaces;
42import org.onosproject.store.service.ConsistentMap;
43import org.onosproject.store.service.MapEvent;
44import org.onosproject.store.service.MapEventListener;
45import org.onosproject.store.service.Serializer;
46import org.onosproject.store.service.StorageService;
47import org.onosproject.store.service.Versioned;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070048import org.osgi.service.component.annotations.Activate;
49import org.osgi.service.component.annotations.Component;
50import org.osgi.service.component.annotations.Deactivate;
51import org.osgi.service.component.annotations.Reference;
52import org.osgi.service.component.annotations.ReferenceCardinality;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070053import org.slf4j.Logger;
54import org.slf4j.LoggerFactory;
55
56import java.util.LinkedHashMap;
57import java.util.Map;
58import java.util.Objects;
59import java.util.Set;
60
Thomas Vachuskace0bbb32015-11-18 16:56:10 -080061import static com.google.common.base.Preconditions.checkArgument;
Jonathan Hartb11c4d02016-03-23 09:05:44 -070062import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_ADDED;
63import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_REGISTERED;
64import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_REMOVED;
65import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_UNREGISTERED;
66import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_UPDATED;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070067
68/**
69 * Implementation of a distributed network configuration store.
70 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070071@Component(immediate = true, service = NetworkConfigStore.class)
Thomas Vachuska96d55b12015-05-11 08:52:03 -070072public class DistributedNetworkConfigStore
73 extends AbstractStore<NetworkConfigEvent, NetworkConfigStoreDelegate>
74 implements NetworkConfigStore {
75
76 private final Logger log = LoggerFactory.getLogger(getClass());
77
Thomas Vachuskace0bbb32015-11-18 16:56:10 -080078 private static final String INVALID_CONFIG_JSON =
79 "JSON node does not contain valid configuration";
Jonathan Hartb11c4d02016-03-23 09:05:44 -070080 private static final String INVALID_JSON_LIST =
81 "JSON node is not a list for list type config";
82 private static final String INVALID_JSON_OBJECT =
83 "JSON node is not an object for object type config";
Thomas Vachuskace0bbb32015-11-18 16:56:10 -080084
Ray Milkeyd84f89b2018-08-17 14:54:17 -070085 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuska96d55b12015-05-11 08:52:03 -070086 protected StorageService storageService;
87
Thomas Vachuska0a400ea2015-09-04 11:25:03 -070088 private ConsistentMap<ConfigKey, JsonNode> configs;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070089
90 private final Map<String, ConfigFactory> factoriesByConfig = Maps.newConcurrentMap();
91 private final ObjectMapper mapper = new ObjectMapper();
92 private final ConfigApplyDelegate applyDelegate = new InternalApplyDelegate();
Thomas Vachuska0a400ea2015-09-04 11:25:03 -070093 private final MapEventListener<ConfigKey, JsonNode> listener = new InternalMapListener();
Thomas Vachuska96d55b12015-05-11 08:52:03 -070094
95 @Activate
96 public void activate() {
97 KryoNamespace.Builder kryoBuilder = new KryoNamespace.Builder()
98 .register(KryoNamespaces.API)
Jonathan Hart111b42b2015-07-14 13:28:05 -070099 .register(ConfigKey.class, ObjectNode.class, ArrayNode.class,
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700100 JsonNodeFactory.class, LinkedHashMap.class,
101 TextNode.class, BooleanNode.class,
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800102 LongNode.class, DoubleNode.class, ShortNode.class, IntNode.class,
103 NullNode.class);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700104
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700105 configs = storageService.<ConfigKey, JsonNode>consistentMapBuilder()
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700106 .withSerializer(Serializer.using(kryoBuilder.build()))
107 .withName("onos-network-configs")
Madan Jampani3d6a2f62015-08-12 07:19:07 -0700108 .withRelaxedReadConsistency()
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700109 .build();
110 configs.addListener(listener);
111 log.info("Started");
112 }
113
114 @Deactivate
115 public void deactivate() {
116 configs.removeListener(listener);
117 log.info("Stopped");
118 }
119
120 @Override
121 public void addConfigFactory(ConfigFactory configFactory) {
122 factoriesByConfig.put(configFactory.configClass().getName(), configFactory);
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800123 processPendingConfigs(configFactory);
Thomas Vachuskae6360222015-07-21 10:10:36 -0700124 notifyDelegate(new NetworkConfigEvent(CONFIG_REGISTERED, configFactory.configKey(),
125 configFactory.configClass()));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700126 }
127
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800128 // Sweep through any pending configurations, validate them and then prune them.
129 private void processPendingConfigs(ConfigFactory configFactory) {
Jordan Halterman00e92da2018-05-22 23:05:52 -0700130 configs.keySet().forEach(k -> {
Thomas Vachuska096bcc82016-03-07 21:30:29 -0800131 if (Objects.equals(k.configKey, configFactory.configKey()) &&
132 isAssignableFrom(configFactory, k)) {
Thomas Vachuska95148362016-07-13 14:23:27 -0700133 // Prune whether valid or not
134 Versioned<JsonNode> versioned = configs.remove(k);
135 // Allow for the value to be processed by another node already
136 if (versioned != null) {
137 validateConfig(k, configFactory, versioned.value());
138 }
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800139 }
140 });
Thomas Vachuska096bcc82016-03-07 21:30:29 -0800141 }
142
143 @SuppressWarnings("unchecked")
144 private boolean isAssignableFrom(ConfigFactory configFactory, ConfigKey k) {
145 return configFactory.subjectFactory().subjectClass().isAssignableFrom(k.subject.getClass());
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800146 }
147
148 @SuppressWarnings("unchecked")
149 private void validateConfig(ConfigKey key, ConfigFactory configFactory, JsonNode json) {
HIGUCHI Yutaca2208d2016-02-18 15:03:08 -0800150 Object subject;
151 if (key.subject instanceof String) {
152 subject = configFactory.subjectFactory().createSubject((String) key.subject);
153 } else {
154 subject = key.subject;
155 }
156 Config config = createConfig(subject, configFactory.configClass(), json);
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800157 try {
158 checkArgument(config.isValid(), INVALID_CONFIG_JSON);
HIGUCHI Yutaca2208d2016-02-18 15:03:08 -0800159 configs.putAndGet(key(subject, configFactory.configClass()), json);
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800160 } catch (Exception e) {
161 log.warn("Failed to validate pending {} configuration for {}: {}",
HIGUCHI Yutaca2208d2016-02-18 15:03:08 -0800162 key.configKey, key.subject, json);
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800163 }
164 }
165
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700166 @Override
167 public void removeConfigFactory(ConfigFactory configFactory) {
168 factoriesByConfig.remove(configFactory.configClass().getName());
Jonathan Hart73518ac2016-05-20 08:00:22 -0700169 processExistingConfigs(configFactory);
Thomas Vachuskae6360222015-07-21 10:10:36 -0700170 notifyDelegate(new NetworkConfigEvent(CONFIG_UNREGISTERED, configFactory.configKey(),
171 configFactory.configClass()));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700172 }
173
Jonathan Hart73518ac2016-05-20 08:00:22 -0700174 // Sweep through any configurations for the config factory, set back to pending state.
175 private void processExistingConfigs(ConfigFactory configFactory) {
Jordan Halterman00e92da2018-05-22 23:05:52 -0700176 configs.keySet().forEach(k -> {
Jonathan Hart73518ac2016-05-20 08:00:22 -0700177 if (Objects.equals(configFactory.configClass().getName(), k.configClass)) {
Ray Milkey38809282016-07-07 14:36:23 -0700178 Versioned<JsonNode> remove = configs.remove(k);
179 if (remove != null) {
180 JsonNode json = remove.value();
181 configs.put(key(k.subject, configFactory.configKey()), json);
182 log.debug("Set config pending: {}, {}", k.subject, k.configClass);
183 }
Jonathan Hart73518ac2016-05-20 08:00:22 -0700184 }
185 });
186 }
187
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700188 @Override
189 @SuppressWarnings("unchecked")
190 public <S, C extends Config<S>> ConfigFactory<S, C> getConfigFactory(Class<C> configClass) {
HIGUCHI Yutaca2208d2016-02-18 15:03:08 -0800191 return factoriesByConfig.get(configClass.getName());
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700192 }
193
194 @Override
195 @SuppressWarnings("unchecked")
196 public <S> Set<S> getSubjects(Class<S> subjectClass) {
197 ImmutableSet.Builder<S> builder = ImmutableSet.builder();
198 configs.keySet().forEach(k -> {
199 if (subjectClass.isInstance(k.subject)) {
200 builder.add((S) k.subject);
201 }
202 });
203 return builder.build();
204 }
205
206 @Override
207 @SuppressWarnings("unchecked")
208 public <S, C extends Config<S>> Set<S> getSubjects(Class<S> subjectClass, Class<C> configClass) {
209 ImmutableSet.Builder<S> builder = ImmutableSet.builder();
210 String cName = configClass.getName();
211 configs.keySet().forEach(k -> {
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800212 if (subjectClass.isInstance(k.subject) && Objects.equals(cName, k.configClass)) {
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700213 builder.add((S) k.subject);
214 }
215 });
216 return builder.build();
217 }
218
219 @Override
220 @SuppressWarnings("unchecked")
221 public <S> Set<Class<? extends Config<S>>> getConfigClasses(S subject) {
222 ImmutableSet.Builder<Class<? extends Config<S>>> builder = ImmutableSet.builder();
223 configs.keySet().forEach(k -> {
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800224 if (Objects.equals(subject, k.subject) && k.configClass != null && delegate != null) {
Jonathan Hart80fe4422016-05-24 18:47:37 -0700225 ConfigFactory<S, ? extends Config<S>> configFactory = factoriesByConfig.get(k.configClass);
226 if (configFactory == null) {
Thomas Vachuska698ba662016-07-28 11:42:39 -0700227 log.warn("Found config but no config factory: subject={}, configClass={}",
228 subject, k.configClass);
229 } else {
230 builder.add(configFactory.configClass());
Jonathan Hart80fe4422016-05-24 18:47:37 -0700231 }
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700232 }
233 });
234 return builder.build();
235 }
236
237 @Override
238 public <S, T extends Config<S>> T getConfig(S subject, Class<T> configClass) {
Madan Jampanic6371882016-06-03 21:30:17 -0700239 Versioned<JsonNode> json = configs.get(key(subject, configClass));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700240 return json != null ? createConfig(subject, configClass, json.value()) : null;
241 }
242
243
244 @Override
245 public <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700246 ConfigFactory<S, C> factory = getConfigFactory(configClass);
247 Versioned<JsonNode> json = configs.computeIfAbsent(key(subject, configClass),
248 k -> factory.isList() ?
249 mapper.createArrayNode() :
250 mapper.createObjectNode());
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700251 return createConfig(subject, configClass, json.value());
252 }
253
254 @Override
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700255 public <S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass, JsonNode json) {
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800256 // Create the configuration and validate it.
257 C config = createConfig(subject, configClass, json);
Jonathan Hart54b83e82016-03-26 20:37:20 -0700258
259 try {
260 checkArgument(config.isValid(), INVALID_CONFIG_JSON);
261 } catch (RuntimeException e) {
262 ConfigFactory<S, C> configFactory = getConfigFactory(configClass);
263 String subjectKey = configFactory.subjectFactory().subjectClassKey();
264 String subjectString = configFactory.subjectFactory().subjectKey(config.subject());
265 String configKey = config.key();
266
267 throw new InvalidConfigException(subjectKey, subjectString, configKey, e);
268 }
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800269
270 // Insert the validated configuration and get it back.
271 Versioned<JsonNode> versioned = configs.putAndGet(key(subject, configClass), json);
272
273 // Re-create the config if for some reason what we attempted to put
274 // was supplanted by someone else already.
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800275 return versioned.value() == json ? config : createConfig(subject, configClass, versioned.value());
276 }
277
278 @Override
279 public <S> void queueConfig(S subject, String configKey, JsonNode json) {
280 configs.put(key(subject, configKey), json);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700281 }
282
283 @Override
284 public <S, C extends Config<S>> void clearConfig(S subject, Class<C> configClass) {
285 configs.remove(key(subject, configClass));
286 }
287
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800288 @Override
289 public <S> void clearQueuedConfig(S subject, String configKey) {
290 configs.remove(key(subject, configKey));
291 }
292
Deepa Vaddireddy0c49b602016-06-02 12:19:07 +0530293 @Override
294 public <S> void clearConfig(S subject) {
Jordan Halterman00e92da2018-05-22 23:05:52 -0700295 configs.keySet().forEach(k -> {
Deepa Vaddireddy0c49b602016-06-02 12:19:07 +0530296 if (Objects.equals(subject, k.subject) && delegate != null) {
297 configs.remove(k);
298 }
299 });
300 }
301
302 @Override
303 public <S> void clearConfig() {
Jordan Halterman00e92da2018-05-22 23:05:52 -0700304 configs.keySet().forEach(k -> {
Deepa Vaddireddy0c49b602016-06-02 12:19:07 +0530305 if (delegate != null) {
306 configs.remove(k);
307 }
308 });
309 }
310
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700311 /**
312 * Produces a config from the specified subject, config class and raw JSON.
313 *
314 * @param subject config subject
315 * @param configClass config class
316 * @param json raw JSON data
317 * @return config object or null of no factory found or if the specified
318 * JSON is null
319 */
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700320 private <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass,
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700321 JsonNode json) {
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700322 return createConfig(subject, configClass, json, false);
323 }
324
325 /**
326 * Produces a config from the specified subject, config class and raw JSON.
327 *
328 * The config can optionally be detached, which means it does not contain a
329 * reference to an apply delegate. This means a detached config can not be
330 * applied. This should be used only for passing the config object in the
331 * NetworkConfigEvent.
332 *
333 * @param subject config subject
334 * @param configClass config class
335 * @param json raw JSON data
336 * @param detached whether the config should be detached, that is, should
337 * be created without setting an apply delegate.
338 * @return config object or null of no factory found or if the specified
339 * JSON is null
340 */
341 @SuppressWarnings("unchecked")
342 private <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass,
343 JsonNode json, boolean detached) {
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700344 if (json != null) {
345 ConfigFactory<S, C> factory = factoriesByConfig.get(configClass.getName());
346 if (factory != null) {
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700347 validateJsonType(json, factory);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700348 C config = factory.createConfig();
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700349 config.init(subject, factory.configKey(), json, mapper,
350 detached ? null : applyDelegate);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700351 return config;
352 }
353 }
354 return null;
355 }
356
Charles Chan023a8982016-02-04 11:00:41 -0800357 /**
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700358 * Validates that the type of the JSON node is appropriate for the type of
359 * configuration. A list type configuration must be created with an
360 * ArrayNode, and an object type configuration must be created with an
361 * ObjectNode.
Charles Chan023a8982016-02-04 11:00:41 -0800362 *
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700363 * @param json JSON node to check
364 * @param factory config factory of configuration
365 * @param <S> subject
366 * @param <C> configuration
367 * @return true if the JSON node type is appropriate for the configuration
Charles Chan023a8982016-02-04 11:00:41 -0800368 */
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700369 private <S, C extends Config<S>> boolean validateJsonType(JsonNode json,
370 ConfigFactory<S, C> factory) {
371 if (factory.isList() && !(json instanceof ArrayNode)) {
372 throw new IllegalArgumentException(INVALID_JSON_LIST);
Charles Chan023a8982016-02-04 11:00:41 -0800373 }
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700374 if (!factory.isList() && !(json instanceof ObjectNode)) {
375 throw new IllegalArgumentException(INVALID_JSON_OBJECT);
376 }
377
378 return true;
Charles Chan023a8982016-02-04 11:00:41 -0800379 }
380
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700381
382 // Auxiliary delegate to receive notifications about changes applied to
383 // the network configuration - by the apps.
384 private class InternalApplyDelegate implements ConfigApplyDelegate {
385 @Override
386 public void onApply(Config config) {
387 configs.put(key(config.subject(), config.getClass()), config.node());
388 }
389 }
390
391 // Produces a key for uniquely tracking a subject config.
392 private static ConfigKey key(Object subject, Class<?> configClass) {
393 return new ConfigKey(subject, configClass);
394 }
395
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800396 // Produces a key for uniquely tracking a subject config.
397 private static ConfigKey key(Object subject, String configKey) {
398 return new ConfigKey(subject, configKey);
399 }
400
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700401 // Auxiliary key to track subject configurations.
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800402 // Keys with non-null configKey are pending configurations.
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700403 private static final class ConfigKey {
404 final Object subject;
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800405 final String configKey;
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700406 final String configClass;
407
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800408 // Create a key for pending configuration class
409 private ConfigKey(Object subject, String configKey) {
410 this.subject = subject;
411 this.configKey = configKey;
412 this.configClass = null;
413 }
414
415 // Create a key for registered class configuration
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700416 private ConfigKey(Object subject, Class<?> configClass) {
417 this.subject = subject;
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800418 this.configKey = null;
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700419 this.configClass = configClass.getName();
420 }
421
422 @Override
423 public int hashCode() {
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800424 return Objects.hash(subject, configKey, configClass);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700425 }
426
427 @Override
428 public boolean equals(Object obj) {
429 if (this == obj) {
430 return true;
431 }
432 if (obj instanceof ConfigKey) {
433 final ConfigKey other = (ConfigKey) obj;
434 return Objects.equals(this.subject, other.subject)
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800435 && Objects.equals(this.configKey, other.configKey)
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700436 && Objects.equals(this.configClass, other.configClass);
437 }
438 return false;
439 }
440 }
441
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700442 private class InternalMapListener implements MapEventListener<ConfigKey, JsonNode> {
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700443 @Override
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700444 public void event(MapEvent<ConfigKey, JsonNode> event) {
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800445 // Do not delegate pending configs.
446 if (event.key().configClass == null) {
447 return;
448 }
449
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700450 ConfigFactory factory = factoriesByConfig.get(event.key().configClass);
451 if (factory != null) {
Charles Chan023a8982016-02-04 11:00:41 -0800452 Object subject = event.key().subject;
453 Class configClass = factory.configClass();
454 Versioned<JsonNode> newValue = event.newValue();
455 Versioned<JsonNode> oldValue = event.oldValue();
456
457 Config config = (newValue != null) ?
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700458 createConfig(subject, configClass, newValue.value(), true) :
459 null;
Charles Chan023a8982016-02-04 11:00:41 -0800460 Config prevConfig = (oldValue != null) ?
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700461 createConfig(subject, configClass, oldValue.value(), true) :
462 null;
Charles Chan023a8982016-02-04 11:00:41 -0800463
464 NetworkConfigEvent.Type type;
465 switch (event.type()) {
466 case INSERT:
467 type = CONFIG_ADDED;
468 break;
469 case UPDATE:
470 type = CONFIG_UPDATED;
471 break;
472 case REMOVE:
473 default:
474 type = CONFIG_REMOVED;
475 break;
476 }
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700477 notifyDelegate(new NetworkConfigEvent(type, event.key().subject,
Charles Chan023a8982016-02-04 11:00:41 -0800478 config, prevConfig, factory.configClass()));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700479 }
480 }
481 }
482}