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