blob: 2d4fc4d3de67d851d3ba6f8daa5712ec195907da [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;
42import org.onosproject.net.config.NetworkConfigEvent;
43import org.onosproject.net.config.NetworkConfigStore;
44import org.onosproject.net.config.NetworkConfigStoreDelegate;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070045import org.onosproject.store.AbstractStore;
46import org.onosproject.store.serializers.KryoNamespaces;
47import org.onosproject.store.service.ConsistentMap;
48import org.onosproject.store.service.MapEvent;
49import org.onosproject.store.service.MapEventListener;
50import org.onosproject.store.service.Serializer;
51import org.onosproject.store.service.StorageService;
52import org.onosproject.store.service.Versioned;
53import 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 */
71@Component(immediate = true)
72@Service
73public class DistributedNetworkConfigStore
74 extends AbstractStore<NetworkConfigEvent, NetworkConfigStoreDelegate>
75 implements NetworkConfigStore {
76
77 private final Logger log = LoggerFactory.getLogger(getClass());
78
Thomas Vachuskace0bbb32015-11-18 16:56:10 -080079 private static final String INVALID_CONFIG_JSON =
80 "JSON node does not contain valid configuration";
Jonathan Hartb11c4d02016-03-23 09:05:44 -070081 private static final String INVALID_JSON_LIST =
82 "JSON node is not a list for list type config";
83 private static final String INVALID_JSON_OBJECT =
84 "JSON node is not an object for object type config";
Thomas Vachuskace0bbb32015-11-18 16:56:10 -080085
Thomas Vachuska96d55b12015-05-11 08:52:03 -070086 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected StorageService storageService;
88
Thomas Vachuska0a400ea2015-09-04 11:25:03 -070089 private ConsistentMap<ConfigKey, JsonNode> configs;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070090
91 private final Map<String, ConfigFactory> factoriesByConfig = Maps.newConcurrentMap();
92 private final ObjectMapper mapper = new ObjectMapper();
93 private final ConfigApplyDelegate applyDelegate = new InternalApplyDelegate();
Thomas Vachuska0a400ea2015-09-04 11:25:03 -070094 private final MapEventListener<ConfigKey, JsonNode> listener = new InternalMapListener();
Thomas Vachuska96d55b12015-05-11 08:52:03 -070095
96 @Activate
97 public void activate() {
98 KryoNamespace.Builder kryoBuilder = new KryoNamespace.Builder()
99 .register(KryoNamespaces.API)
Jonathan Hart111b42b2015-07-14 13:28:05 -0700100 .register(ConfigKey.class, ObjectNode.class, ArrayNode.class,
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700101 JsonNodeFactory.class, LinkedHashMap.class,
102 TextNode.class, BooleanNode.class,
HIGUCHI Yutad9fe3a32015-11-24 18:52:25 -0800103 LongNode.class, DoubleNode.class, ShortNode.class, IntNode.class,
104 NullNode.class);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700105
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700106 configs = storageService.<ConfigKey, JsonNode>consistentMapBuilder()
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700107 .withSerializer(Serializer.using(kryoBuilder.build()))
108 .withName("onos-network-configs")
Madan Jampani3d6a2f62015-08-12 07:19:07 -0700109 .withRelaxedReadConsistency()
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700110 .build();
111 configs.addListener(listener);
112 log.info("Started");
113 }
114
115 @Deactivate
116 public void deactivate() {
117 configs.removeListener(listener);
118 log.info("Stopped");
119 }
120
121 @Override
122 public void addConfigFactory(ConfigFactory configFactory) {
123 factoriesByConfig.put(configFactory.configClass().getName(), configFactory);
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800124 processPendingConfigs(configFactory);
Thomas Vachuskae6360222015-07-21 10:10:36 -0700125 notifyDelegate(new NetworkConfigEvent(CONFIG_REGISTERED, configFactory.configKey(),
126 configFactory.configClass()));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700127 }
128
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800129 // Sweep through any pending configurations, validate them and then prune them.
130 private void processPendingConfigs(ConfigFactory configFactory) {
Thomas Vachuska096bcc82016-03-07 21:30:29 -0800131 ImmutableSet.copyOf(configs.keySet()).forEach(k -> {
132 if (Objects.equals(k.configKey, configFactory.configKey()) &&
133 isAssignableFrom(configFactory, k)) {
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800134 validateConfig(k, configFactory, configs.get(k).value());
Thomas Vachuska096bcc82016-03-07 21:30:29 -0800135 configs.remove(k); // Prune whether valid or not
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800136 }
137 });
Thomas Vachuska096bcc82016-03-07 21:30:29 -0800138 }
139
140 @SuppressWarnings("unchecked")
141 private boolean isAssignableFrom(ConfigFactory configFactory, ConfigKey k) {
142 return configFactory.subjectFactory().subjectClass().isAssignableFrom(k.subject.getClass());
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800143 }
144
145 @SuppressWarnings("unchecked")
146 private void validateConfig(ConfigKey key, ConfigFactory configFactory, JsonNode json) {
HIGUCHI Yutaca2208d2016-02-18 15:03:08 -0800147 Object subject;
148 if (key.subject instanceof String) {
149 subject = configFactory.subjectFactory().createSubject((String) key.subject);
150 } else {
151 subject = key.subject;
152 }
153 Config config = createConfig(subject, configFactory.configClass(), json);
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800154 try {
155 checkArgument(config.isValid(), INVALID_CONFIG_JSON);
HIGUCHI Yutaca2208d2016-02-18 15:03:08 -0800156 configs.putAndGet(key(subject, configFactory.configClass()), json);
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800157 } catch (Exception e) {
158 log.warn("Failed to validate pending {} configuration for {}: {}",
HIGUCHI Yutaca2208d2016-02-18 15:03:08 -0800159 key.configKey, key.subject, json);
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800160 }
161 }
162
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700163 @Override
164 public void removeConfigFactory(ConfigFactory configFactory) {
165 factoriesByConfig.remove(configFactory.configClass().getName());
Jonathan Hart73518ac2016-05-20 08:00:22 -0700166 processExistingConfigs(configFactory);
Thomas Vachuskae6360222015-07-21 10:10:36 -0700167 notifyDelegate(new NetworkConfigEvent(CONFIG_UNREGISTERED, configFactory.configKey(),
168 configFactory.configClass()));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700169 }
170
Jonathan Hart73518ac2016-05-20 08:00:22 -0700171 // Sweep through any configurations for the config factory, set back to pending state.
172 private void processExistingConfigs(ConfigFactory configFactory) {
173 ImmutableSet.copyOf(configs.keySet()).forEach(k -> {
174 if (Objects.equals(configFactory.configClass().getName(), k.configClass)) {
175 JsonNode json = configs.remove(k).value();
176 configs.put(key(k.subject, configFactory.configKey()), json);
177 log.debug("Set config pending: {}, {}", k.subject, k.configClass);
178 }
179 });
180 }
181
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700182 @Override
183 @SuppressWarnings("unchecked")
184 public <S, C extends Config<S>> ConfigFactory<S, C> getConfigFactory(Class<C> configClass) {
HIGUCHI Yutaca2208d2016-02-18 15:03:08 -0800185 return factoriesByConfig.get(configClass.getName());
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700186 }
187
188 @Override
189 @SuppressWarnings("unchecked")
190 public <S> Set<S> getSubjects(Class<S> subjectClass) {
191 ImmutableSet.Builder<S> builder = ImmutableSet.builder();
192 configs.keySet().forEach(k -> {
193 if (subjectClass.isInstance(k.subject)) {
194 builder.add((S) k.subject);
195 }
196 });
197 return builder.build();
198 }
199
200 @Override
201 @SuppressWarnings("unchecked")
202 public <S, C extends Config<S>> Set<S> getSubjects(Class<S> subjectClass, Class<C> configClass) {
203 ImmutableSet.Builder<S> builder = ImmutableSet.builder();
204 String cName = configClass.getName();
205 configs.keySet().forEach(k -> {
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800206 if (subjectClass.isInstance(k.subject) && Objects.equals(cName, k.configClass)) {
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700207 builder.add((S) k.subject);
208 }
209 });
210 return builder.build();
211 }
212
213 @Override
214 @SuppressWarnings("unchecked")
215 public <S> Set<Class<? extends Config<S>>> getConfigClasses(S subject) {
216 ImmutableSet.Builder<Class<? extends Config<S>>> builder = ImmutableSet.builder();
217 configs.keySet().forEach(k -> {
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800218 if (Objects.equals(subject, k.subject) && k.configClass != null && delegate != null) {
Jonathan Hart80fe4422016-05-24 18:47:37 -0700219 ConfigFactory<S, ? extends Config<S>> configFactory = factoriesByConfig.get(k.configClass);
220 if (configFactory == null) {
221 log.error("Found config but no config factory: subject={}, configClass={}",
222 subject, k.configClass);
223 }
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700224 builder.add(factoriesByConfig.get(k.configClass).configClass());
225 }
226 });
227 return builder.build();
228 }
229
230 @Override
231 public <S, T extends Config<S>> T getConfig(S subject, Class<T> configClass) {
Madan Jampanic6371882016-06-03 21:30:17 -0700232 Versioned<JsonNode> json = configs.get(key(subject, configClass));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700233 return json != null ? createConfig(subject, configClass, json.value()) : null;
234 }
235
236
237 @Override
238 public <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700239 ConfigFactory<S, C> factory = getConfigFactory(configClass);
240 Versioned<JsonNode> json = configs.computeIfAbsent(key(subject, configClass),
241 k -> factory.isList() ?
242 mapper.createArrayNode() :
243 mapper.createObjectNode());
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700244 return createConfig(subject, configClass, json.value());
245 }
246
247 @Override
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700248 public <S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass, JsonNode json) {
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800249 // Create the configuration and validate it.
250 C config = createConfig(subject, configClass, json);
251 checkArgument(config.isValid(), INVALID_CONFIG_JSON);
252
253 // Insert the validated configuration and get it back.
254 Versioned<JsonNode> versioned = configs.putAndGet(key(subject, configClass), json);
255
256 // Re-create the config if for some reason what we attempted to put
257 // was supplanted by someone else already.
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800258 return versioned.value() == json ? config : createConfig(subject, configClass, versioned.value());
259 }
260
261 @Override
262 public <S> void queueConfig(S subject, String configKey, JsonNode json) {
263 configs.put(key(subject, configKey), json);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700264 }
265
266 @Override
267 public <S, C extends Config<S>> void clearConfig(S subject, Class<C> configClass) {
268 configs.remove(key(subject, configClass));
269 }
270
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800271 @Override
272 public <S> void clearQueuedConfig(S subject, String configKey) {
273 configs.remove(key(subject, configKey));
274 }
275
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700276 /**
277 * Produces a config from the specified subject, config class and raw JSON.
278 *
279 * @param subject config subject
280 * @param configClass config class
281 * @param json raw JSON data
282 * @return config object or null of no factory found or if the specified
283 * JSON is null
284 */
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700285 private <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass,
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700286 JsonNode json) {
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700287 return createConfig(subject, configClass, json, false);
288 }
289
290 /**
291 * Produces a config from the specified subject, config class and raw JSON.
292 *
293 * The config can optionally be detached, which means it does not contain a
294 * reference to an apply delegate. This means a detached config can not be
295 * applied. This should be used only for passing the config object in the
296 * NetworkConfigEvent.
297 *
298 * @param subject config subject
299 * @param configClass config class
300 * @param json raw JSON data
301 * @param detached whether the config should be detached, that is, should
302 * be created without setting an apply delegate.
303 * @return config object or null of no factory found or if the specified
304 * JSON is null
305 */
306 @SuppressWarnings("unchecked")
307 private <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass,
308 JsonNode json, boolean detached) {
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700309 if (json != null) {
310 ConfigFactory<S, C> factory = factoriesByConfig.get(configClass.getName());
311 if (factory != null) {
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700312 validateJsonType(json, factory);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700313 C config = factory.createConfig();
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700314 config.init(subject, factory.configKey(), json, mapper,
315 detached ? null : applyDelegate);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700316 return config;
317 }
318 }
319 return null;
320 }
321
Charles Chan023a8982016-02-04 11:00:41 -0800322 /**
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700323 * Validates that the type of the JSON node is appropriate for the type of
324 * configuration. A list type configuration must be created with an
325 * ArrayNode, and an object type configuration must be created with an
326 * ObjectNode.
Charles Chan023a8982016-02-04 11:00:41 -0800327 *
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700328 * @param json JSON node to check
329 * @param factory config factory of configuration
330 * @param <S> subject
331 * @param <C> configuration
332 * @return true if the JSON node type is appropriate for the configuration
Charles Chan023a8982016-02-04 11:00:41 -0800333 */
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700334 private <S, C extends Config<S>> boolean validateJsonType(JsonNode json,
335 ConfigFactory<S, C> factory) {
336 if (factory.isList() && !(json instanceof ArrayNode)) {
337 throw new IllegalArgumentException(INVALID_JSON_LIST);
Charles Chan023a8982016-02-04 11:00:41 -0800338 }
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700339 if (!factory.isList() && !(json instanceof ObjectNode)) {
340 throw new IllegalArgumentException(INVALID_JSON_OBJECT);
341 }
342
343 return true;
Charles Chan023a8982016-02-04 11:00:41 -0800344 }
345
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700346
347 // Auxiliary delegate to receive notifications about changes applied to
348 // the network configuration - by the apps.
349 private class InternalApplyDelegate implements ConfigApplyDelegate {
350 @Override
351 public void onApply(Config config) {
352 configs.put(key(config.subject(), config.getClass()), config.node());
353 }
354 }
355
356 // Produces a key for uniquely tracking a subject config.
357 private static ConfigKey key(Object subject, Class<?> configClass) {
358 return new ConfigKey(subject, configClass);
359 }
360
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800361 // Produces a key for uniquely tracking a subject config.
362 private static ConfigKey key(Object subject, String configKey) {
363 return new ConfigKey(subject, configKey);
364 }
365
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700366 // Auxiliary key to track subject configurations.
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800367 // Keys with non-null configKey are pending configurations.
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700368 private static final class ConfigKey {
369 final Object subject;
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800370 final String configKey;
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700371 final String configClass;
372
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800373 // Create a key for pending configuration class
374 private ConfigKey(Object subject, String configKey) {
375 this.subject = subject;
376 this.configKey = configKey;
377 this.configClass = null;
378 }
379
380 // Create a key for registered class configuration
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700381 private ConfigKey(Object subject, Class<?> configClass) {
382 this.subject = subject;
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800383 this.configKey = null;
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700384 this.configClass = configClass.getName();
385 }
386
387 @Override
388 public int hashCode() {
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800389 return Objects.hash(subject, configKey, configClass);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700390 }
391
392 @Override
393 public boolean equals(Object obj) {
394 if (this == obj) {
395 return true;
396 }
397 if (obj instanceof ConfigKey) {
398 final ConfigKey other = (ConfigKey) obj;
399 return Objects.equals(this.subject, other.subject)
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800400 && Objects.equals(this.configKey, other.configKey)
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700401 && Objects.equals(this.configClass, other.configClass);
402 }
403 return false;
404 }
405 }
406
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700407 private class InternalMapListener implements MapEventListener<ConfigKey, JsonNode> {
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700408 @Override
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700409 public void event(MapEvent<ConfigKey, JsonNode> event) {
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800410 // Do not delegate pending configs.
411 if (event.key().configClass == null) {
412 return;
413 }
414
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700415 ConfigFactory factory = factoriesByConfig.get(event.key().configClass);
416 if (factory != null) {
Charles Chan023a8982016-02-04 11:00:41 -0800417 Object subject = event.key().subject;
418 Class configClass = factory.configClass();
419 Versioned<JsonNode> newValue = event.newValue();
420 Versioned<JsonNode> oldValue = event.oldValue();
421
422 Config config = (newValue != null) ?
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700423 createConfig(subject, configClass, newValue.value(), true) :
424 null;
Charles Chan023a8982016-02-04 11:00:41 -0800425 Config prevConfig = (oldValue != null) ?
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700426 createConfig(subject, configClass, oldValue.value(), true) :
427 null;
Charles Chan023a8982016-02-04 11:00:41 -0800428
429 NetworkConfigEvent.Type type;
430 switch (event.type()) {
431 case INSERT:
432 type = CONFIG_ADDED;
433 break;
434 case UPDATE:
435 type = CONFIG_UPDATED;
436 break;
437 case REMOVE:
438 default:
439 type = CONFIG_REMOVED;
440 break;
441 }
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700442 notifyDelegate(new NetworkConfigEvent(type, event.key().subject,
Charles Chan023a8982016-02-04 11:00:41 -0800443 config, prevConfig, factory.configClass()));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700444 }
445 }
446 }
447}