Madan Jampani
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)
...@@ -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 }
......
...@@ -15,6 +15,6 @@ ...@@ -15,6 +15,6 @@
15 */ 15 */
16 16
17 /** 17 /**
18 - * Implementation of distributed applications store. 18 + * Implementation of distributed application store.
19 */ 19 */
20 package org.onosproject.store.app; 20 package org.onosproject.store.app;
......
...@@ -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)
......