ONOS-5101: Ensure app activation order reflects the dependency order
Change-Id: I77e0579436d80643b8262f0ec5ad6efb57936a0b
diff --git a/core/store/dist/src/main/java/org/onosproject/store/app/DistributedApplicationStore.java b/core/store/dist/src/main/java/org/onosproject/store/app/DistributedApplicationStore.java
index a043814..9f9cb2d 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/app/DistributedApplicationStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/app/DistributedApplicationStore.java
@@ -30,7 +30,6 @@
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
-import org.onlab.util.Tools;
import org.onosproject.app.ApplicationDescription;
import org.onosproject.app.ApplicationEvent;
import org.onosproject.app.ApplicationException;
@@ -49,6 +48,9 @@
import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
import org.onosproject.store.cluster.messaging.MessageSubject;
import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.AtomicValue;
+import org.onosproject.store.service.AtomicValueEvent;
+import org.onosproject.store.service.AtomicValueEventListener;
import org.onosproject.store.service.ConsistentMap;
import org.onosproject.store.service.MapEvent;
import org.onosproject.store.service.MapEventListener;
@@ -119,6 +121,7 @@
private ExecutorService messageHandlingExecutor;
private ConsistentMap<ApplicationId, InternalApplicationHolder> apps;
+ private AtomicValue<Application> nextAppToActivate;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ClusterCommunicationService clusterCommunicator;
@@ -133,6 +136,7 @@
protected ApplicationIdStore idStore;
private final InternalAppsListener appsListener = new InternalAppsListener();
+ private final NextAppToActivateValueListener nextAppToActivateListener = new NextAppToActivateValueListener();
private Consumer<Status> statusChangeListener;
@@ -168,6 +172,14 @@
InternalState.class))
.build();
+ nextAppToActivate = storageService.<Application>atomicValueBuilder()
+ .withName("onos-apps-activation-order-value")
+ .withSerializer(Serializer.using(KryoNamespaces.API))
+ .build()
+ .asAtomicValue();
+
+ nextAppToActivate.addListener(nextAppToActivateListener);
+
executor = newSingleThreadScheduledExecutor(groupedThreads("onos/app", "store", log));
statusChangeListener = status -> {
if (status == Status.ACTIVE) {
@@ -248,6 +260,7 @@
clusterCommunicator.removeSubscriber(APP_BITS_REQUEST);
apps.removeStatusChangeListener(statusChangeListener);
apps.removeListener(appsListener);
+ nextAppToActivate.removeListener(nextAppToActivateListener);
messageHandlingExecutor.shutdown();
executor.shutdown();
log.info("Stopped");
@@ -346,15 +359,10 @@
}
activateRequiredApps(vAppHolder.value().app());
- // FIXME: Take a breath before the post-order operation to allow required app
- // activation events to fully propagate. There appears to be an out-of-order
- // event delivery issue that needs to be fixed.
- Tools.delay(FIXME_ACTIVATION_DELAY);
-
apps.computeIf(appId, v -> v != null && v.state() != ACTIVATED,
(k, v) -> new InternalApplicationHolder(
v.app(), ACTIVATED, v.permissions()));
-
+ nextAppToActivate.set(vAppHolder.value().app());
}
}
@@ -427,6 +435,20 @@
}
}
+ private class NextAppToActivateValueListener implements AtomicValueEventListener<Application> {
+
+ @Override
+ public void event(AtomicValueEvent<Application> event) {
+ messageHandlingExecutor.execute(() -> {
+ Application app = event.newValue();
+ String appName = app.id().name();
+ installAppIfNeeded(app);
+ setActive(appName);
+ delegate.notify(new ApplicationEvent(APP_ACTIVATED, app));
+ });
+ }
+ }
+
/**
* Listener to application state distributed map changes.
*/
@@ -454,13 +476,10 @@
}
private void setupApplicationAndNotify(ApplicationId appId, Application app, InternalState state) {
+ // ACTIVATED state is handled separately in NextAppToActivateValueListener
if (state == INSTALLED) {
fetchBitsIfNeeded(app);
delegate.notify(new ApplicationEvent(APP_INSTALLED, app));
- } else if (state == ACTIVATED) {
- installAppIfNeeded(app);
- setActive(appId.name());
- delegate.notify(new ApplicationEvent(APP_ACTIVATED, app));
} else if (state == DEACTIVATED) {
clearActive(appId.name());
delegate.notify(new ApplicationEvent(APP_DEACTIVATED, app));