Committed by
Gerrit Code Review
New ApplicationStore that uses a single ConsistentMap to track all app related state
Change-Id: Ieacc97f213add8ece8f462cd9971fb6ef3d0dde5 (cherry picked from commit 6c02d9e1)
Showing
5 changed files
with
191 additions
and
139 deletions
... | @@ -96,7 +96,7 @@ public class Versioned<V> { | ... | @@ -96,7 +96,7 @@ public class Versioned<V> { |
96 | * @param <U> value type of the returned instance | 96 | * @param <U> value type of the returned instance |
97 | * @return mapped instance | 97 | * @return mapped instance |
98 | */ | 98 | */ |
99 | - public <U> Versioned<U> map(Function<V, U> transformer) { | 99 | + public synchronized <U> Versioned<U> map(Function<V, U> transformer) { |
100 | return new Versioned<>(transformer.apply(value), version, creationTime); | 100 | return new Versioned<>(transformer.apply(value), version, creationTime); |
101 | } | 101 | } |
102 | 102 | ... | ... |
1 | /* | 1 | /* |
2 | - * Copyright 2015-present Open Networking Laboratory | 2 | + * Copyright 2016-present Open Networking Laboratory |
3 | * | 3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
... | @@ -16,18 +16,20 @@ | ... | @@ -16,18 +16,20 @@ |
16 | package org.onosproject.store.app; | 16 | package org.onosproject.store.app; |
17 | 17 | ||
18 | import com.google.common.base.Charsets; | 18 | import com.google.common.base.Charsets; |
19 | +import com.google.common.base.MoreObjects; | ||
20 | +import com.google.common.base.Preconditions; | ||
19 | import com.google.common.collect.ImmutableSet; | 21 | import com.google.common.collect.ImmutableSet; |
20 | import com.google.common.collect.Lists; | 22 | import com.google.common.collect.Lists; |
21 | import com.google.common.collect.Maps; | 23 | import com.google.common.collect.Maps; |
22 | import com.google.common.collect.Multimap; | 24 | import com.google.common.collect.Multimap; |
23 | import com.google.common.collect.Sets; | 25 | import com.google.common.collect.Sets; |
26 | + | ||
24 | import org.apache.felix.scr.annotations.Activate; | 27 | import org.apache.felix.scr.annotations.Activate; |
25 | import org.apache.felix.scr.annotations.Component; | 28 | import org.apache.felix.scr.annotations.Component; |
26 | import org.apache.felix.scr.annotations.Deactivate; | 29 | import org.apache.felix.scr.annotations.Deactivate; |
27 | import org.apache.felix.scr.annotations.Reference; | 30 | import org.apache.felix.scr.annotations.Reference; |
28 | import org.apache.felix.scr.annotations.ReferenceCardinality; | 31 | import org.apache.felix.scr.annotations.ReferenceCardinality; |
29 | import org.apache.felix.scr.annotations.Service; | 32 | import org.apache.felix.scr.annotations.Service; |
30 | -import org.onlab.util.KryoNamespace; | ||
31 | import org.onosproject.app.ApplicationDescription; | 33 | import org.onosproject.app.ApplicationDescription; |
32 | import org.onosproject.app.ApplicationEvent; | 34 | import org.onosproject.app.ApplicationEvent; |
33 | import org.onosproject.app.ApplicationException; | 35 | import org.onosproject.app.ApplicationException; |
... | @@ -46,13 +48,14 @@ import org.onosproject.security.Permission; | ... | @@ -46,13 +48,14 @@ import org.onosproject.security.Permission; |
46 | import org.onosproject.store.cluster.messaging.ClusterCommunicationService; | 48 | import org.onosproject.store.cluster.messaging.ClusterCommunicationService; |
47 | import org.onosproject.store.cluster.messaging.MessageSubject; | 49 | import org.onosproject.store.cluster.messaging.MessageSubject; |
48 | import org.onosproject.store.serializers.KryoNamespaces; | 50 | import org.onosproject.store.serializers.KryoNamespaces; |
49 | -import org.onosproject.store.service.EventuallyConsistentMap; | 51 | +import org.onosproject.store.service.ConsistentMap; |
50 | -import org.onosproject.store.service.EventuallyConsistentMapEvent; | 52 | +import org.onosproject.store.service.MapEvent; |
51 | -import org.onosproject.store.service.EventuallyConsistentMapListener; | 53 | +import org.onosproject.store.service.MapEventListener; |
52 | -import org.onosproject.store.service.LogicalClockService; | 54 | +import org.onosproject.store.service.Serializer; |
53 | -import org.onosproject.store.service.MultiValuedTimestamp; | ||
54 | import org.onosproject.store.service.StorageException; | 55 | import org.onosproject.store.service.StorageException; |
55 | import org.onosproject.store.service.StorageService; | 56 | import org.onosproject.store.service.StorageService; |
57 | +import org.onosproject.store.service.Versioned; | ||
58 | +import org.onosproject.store.service.DistributedPrimitive.Status; | ||
56 | import org.slf4j.Logger; | 59 | import org.slf4j.Logger; |
57 | 60 | ||
58 | import java.io.ByteArrayInputStream; | 61 | import java.io.ByteArrayInputStream; |
... | @@ -66,7 +69,10 @@ import java.util.concurrent.ExecutorService; | ... | @@ -66,7 +69,10 @@ import java.util.concurrent.ExecutorService; |
66 | import java.util.concurrent.Executors; | 69 | import java.util.concurrent.Executors; |
67 | import java.util.concurrent.ScheduledExecutorService; | 70 | import java.util.concurrent.ScheduledExecutorService; |
68 | import java.util.concurrent.TimeUnit; | 71 | import java.util.concurrent.TimeUnit; |
72 | +import java.util.concurrent.atomic.AtomicBoolean; | ||
73 | +import java.util.function.Consumer; | ||
69 | import java.util.function.Function; | 74 | import java.util.function.Function; |
75 | +import java.util.stream.Collectors; | ||
70 | 76 | ||
71 | import static com.google.common.collect.Multimaps.newSetMultimap; | 77 | import static com.google.common.collect.Multimaps.newSetMultimap; |
72 | import static com.google.common.collect.Multimaps.synchronizedSetMultimap; | 78 | import static com.google.common.collect.Multimaps.synchronizedSetMultimap; |
... | @@ -76,18 +82,16 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS; | ... | @@ -76,18 +82,16 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS; |
76 | import static org.onlab.util.Tools.groupedThreads; | 82 | import static org.onlab.util.Tools.groupedThreads; |
77 | import static org.onlab.util.Tools.randomDelay; | 83 | import static org.onlab.util.Tools.randomDelay; |
78 | import static org.onosproject.app.ApplicationEvent.Type.*; | 84 | import static org.onosproject.app.ApplicationEvent.Type.*; |
79 | -import static org.onosproject.store.app.GossipApplicationStore.InternalState.*; | 85 | +import static org.onosproject.store.app.DistributedApplicationStore.InternalState.*; |
80 | -import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.PUT; | ||
81 | -import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.REMOVE; | ||
82 | import static org.slf4j.LoggerFactory.getLogger; | 86 | import static org.slf4j.LoggerFactory.getLogger; |
83 | 87 | ||
84 | /** | 88 | /** |
85 | - * Manages inventory of applications in a distributed data store that uses | 89 | + * Manages inventory of applications in a distributed data store providing |
86 | - * optimistic replication and gossip based anti-entropy techniques. | 90 | + * stronger consistency guarantees. |
87 | */ | 91 | */ |
88 | -@Component(immediate = true) | 92 | +@Component(immediate = true, enabled = true) |
89 | @Service | 93 | @Service |
90 | -public class GossipApplicationStore extends ApplicationArchive | 94 | +public class DistributedApplicationStore extends ApplicationArchive |
91 | implements ApplicationStore { | 95 | implements ApplicationStore { |
92 | 96 | ||
93 | private final Logger log = getLogger(getClass()); | 97 | private final Logger log = getLogger(getClass()); |
... | @@ -110,9 +114,7 @@ public class GossipApplicationStore extends ApplicationArchive | ... | @@ -110,9 +114,7 @@ public class GossipApplicationStore extends ApplicationArchive |
110 | private ScheduledExecutorService executor; | 114 | private ScheduledExecutorService executor; |
111 | private ExecutorService messageHandlingExecutor; | 115 | private ExecutorService messageHandlingExecutor; |
112 | 116 | ||
113 | - private EventuallyConsistentMap<ApplicationId, Application> apps; | 117 | + private ConsistentMap<ApplicationId, InternalApplicationHolder> apps; |
114 | - private EventuallyConsistentMap<Application, InternalState> states; | ||
115 | - private EventuallyConsistentMap<Application, Set<Permission>> permissions; | ||
116 | 118 | ||
117 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 119 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
118 | protected ClusterCommunicationService clusterCommunicator; | 120 | protected ClusterCommunicationService clusterCommunicator; |
... | @@ -124,11 +126,12 @@ public class GossipApplicationStore extends ApplicationArchive | ... | @@ -124,11 +126,12 @@ public class GossipApplicationStore extends ApplicationArchive |
124 | protected StorageService storageService; | 126 | protected StorageService storageService; |
125 | 127 | ||
126 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 128 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
127 | - protected LogicalClockService clockService; | ||
128 | - | ||
129 | - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
130 | protected ApplicationIdStore idStore; | 129 | protected ApplicationIdStore idStore; |
131 | 130 | ||
131 | + private final InternalAppsListener appsListener = new InternalAppsListener(); | ||
132 | + | ||
133 | + private Consumer<Status> statusChangeListener; | ||
134 | + | ||
132 | // Multimap to track which apps are required by others apps | 135 | // Multimap to track which apps are required by others apps |
133 | // app -> { required-by, ... } | 136 | // app -> { required-by, ... } |
134 | // Apps explicitly activated will be required by the CORE app | 137 | // Apps explicitly activated will be required by the CORE app |
... | @@ -139,16 +142,8 @@ public class GossipApplicationStore extends ApplicationArchive | ... | @@ -139,16 +142,8 @@ public class GossipApplicationStore extends ApplicationArchive |
139 | 142 | ||
140 | @Activate | 143 | @Activate |
141 | public void activate() { | 144 | public void activate() { |
142 | - KryoNamespace.Builder serializer = KryoNamespace.newBuilder() | ||
143 | - .register(KryoNamespaces.API) | ||
144 | - .register(MultiValuedTimestamp.class) | ||
145 | - .register(InternalState.class); | ||
146 | - | ||
147 | - executor = newSingleThreadScheduledExecutor(groupedThreads("onos/app", "store", log)); | ||
148 | - | ||
149 | messageHandlingExecutor = Executors.newSingleThreadExecutor( | 145 | messageHandlingExecutor = Executors.newSingleThreadExecutor( |
150 | groupedThreads("onos/store/app", "message-handler", log)); | 146 | groupedThreads("onos/store/app", "message-handler", log)); |
151 | - | ||
152 | clusterCommunicator.<String, byte[]>addSubscriber(APP_BITS_REQUEST, | 147 | clusterCommunicator.<String, byte[]>addSubscriber(APP_BITS_REQUEST, |
153 | bytes -> new String(bytes, Charsets.UTF_8), | 148 | bytes -> new String(bytes, Charsets.UTF_8), |
154 | name -> { | 149 | name -> { |
... | @@ -161,33 +156,36 @@ public class GossipApplicationStore extends ApplicationArchive | ... | @@ -161,33 +156,36 @@ public class GossipApplicationStore extends ApplicationArchive |
161 | Function.identity(), | 156 | Function.identity(), |
162 | messageHandlingExecutor); | 157 | messageHandlingExecutor); |
163 | 158 | ||
164 | - // FIXME: Consider consolidating into a single map. | 159 | + apps = storageService.<ApplicationId, InternalApplicationHolder>consistentMapBuilder() |
165 | - | 160 | + .withName("onos-apps") |
166 | - apps = storageService.<ApplicationId, Application>eventuallyConsistentMapBuilder() | 161 | + .withRelaxedReadConsistency() |
167 | - .withName("apps") | 162 | + .withSerializer(Serializer.using(KryoNamespaces.API, |
168 | - .withSerializer(serializer) | 163 | + InternalApplicationHolder.class, |
169 | - .withTimestampProvider((k, v) -> clockService.getTimestamp()) | 164 | + InternalState.class)) |
170 | - .build(); | ||
171 | - | ||
172 | - states = storageService.<Application, InternalState>eventuallyConsistentMapBuilder() | ||
173 | - .withName("app-states") | ||
174 | - .withSerializer(serializer) | ||
175 | - .withTimestampProvider((k, v) -> clockService.getTimestamp()) | ||
176 | - .build(); | ||
177 | - | ||
178 | - states.addListener(new InternalAppStatesListener()); | ||
179 | - | ||
180 | - permissions = storageService.<Application, Set<Permission>>eventuallyConsistentMapBuilder() | ||
181 | - .withName("app-permissions") | ||
182 | - .withSerializer(serializer) | ||
183 | - .withTimestampProvider((k, v) -> clockService.getTimestamp()) | ||
184 | .build(); | 165 | .build(); |
185 | 166 | ||
167 | + executor = newSingleThreadScheduledExecutor(groupedThreads("onos/app", "store", log)); | ||
168 | + statusChangeListener = status -> { | ||
169 | + if (status == Status.ACTIVE) { | ||
170 | + executor.execute(this::bootstrapExistingApplications); | ||
171 | + } | ||
172 | + }; | ||
173 | + apps.addListener(appsListener, messageHandlingExecutor); | ||
174 | + apps.addStatusChangeListener(statusChangeListener); | ||
186 | coreAppId = getId(CoreService.CORE_APP_NAME); | 175 | coreAppId = getId(CoreService.CORE_APP_NAME); |
187 | log.info("Started"); | 176 | log.info("Started"); |
188 | } | 177 | } |
189 | 178 | ||
190 | /** | 179 | /** |
180 | + * Processes existing applications from the distributed map. This is done to | ||
181 | + * account for events that this instance may be have missed due to a staggered start. | ||
182 | + */ | ||
183 | + void bootstrapExistingApplications() { | ||
184 | + apps.asJavaMap().forEach((appId, holder) -> setupApplicationAndNotify(appId, holder.app(), holder.state())); | ||
185 | + | ||
186 | + } | ||
187 | + | ||
188 | + /** | ||
191 | * Loads the application inventory from the disk and activates apps if | 189 | * Loads the application inventory from the disk and activates apps if |
192 | * they are marked to be active. | 190 | * they are marked to be active. |
193 | */ | 191 | */ |
... | @@ -244,23 +242,27 @@ public class GossipApplicationStore extends ApplicationArchive | ... | @@ -244,23 +242,27 @@ public class GossipApplicationStore extends ApplicationArchive |
244 | @Deactivate | 242 | @Deactivate |
245 | public void deactivate() { | 243 | public void deactivate() { |
246 | clusterCommunicator.removeSubscriber(APP_BITS_REQUEST); | 244 | clusterCommunicator.removeSubscriber(APP_BITS_REQUEST); |
245 | + apps.removeStatusChangeListener(statusChangeListener); | ||
246 | + apps.removeListener(appsListener); | ||
247 | messageHandlingExecutor.shutdown(); | 247 | messageHandlingExecutor.shutdown(); |
248 | executor.shutdown(); | 248 | executor.shutdown(); |
249 | - apps.destroy(); | ||
250 | - states.destroy(); | ||
251 | - permissions.destroy(); | ||
252 | log.info("Stopped"); | 249 | log.info("Stopped"); |
253 | } | 250 | } |
254 | 251 | ||
255 | @Override | 252 | @Override |
256 | public void setDelegate(ApplicationStoreDelegate delegate) { | 253 | public void setDelegate(ApplicationStoreDelegate delegate) { |
257 | super.setDelegate(delegate); | 254 | super.setDelegate(delegate); |
255 | + executor.execute(this::bootstrapExistingApplications); | ||
258 | executor.schedule(() -> loadFromDisk(), APP_LOAD_DELAY_MS, TimeUnit.MILLISECONDS); | 256 | executor.schedule(() -> loadFromDisk(), APP_LOAD_DELAY_MS, TimeUnit.MILLISECONDS); |
259 | } | 257 | } |
260 | 258 | ||
261 | @Override | 259 | @Override |
262 | public Set<Application> getApplications() { | 260 | public Set<Application> getApplications() { |
263 | - return ImmutableSet.copyOf(apps.values()); | 261 | + return ImmutableSet.copyOf(apps.values() |
262 | + .stream() | ||
263 | + .map(Versioned::value) | ||
264 | + .map(InternalApplicationHolder::app) | ||
265 | + .collect(Collectors.toSet())); | ||
264 | } | 266 | } |
265 | 267 | ||
266 | @Override | 268 | @Override |
... | @@ -270,15 +272,15 @@ public class GossipApplicationStore extends ApplicationArchive | ... | @@ -270,15 +272,15 @@ public class GossipApplicationStore extends ApplicationArchive |
270 | 272 | ||
271 | @Override | 273 | @Override |
272 | public Application getApplication(ApplicationId appId) { | 274 | public Application getApplication(ApplicationId appId) { |
273 | - return apps.get(appId); | 275 | + InternalApplicationHolder appHolder = Versioned.valueOrNull(apps.get(appId)); |
276 | + return appHolder != null ? appHolder.app() : null; | ||
274 | } | 277 | } |
275 | 278 | ||
276 | @Override | 279 | @Override |
277 | public ApplicationState getState(ApplicationId appId) { | 280 | public ApplicationState getState(ApplicationId appId) { |
278 | - Application app = apps.get(appId); | 281 | + InternalApplicationHolder appHolder = Versioned.valueOrNull(apps.get(appId)); |
279 | - InternalState s = app == null ? null : states.get(app); | 282 | + InternalState state = appHolder != null ? appHolder.state() : null; |
280 | - return s == null ? null : s == ACTIVATED ? | 283 | + return state == null ? null : state == ACTIVATED ? ApplicationState.ACTIVE : ApplicationState.INSTALLED; |
281 | - ApplicationState.ACTIVE : ApplicationState.INSTALLED; | ||
282 | } | 284 | } |
283 | 285 | ||
284 | @Override | 286 | @Override |
... | @@ -300,26 +302,21 @@ public class GossipApplicationStore extends ApplicationArchive | ... | @@ -300,26 +302,21 @@ public class GossipApplicationStore extends ApplicationArchive |
300 | if (updateTime) { | 302 | if (updateTime) { |
301 | updateTime(app.id().name()); | 303 | updateTime(app.id().name()); |
302 | } | 304 | } |
303 | - apps.put(app.id(), app); | 305 | + InternalApplicationHolder previousApp = |
304 | - states.put(app, INSTALLED); | 306 | + Versioned.valueOrNull(apps.putIfAbsent(app.id(), new InternalApplicationHolder(app, INSTALLED, null))); |
305 | - return app; | 307 | + return previousApp != null ? previousApp.app() : app; |
306 | } | 308 | } |
307 | 309 | ||
308 | @Override | 310 | @Override |
309 | public void remove(ApplicationId appId) { | 311 | public void remove(ApplicationId appId) { |
310 | - Application app = apps.get(appId); | 312 | + uninstallDependentApps(appId); |
311 | - if (app != null) { | 313 | + apps.remove(appId); |
312 | - uninstallDependentApps(app); | ||
313 | - apps.remove(appId); | ||
314 | - states.remove(app); | ||
315 | - permissions.remove(app); | ||
316 | - } | ||
317 | } | 314 | } |
318 | 315 | ||
319 | // Uninstalls all apps that depend on the given app. | 316 | // Uninstalls all apps that depend on the given app. |
320 | - private void uninstallDependentApps(Application app) { | 317 | + private void uninstallDependentApps(ApplicationId appId) { |
321 | getApplications().stream() | 318 | getApplications().stream() |
322 | - .filter(a -> a.requiredApps().contains(app.id().name())) | 319 | + .filter(a -> a.requiredApps().contains(appId.name())) |
323 | .forEach(a -> remove(a.id())); | 320 | .forEach(a -> remove(a.id())); |
324 | } | 321 | } |
325 | 322 | ||
... | @@ -335,13 +332,18 @@ public class GossipApplicationStore extends ApplicationArchive | ... | @@ -335,13 +332,18 @@ public class GossipApplicationStore extends ApplicationArchive |
335 | 332 | ||
336 | 333 | ||
337 | private void activate(ApplicationId appId, boolean updateTime) { | 334 | private void activate(ApplicationId appId, boolean updateTime) { |
338 | - Application app = apps.get(appId); | 335 | + AtomicBoolean stateChanged = new AtomicBoolean(false); |
339 | - if (app != null) { | 336 | + InternalApplicationHolder appHolder = Versioned.valueOrNull(apps.computeIf(appId, |
337 | + v -> v != null && v.state() != ACTIVATED, | ||
338 | + (k, v) -> { | ||
339 | + stateChanged.set(true); | ||
340 | + return new InternalApplicationHolder(v.app(), ACTIVATED, v.permissions()); | ||
341 | + })); | ||
342 | + if (stateChanged.get()) { | ||
340 | if (updateTime) { | 343 | if (updateTime) { |
341 | updateTime(appId.name()); | 344 | updateTime(appId.name()); |
342 | } | 345 | } |
343 | - activateRequiredApps(app); | 346 | + activateRequiredApps(appHolder.app()); |
344 | - states.put(app, ACTIVATED); | ||
345 | } | 347 | } |
346 | } | 348 | } |
347 | 349 | ||
... | @@ -352,90 +354,108 @@ public class GossipApplicationStore extends ApplicationArchive | ... | @@ -352,90 +354,108 @@ public class GossipApplicationStore extends ApplicationArchive |
352 | 354 | ||
353 | @Override | 355 | @Override |
354 | public void deactivate(ApplicationId appId) { | 356 | public void deactivate(ApplicationId appId) { |
355 | - deactivateDependentApps(getApplication(appId)); | 357 | + deactivateDependentApps(appId); |
356 | deactivate(appId, coreAppId); | 358 | deactivate(appId, coreAppId); |
357 | } | 359 | } |
358 | 360 | ||
359 | private void deactivate(ApplicationId appId, ApplicationId forAppId) { | 361 | private void deactivate(ApplicationId appId, ApplicationId forAppId) { |
360 | requiredBy.remove(appId, forAppId); | 362 | requiredBy.remove(appId, forAppId); |
361 | if (requiredBy.get(appId).isEmpty()) { | 363 | if (requiredBy.get(appId).isEmpty()) { |
362 | - Application app = apps.get(appId); | 364 | + AtomicBoolean stateChanged = new AtomicBoolean(false); |
363 | - if (app != null) { | 365 | + apps.computeIf(appId, |
366 | + v -> v != null && v.state() != DEACTIVATED, | ||
367 | + (k, v) -> { | ||
368 | + stateChanged.set(true); | ||
369 | + return new InternalApplicationHolder(v.app(), DEACTIVATED, v.permissions()); | ||
370 | + }); | ||
371 | + if (stateChanged.get()) { | ||
364 | updateTime(appId.name()); | 372 | updateTime(appId.name()); |
365 | - states.put(app, DEACTIVATED); | 373 | + deactivateRequiredApps(appId); |
366 | - deactivateRequiredApps(app); | ||
367 | } | 374 | } |
368 | } | 375 | } |
369 | } | 376 | } |
370 | 377 | ||
371 | // Deactivates all apps that require this application. | 378 | // Deactivates all apps that require this application. |
372 | - private void deactivateDependentApps(Application app) { | 379 | + private void deactivateDependentApps(ApplicationId appId) { |
373 | - getApplications().stream() | 380 | + apps.values() |
374 | - .filter(a -> states.get(a) == ACTIVATED) | 381 | + .stream() |
375 | - .filter(a -> a.requiredApps().contains(app.id().name())) | 382 | + .map(Versioned::value) |
376 | - .forEach(a -> deactivate(a.id())); | 383 | + .filter(a -> a.state() == ACTIVATED) |
384 | + .filter(a -> a.app().requiredApps().contains(appId.name())) | ||
385 | + .forEach(a -> deactivate(a.app().id())); | ||
377 | } | 386 | } |
378 | 387 | ||
379 | // Deactivates all apps required by this application. | 388 | // Deactivates all apps required by this application. |
380 | - private void deactivateRequiredApps(Application app) { | 389 | + private void deactivateRequiredApps(ApplicationId appId) { |
381 | - app.requiredApps().stream().map(this::getId).map(this::getApplication) | 390 | + getApplication(appId).requiredApps() |
382 | - .filter(a -> states.get(a) == ACTIVATED) | 391 | + .stream() |
383 | - .forEach(a -> deactivate(a.id(), app.id())); | 392 | + .map(this::getId) |
393 | + .map(apps::get) | ||
394 | + .map(Versioned::value) | ||
395 | + .filter(a -> a.state() == ACTIVATED) | ||
396 | + .forEach(a -> deactivate(a.app().id(), appId)); | ||
384 | } | 397 | } |
385 | 398 | ||
386 | @Override | 399 | @Override |
387 | public Set<Permission> getPermissions(ApplicationId appId) { | 400 | public Set<Permission> getPermissions(ApplicationId appId) { |
388 | - Application app = apps.get(appId); | 401 | + InternalApplicationHolder app = Versioned.valueOrNull(apps.get(appId)); |
389 | - return app != null ? permissions.get(app) : null; | 402 | + return app != null ? ImmutableSet.copyOf(app.permissions()) : ImmutableSet.of(); |
390 | } | 403 | } |
391 | 404 | ||
392 | @Override | 405 | @Override |
393 | public void setPermissions(ApplicationId appId, Set<Permission> permissions) { | 406 | public void setPermissions(ApplicationId appId, Set<Permission> permissions) { |
394 | - Application app = getApplication(appId); | 407 | + AtomicBoolean permissionsChanged = new AtomicBoolean(false); |
395 | - if (app != null) { | 408 | + Versioned<InternalApplicationHolder> appHolder = apps.computeIf(appId, |
396 | - this.permissions.put(app, permissions); | 409 | + v -> v != null && !Sets.symmetricDifference(v.permissions(), permissions).isEmpty(), |
397 | - delegate.notify(new ApplicationEvent(APP_PERMISSIONS_CHANGED, app)); | 410 | + (k, v) -> { |
411 | + permissionsChanged.set(true); | ||
412 | + return new InternalApplicationHolder(v.app(), v.state(), ImmutableSet.copyOf(permissions)); | ||
413 | + }); | ||
414 | + if (permissionsChanged.get()) { | ||
415 | + delegate.notify(new ApplicationEvent(APP_PERMISSIONS_CHANGED, appHolder.value().app())); | ||
398 | } | 416 | } |
399 | } | 417 | } |
400 | 418 | ||
401 | /** | 419 | /** |
402 | * Listener to application state distributed map changes. | 420 | * Listener to application state distributed map changes. |
403 | */ | 421 | */ |
404 | - private final class InternalAppStatesListener | 422 | + private final class InternalAppsListener |
405 | - implements EventuallyConsistentMapListener<Application, InternalState> { | 423 | + implements MapEventListener<ApplicationId, InternalApplicationHolder> { |
406 | @Override | 424 | @Override |
407 | - public void event(EventuallyConsistentMapEvent<Application, InternalState> event) { | 425 | + public void event(MapEvent<ApplicationId, InternalApplicationHolder> event) { |
408 | - // If we do not have a delegate, refuse to process any events entirely. | ||
409 | - // This is to allow the anti-entropy to kick in and process the events | ||
410 | - // perhaps a bit later, but with opportunity to notify delegate. | ||
411 | if (delegate == null) { | 426 | if (delegate == null) { |
412 | return; | 427 | return; |
413 | } | 428 | } |
414 | 429 | ||
415 | - Application app = event.key(); | 430 | + ApplicationId appId = event.key(); |
416 | - InternalState state = event.value(); | 431 | + InternalApplicationHolder newApp = event.newValue() == null ? null : event.newValue().value(); |
417 | - | 432 | + InternalApplicationHolder oldApp = event.oldValue() == null ? null : event.oldValue().value(); |
418 | - if (event.type() == PUT) { | 433 | + if (event.type() == MapEvent.Type.INSERT || event.type() == MapEvent.Type.UPDATE) { |
419 | - if (state == INSTALLED) { | 434 | + if (event.type() == MapEvent.Type.UPDATE && newApp.state() == oldApp.state()) { |
420 | - fetchBitsIfNeeded(app); | 435 | + return; |
421 | - delegate.notify(new ApplicationEvent(APP_INSTALLED, app)); | ||
422 | - | ||
423 | - } else if (state == ACTIVATED) { | ||
424 | - installAppIfNeeded(app); | ||
425 | - setActive(app.id().name()); | ||
426 | - delegate.notify(new ApplicationEvent(APP_ACTIVATED, app)); | ||
427 | - | ||
428 | - } else if (state == DEACTIVATED) { | ||
429 | - clearActive(app.id().name()); | ||
430 | - delegate.notify(new ApplicationEvent(APP_DEACTIVATED, app)); | ||
431 | } | 436 | } |
432 | - } else if (event.type() == REMOVE) { | 437 | + setupApplicationAndNotify(appId, newApp.app(), newApp.state()); |
433 | - delegate.notify(new ApplicationEvent(APP_UNINSTALLED, app)); | 438 | + } else if (event.type() == MapEvent.Type.REMOVE) { |
434 | - purgeApplication(app.id().name()); | 439 | + delegate.notify(new ApplicationEvent(APP_UNINSTALLED, oldApp.app())); |
440 | + purgeApplication(appId.name()); | ||
435 | } | 441 | } |
436 | } | 442 | } |
437 | } | 443 | } |
438 | 444 | ||
445 | + private void setupApplicationAndNotify(ApplicationId appId, Application app, InternalState state) { | ||
446 | + if (state == INSTALLED) { | ||
447 | + fetchBitsIfNeeded(app); | ||
448 | + delegate.notify(new ApplicationEvent(APP_INSTALLED, app)); | ||
449 | + } else if (state == ACTIVATED) { | ||
450 | + installAppIfNeeded(app); | ||
451 | + setActive(appId.name()); | ||
452 | + delegate.notify(new ApplicationEvent(APP_ACTIVATED, app)); | ||
453 | + } else if (state == DEACTIVATED) { | ||
454 | + clearActive(appId.name()); | ||
455 | + delegate.notify(new ApplicationEvent(APP_DEACTIVATED, app)); | ||
456 | + } | ||
457 | + } | ||
458 | + | ||
439 | /** | 459 | /** |
440 | * Determines if the application bits are available locally. | 460 | * Determines if the application bits are available locally. |
441 | */ | 461 | */ |
... | @@ -512,19 +532,6 @@ public class GossipApplicationStore extends ApplicationArchive | ... | @@ -512,19 +532,6 @@ public class GossipApplicationStore extends ApplicationArchive |
512 | } | 532 | } |
513 | 533 | ||
514 | /** | 534 | /** |
515 | - * Prunes applications which are not in the map, but are on disk. | ||
516 | - */ | ||
517 | - private void pruneUninstalledApps() { | ||
518 | - for (String name : getApplicationNames()) { | ||
519 | - if (getApplication(getId(name)) == null) { | ||
520 | - Application app = registerApp(getApplicationDescription(name)); | ||
521 | - delegate.notify(new ApplicationEvent(APP_UNINSTALLED, app)); | ||
522 | - purgeApplication(app.id().name()); | ||
523 | - } | ||
524 | - } | ||
525 | - } | ||
526 | - | ||
527 | - /** | ||
528 | * Produces a registered application from the supplied description. | 535 | * Produces a registered application from the supplied description. |
529 | */ | 536 | */ |
530 | private Application registerApp(ApplicationDescription appDesc) { | 537 | private Application registerApp(ApplicationDescription appDesc) { |
... | @@ -544,4 +551,46 @@ public class GossipApplicationStore extends ApplicationArchive | ... | @@ -544,4 +551,46 @@ public class GossipApplicationStore extends ApplicationArchive |
544 | appDesc.features(), | 551 | appDesc.features(), |
545 | appDesc.requiredApps()); | 552 | appDesc.requiredApps()); |
546 | } | 553 | } |
554 | + | ||
555 | + /** | ||
556 | + * Internal class for holding app information. | ||
557 | + */ | ||
558 | + private static class InternalApplicationHolder { | ||
559 | + private final Application app; | ||
560 | + private final InternalState state; | ||
561 | + private final Set<Permission> permissions; | ||
562 | + | ||
563 | + @SuppressWarnings("unused") | ||
564 | + private InternalApplicationHolder() { | ||
565 | + app = null; | ||
566 | + state = null; | ||
567 | + permissions = null; | ||
568 | + } | ||
569 | + | ||
570 | + public InternalApplicationHolder(Application app, InternalState state, Set<Permission> permissions) { | ||
571 | + this.app = Preconditions.checkNotNull(app); | ||
572 | + this.state = state; | ||
573 | + this.permissions = permissions == null ? null : ImmutableSet.copyOf(permissions); | ||
574 | + } | ||
575 | + | ||
576 | + public Application app() { | ||
577 | + return app; | ||
578 | + } | ||
579 | + | ||
580 | + public InternalState state() { | ||
581 | + return state; | ||
582 | + } | ||
583 | + | ||
584 | + public Set<Permission> permissions() { | ||
585 | + return permissions; | ||
586 | + } | ||
587 | + | ||
588 | + @Override | ||
589 | + public String toString() { | ||
590 | + return MoreObjects.toStringHelper(getClass()) | ||
591 | + .add("app", app.id()) | ||
592 | + .add("state", state) | ||
593 | + .toString(); | ||
594 | + } | ||
595 | + } | ||
547 | } | 596 | } | ... | ... |
... | @@ -38,7 +38,6 @@ import org.onosproject.store.service.MapEvent; | ... | @@ -38,7 +38,6 @@ import org.onosproject.store.service.MapEvent; |
38 | import org.onosproject.store.service.MapEventListener; | 38 | import org.onosproject.store.service.MapEventListener; |
39 | import org.onosproject.store.service.Serializer; | 39 | import org.onosproject.store.service.Serializer; |
40 | import org.onosproject.store.service.StorageService; | 40 | import org.onosproject.store.service.StorageService; |
41 | -import org.onosproject.store.service.Versioned; | ||
42 | import org.slf4j.Logger; | 41 | import org.slf4j.Logger; |
43 | 42 | ||
44 | 43 | ||
... | @@ -109,8 +108,14 @@ public class DistributedApplicationIdStore implements ApplicationIdStore { | ... | @@ -109,8 +108,14 @@ public class DistributedApplicationIdStore implements ApplicationIdStore { |
109 | 108 | ||
110 | @Override | 109 | @Override |
111 | public ApplicationId registerApplication(String name) { | 110 | public ApplicationId registerApplication(String name) { |
112 | - return Versioned.valueOrNull(registeredIds.computeIfAbsent(name, | 111 | + ApplicationId exisitingAppId = registeredIds.asJavaMap().get(name); |
113 | - key -> new DefaultApplicationId((int) appIdCounter.incrementAndGet(), name))); | 112 | + if (exisitingAppId == null) { |
113 | + ApplicationId newAppId = new DefaultApplicationId((int) appIdCounter.incrementAndGet(), name); | ||
114 | + exisitingAppId = registeredIds.asJavaMap().putIfAbsent(name, newAppId); | ||
115 | + return exisitingAppId == null ? newAppId : exisitingAppId; | ||
116 | + } else { | ||
117 | + return exisitingAppId; | ||
118 | + } | ||
114 | } | 119 | } |
115 | 120 | ||
116 | private void primeIdToAppIdCache() { | 121 | private void primeIdToAppIdCache() { | ... | ... |
... | @@ -54,7 +54,6 @@ import org.onosproject.net.DefaultAnnotations; | ... | @@ -54,7 +54,6 @@ import org.onosproject.net.DefaultAnnotations; |
54 | import org.onosproject.net.SparseAnnotations; | 54 | import org.onosproject.net.SparseAnnotations; |
55 | import org.onosproject.net.provider.ProviderId; | 55 | import org.onosproject.net.provider.ProviderId; |
56 | import org.onosproject.store.AbstractStore; | 56 | import org.onosproject.store.AbstractStore; |
57 | -import org.onosproject.store.app.GossipApplicationStore.InternalState; | ||
58 | import org.onosproject.store.cluster.messaging.ClusterCommunicationService; | 57 | import org.onosproject.store.cluster.messaging.ClusterCommunicationService; |
59 | import org.onosproject.store.serializers.KryoNamespaces; | 58 | import org.onosproject.store.serializers.KryoNamespaces; |
60 | import org.onosproject.store.service.EventuallyConsistentMap; | 59 | import org.onosproject.store.service.EventuallyConsistentMap; |
... | @@ -121,8 +120,7 @@ public class DistributedTunnelStore | ... | @@ -121,8 +120,7 @@ public class DistributedTunnelStore |
121 | public void activate() { | 120 | public void activate() { |
122 | KryoNamespace.Builder serializer = KryoNamespace.newBuilder() | 121 | KryoNamespace.Builder serializer = KryoNamespace.newBuilder() |
123 | .register(KryoNamespaces.API) | 122 | .register(KryoNamespaces.API) |
124 | - .register(MultiValuedTimestamp.class) | 123 | + .register(MultiValuedTimestamp.class); |
125 | - .register(InternalState.class); | ||
126 | tunnelIdAsKeyStore = storageService | 124 | tunnelIdAsKeyStore = storageService |
127 | .<TunnelId, Tunnel>eventuallyConsistentMapBuilder() | 125 | .<TunnelId, Tunnel>eventuallyConsistentMapBuilder() |
128 | .withName("all_tunnel").withSerializer(serializer) | 126 | .withName("all_tunnel").withSerializer(serializer) | ... | ... |
-
Please register or login to post a comment