Adding persistence to the gossip application store.
Change-Id: Ib1382f9d1009297dde902f0d3e0daf27596587c5
diff --git a/core/common/src/main/java/org/onosproject/common/app/ApplicationArchive.java b/core/common/src/main/java/org/onosproject/common/app/ApplicationArchive.java
index 9003f0d..d60f74b 100644
--- a/core/common/src/main/java/org/onosproject/common/app/ApplicationArchive.java
+++ b/core/common/src/main/java/org/onosproject/common/app/ApplicationArchive.java
@@ -121,6 +121,17 @@
}
/**
+ * Returns the timestamp in millis since start of epoch, of when the
+ * specified application was last modified or changed state.
+ *
+ * @param appName application name
+ * @return number of millis since start of epoch
+ */
+ public long getUpdateTime(String appName) {
+ return appFile(appName, APP_XML).lastModified();
+ }
+
+ /**
* Loads the application descriptor from the specified application archive
* stream and saves the stream in the appropriate application archive
* directory.
@@ -313,7 +324,7 @@
*/
protected boolean setActive(String appName) {
try {
- return appFile(appName, "active").createNewFile();
+ return appFile(appName, "active").createNewFile() && updateTime(appName);
} catch (IOException e) {
throw new ApplicationException("Unable to mark app as active", e);
}
@@ -326,7 +337,17 @@
* @return true if file was deleted
*/
protected boolean clearActive(String appName) {
- return appFile(appName, "active").delete();
+ return appFile(appName, "active").delete() && updateTime(appName);
+ }
+
+ /**
+ * Updates the time-stamp of the app descriptor file.
+ *
+ * @param appName application name
+ * @return true if the app descriptor was updated
+ */
+ private boolean updateTime(String appName) {
+ return appFile(appName, APP_XML).setLastModified(System.currentTimeMillis());
}
/**
diff --git a/core/store/dist/src/main/java/org/onosproject/store/app/GossipApplicationStore.java b/core/store/dist/src/main/java/org/onosproject/store/app/GossipApplicationStore.java
index 805c9ca..d8ce7d5 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/app/GossipApplicationStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/app/GossipApplicationStore.java
@@ -18,7 +18,6 @@
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.ListenableFuture;
-
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
@@ -31,6 +30,7 @@
import org.onosproject.app.ApplicationException;
import org.onosproject.app.ApplicationState;
import org.onosproject.app.ApplicationStore;
+import org.onosproject.app.ApplicationStoreDelegate;
import org.onosproject.cluster.ClusterService;
import org.onosproject.cluster.ControllerNode;
import org.onosproject.common.app.ApplicationArchive;
@@ -47,6 +47,8 @@
import org.onosproject.store.ecmap.EventuallyConsistentMapEvent;
import org.onosproject.store.ecmap.EventuallyConsistentMapImpl;
import org.onosproject.store.ecmap.EventuallyConsistentMapListener;
+import org.onosproject.store.impl.ClockService;
+import org.onosproject.store.impl.MultiValuedTimestamp;
import org.onosproject.store.impl.WallclockClockManager;
import org.onosproject.store.serializers.KryoNamespaces;
import org.slf4j.Logger;
@@ -59,6 +61,7 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.atomic.AtomicLong;
import static com.google.common.io.ByteStreams.toByteArray;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
@@ -105,10 +108,13 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ApplicationIdStore idStore;
+ private final AtomicLong sequence = new AtomicLong();
+
@Activate
public void activate() {
- KryoNamespace.Builder intentSerializer = KryoNamespace.newBuilder()
+ KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
.register(KryoNamespaces.API)
+ .register(MultiValuedTimestamp.class)
.register(InternalState.class);
executor = Executors.newSingleThreadScheduledExecutor(groupedThreads("onos/app", "store"));
@@ -118,35 +124,48 @@
clusterCommunicator.addSubscriber(APP_BITS_REQUEST, new InternalBitServer(), messageHandlingExecutor);
+ // FIXME: Consider consolidating into a single map.
+
+ ClockService<ApplicationId, Application> appsClockService = (appId, app) ->
+ new MultiValuedTimestamp<>(getUpdateTime(appId.name()),
+ sequence.incrementAndGet());
+
apps = new EventuallyConsistentMapImpl<>("apps", clusterService,
clusterCommunicator,
- intentSerializer,
- new WallclockClockManager<>());
+ serializer,
+ appsClockService);
+
+ ClockService<Application, InternalState> statesClockService = (app, state) ->
+ new MultiValuedTimestamp<>(getUpdateTime(app.id().name()),
+ sequence.incrementAndGet());
states = new EventuallyConsistentMapImpl<>("app-states",
clusterService,
clusterCommunicator,
- intentSerializer,
- new WallclockClockManager<>());
+ serializer,
+ statesClockService);
states.addListener(new InternalAppStatesListener());
permissions = new EventuallyConsistentMapImpl<>("app-permissions",
clusterService,
clusterCommunicator,
- intentSerializer,
+ serializer,
new WallclockClockManager<>());
- // FIXME: figure out load from disk; this will require resolving the dual authority problem
-
- executor.schedule(this::pruneUninstalledApps, LOAD_TIMEOUT_MS, MILLISECONDS);
-
log.info("Started");
}
+ /**
+ * Loads the application inventory from the disk and activates apps if
+ * they are marked to be active.
+ */
private void loadFromDisk() {
for (String name : getApplicationNames()) {
- create(getApplicationDescription(name));
- // load app permissions
+ Application app = create(getApplicationDescription(name));
+ if (app != null && isActive(app.id().name())) {
+ activate(app.id());
+ // load app permissions
+ }
}
}
@@ -162,6 +181,13 @@
}
@Override
+ public void setDelegate(ApplicationStoreDelegate delegate) {
+ super.setDelegate(delegate);
+ loadFromDisk();
+// executor.schedule(this::pruneUninstalledApps, LOAD_TIMEOUT_MS, MILLISECONDS);
+ }
+
+ @Override
public Set<Application> getApplications() {
return ImmutableSet.copyOf(apps.values());
}
@@ -403,5 +429,6 @@
appDesc.origin(), appDesc.permissions(),
appDesc.featuresRepo(), appDesc.features());
}
+
}