blob: 7e1cc7677f9cf6dd392d2a957632f4fc4d5ab9a4 [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);
106 }
107
108 @Override
109 public void removeConfigFactory(ConfigFactory configFactory) {
110 factoriesByConfig.remove(configFactory.configClass().getName());
111 }
112
113 @Override
114 @SuppressWarnings("unchecked")
115 public <S, C extends Config<S>> ConfigFactory<S, C> getConfigFactory(Class<C> configClass) {
116 return (ConfigFactory<S, C>) factoriesByConfig.get(configClass.getName());
117 }
118
119 @Override
120 @SuppressWarnings("unchecked")
121 public <S> Set<S> getSubjects(Class<S> subjectClass) {
122 ImmutableSet.Builder<S> builder = ImmutableSet.builder();
123 configs.keySet().forEach(k -> {
124 if (subjectClass.isInstance(k.subject)) {
125 builder.add((S) k.subject);
126 }
127 });
128 return builder.build();
129 }
130
131 @Override
132 @SuppressWarnings("unchecked")
133 public <S, C extends Config<S>> Set<S> getSubjects(Class<S> subjectClass, Class<C> configClass) {
134 ImmutableSet.Builder<S> builder = ImmutableSet.builder();
135 String cName = configClass.getName();
136 configs.keySet().forEach(k -> {
137 if (subjectClass.isInstance(k.subject) && cName.equals(k.configClass)) {
138 builder.add((S) k.subject);
139 }
140 });
141 return builder.build();
142 }
143
144 @Override
145 @SuppressWarnings("unchecked")
146 public <S> Set<Class<? extends Config<S>>> getConfigClasses(S subject) {
147 ImmutableSet.Builder<Class<? extends Config<S>>> builder = ImmutableSet.builder();
148 configs.keySet().forEach(k -> {
149 if (Objects.equals(subject, k.subject) && delegate != null) {
150 builder.add(factoriesByConfig.get(k.configClass).configClass());
151 }
152 });
153 return builder.build();
154 }
155
156 @Override
157 public <S, T extends Config<S>> T getConfig(S subject, Class<T> configClass) {
158 Versioned<ObjectNode> json = configs.get(key(subject, configClass));
159 return json != null ? createConfig(subject, configClass, json.value()) : null;
160 }
161
162
163 @Override
164 public <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass) {
165 Versioned<ObjectNode> json = configs.computeIfAbsent(key(subject, configClass),
166 k -> mapper.createObjectNode());
167 return createConfig(subject, configClass, json.value());
168 }
169
170 @Override
171 public <S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass, ObjectNode json) {
172 return createConfig(subject, configClass,
173 configs.putAndGet(key(subject, configClass), json).value());
174 }
175
176 @Override
177 public <S, C extends Config<S>> void clearConfig(S subject, Class<C> configClass) {
178 configs.remove(key(subject, configClass));
179 }
180
181 /**
182 * Produces a config from the specified subject, config class and raw JSON.
183 *
184 * @param subject config subject
185 * @param configClass config class
186 * @param json raw JSON data
187 * @return config object or null of no factory found or if the specified
188 * JSON is null
189 */
190 @SuppressWarnings("unchecked")
191 private <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass,
192 ObjectNode json) {
193 if (json != null) {
194 ConfigFactory<S, C> factory = factoriesByConfig.get(configClass.getName());
195 if (factory != null) {
196 C config = factory.createConfig();
197 config.init(subject, factory.configKey(), json, mapper, applyDelegate);
198 return config;
199 }
200 }
201 return null;
202 }
203
204
205 // Auxiliary delegate to receive notifications about changes applied to
206 // the network configuration - by the apps.
207 private class InternalApplyDelegate implements ConfigApplyDelegate {
208 @Override
209 public void onApply(Config config) {
210 configs.put(key(config.subject(), config.getClass()), config.node());
211 }
212 }
213
214 // Produces a key for uniquely tracking a subject config.
215 private static ConfigKey key(Object subject, Class<?> configClass) {
216 return new ConfigKey(subject, configClass);
217 }
218
219 // Auxiliary key to track subject configurations.
220 private static final class ConfigKey {
221 final Object subject;
222 final String configClass;
223
224 private ConfigKey(Object subject, Class<?> configClass) {
225 this.subject = subject;
226 this.configClass = configClass.getName();
227 }
228
229 @Override
230 public int hashCode() {
231 return Objects.hash(subject, configClass);
232 }
233
234 @Override
235 public boolean equals(Object obj) {
236 if (this == obj) {
237 return true;
238 }
239 if (obj instanceof ConfigKey) {
240 final ConfigKey other = (ConfigKey) obj;
241 return Objects.equals(this.subject, other.subject)
242 && Objects.equals(this.configClass, other.configClass);
243 }
244 return false;
245 }
246 }
247
248 private class InternalMapListener implements MapEventListener<ConfigKey, ObjectNode> {
249 @Override
250 public void event(MapEvent<ConfigKey, ObjectNode> event) {
251 NetworkConfigEvent.Type type;
252 switch (event.type()) {
253 case INSERT:
254 type = CONFIG_ADDED;
255 break;
256 case UPDATE:
257 type = CONFIG_UPDATED;
258 break;
259 case REMOVE:
260 default:
261 type = CONFIG_REMOVED;
262 break;
263 }
264 ConfigFactory factory = factoriesByConfig.get(event.key().configClass);
265 if (factory != null) {
266 notifyDelegate(new NetworkConfigEvent(type, event.key().subject,
267 factory.configClass()));
268 }
269 }
270 }
271}