Thomas Vachuska
Committed by Gerrit Code Review

Adding persistence to the gossip application store.

Change-Id: Ib1382f9d1009297dde902f0d3e0daf27596587c5
...@@ -121,6 +121,17 @@ public class ApplicationArchive ...@@ -121,6 +121,17 @@ public class ApplicationArchive
121 } 121 }
122 122
123 /** 123 /**
124 + * Returns the timestamp in millis since start of epoch, of when the
125 + * specified application was last modified or changed state.
126 + *
127 + * @param appName application name
128 + * @return number of millis since start of epoch
129 + */
130 + public long getUpdateTime(String appName) {
131 + return appFile(appName, APP_XML).lastModified();
132 + }
133 +
134 + /**
124 * Loads the application descriptor from the specified application archive 135 * Loads the application descriptor from the specified application archive
125 * stream and saves the stream in the appropriate application archive 136 * stream and saves the stream in the appropriate application archive
126 * directory. 137 * directory.
...@@ -313,7 +324,7 @@ public class ApplicationArchive ...@@ -313,7 +324,7 @@ public class ApplicationArchive
313 */ 324 */
314 protected boolean setActive(String appName) { 325 protected boolean setActive(String appName) {
315 try { 326 try {
316 - return appFile(appName, "active").createNewFile(); 327 + return appFile(appName, "active").createNewFile() && updateTime(appName);
317 } catch (IOException e) { 328 } catch (IOException e) {
318 throw new ApplicationException("Unable to mark app as active", e); 329 throw new ApplicationException("Unable to mark app as active", e);
319 } 330 }
...@@ -326,7 +337,17 @@ public class ApplicationArchive ...@@ -326,7 +337,17 @@ public class ApplicationArchive
326 * @return true if file was deleted 337 * @return true if file was deleted
327 */ 338 */
328 protected boolean clearActive(String appName) { 339 protected boolean clearActive(String appName) {
329 - return appFile(appName, "active").delete(); 340 + return appFile(appName, "active").delete() && updateTime(appName);
341 + }
342 +
343 + /**
344 + * Updates the time-stamp of the app descriptor file.
345 + *
346 + * @param appName application name
347 + * @return true if the app descriptor was updated
348 + */
349 + private boolean updateTime(String appName) {
350 + return appFile(appName, APP_XML).setLastModified(System.currentTimeMillis());
330 } 351 }
331 352
332 /** 353 /**
......
...@@ -18,7 +18,6 @@ package org.onosproject.store.app; ...@@ -18,7 +18,6 @@ package org.onosproject.store.app;
18 import com.google.common.base.Charsets; 18 import com.google.common.base.Charsets;
19 import com.google.common.collect.ImmutableSet; 19 import com.google.common.collect.ImmutableSet;
20 import com.google.common.util.concurrent.ListenableFuture; 20 import com.google.common.util.concurrent.ListenableFuture;
21 -
22 import org.apache.felix.scr.annotations.Activate; 21 import org.apache.felix.scr.annotations.Activate;
23 import org.apache.felix.scr.annotations.Component; 22 import org.apache.felix.scr.annotations.Component;
24 import org.apache.felix.scr.annotations.Deactivate; 23 import org.apache.felix.scr.annotations.Deactivate;
...@@ -31,6 +30,7 @@ import org.onosproject.app.ApplicationEvent; ...@@ -31,6 +30,7 @@ import org.onosproject.app.ApplicationEvent;
31 import org.onosproject.app.ApplicationException; 30 import org.onosproject.app.ApplicationException;
32 import org.onosproject.app.ApplicationState; 31 import org.onosproject.app.ApplicationState;
33 import org.onosproject.app.ApplicationStore; 32 import org.onosproject.app.ApplicationStore;
33 +import org.onosproject.app.ApplicationStoreDelegate;
34 import org.onosproject.cluster.ClusterService; 34 import org.onosproject.cluster.ClusterService;
35 import org.onosproject.cluster.ControllerNode; 35 import org.onosproject.cluster.ControllerNode;
36 import org.onosproject.common.app.ApplicationArchive; 36 import org.onosproject.common.app.ApplicationArchive;
...@@ -47,6 +47,8 @@ import org.onosproject.store.ecmap.EventuallyConsistentMap; ...@@ -47,6 +47,8 @@ import org.onosproject.store.ecmap.EventuallyConsistentMap;
47 import org.onosproject.store.ecmap.EventuallyConsistentMapEvent; 47 import org.onosproject.store.ecmap.EventuallyConsistentMapEvent;
48 import org.onosproject.store.ecmap.EventuallyConsistentMapImpl; 48 import org.onosproject.store.ecmap.EventuallyConsistentMapImpl;
49 import org.onosproject.store.ecmap.EventuallyConsistentMapListener; 49 import org.onosproject.store.ecmap.EventuallyConsistentMapListener;
50 +import org.onosproject.store.impl.ClockService;
51 +import org.onosproject.store.impl.MultiValuedTimestamp;
50 import org.onosproject.store.impl.WallclockClockManager; 52 import org.onosproject.store.impl.WallclockClockManager;
51 import org.onosproject.store.serializers.KryoNamespaces; 53 import org.onosproject.store.serializers.KryoNamespaces;
52 import org.slf4j.Logger; 54 import org.slf4j.Logger;
...@@ -59,6 +61,7 @@ import java.util.concurrent.CountDownLatch; ...@@ -59,6 +61,7 @@ import java.util.concurrent.CountDownLatch;
59 import java.util.concurrent.ExecutorService; 61 import java.util.concurrent.ExecutorService;
60 import java.util.concurrent.Executors; 62 import java.util.concurrent.Executors;
61 import java.util.concurrent.ScheduledExecutorService; 63 import java.util.concurrent.ScheduledExecutorService;
64 +import java.util.concurrent.atomic.AtomicLong;
62 65
63 import static com.google.common.io.ByteStreams.toByteArray; 66 import static com.google.common.io.ByteStreams.toByteArray;
64 import static java.util.concurrent.TimeUnit.MILLISECONDS; 67 import static java.util.concurrent.TimeUnit.MILLISECONDS;
...@@ -105,10 +108,13 @@ public class GossipApplicationStore extends ApplicationArchive ...@@ -105,10 +108,13 @@ public class GossipApplicationStore extends ApplicationArchive
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected ApplicationIdStore idStore; 109 protected ApplicationIdStore idStore;
107 110
111 + private final AtomicLong sequence = new AtomicLong();
112 +
108 @Activate 113 @Activate
109 public void activate() { 114 public void activate() {
110 - KryoNamespace.Builder intentSerializer = KryoNamespace.newBuilder() 115 + KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
111 .register(KryoNamespaces.API) 116 .register(KryoNamespaces.API)
117 + .register(MultiValuedTimestamp.class)
112 .register(InternalState.class); 118 .register(InternalState.class);
113 119
114 executor = Executors.newSingleThreadScheduledExecutor(groupedThreads("onos/app", "store")); 120 executor = Executors.newSingleThreadScheduledExecutor(groupedThreads("onos/app", "store"));
...@@ -118,35 +124,48 @@ public class GossipApplicationStore extends ApplicationArchive ...@@ -118,35 +124,48 @@ public class GossipApplicationStore extends ApplicationArchive
118 124
119 clusterCommunicator.addSubscriber(APP_BITS_REQUEST, new InternalBitServer(), messageHandlingExecutor); 125 clusterCommunicator.addSubscriber(APP_BITS_REQUEST, new InternalBitServer(), messageHandlingExecutor);
120 126
127 + // FIXME: Consider consolidating into a single map.
128 +
129 + ClockService<ApplicationId, Application> appsClockService = (appId, app) ->
130 + new MultiValuedTimestamp<>(getUpdateTime(appId.name()),
131 + sequence.incrementAndGet());
132 +
121 apps = new EventuallyConsistentMapImpl<>("apps", clusterService, 133 apps = new EventuallyConsistentMapImpl<>("apps", clusterService,
122 clusterCommunicator, 134 clusterCommunicator,
123 - intentSerializer, 135 + serializer,
124 - new WallclockClockManager<>()); 136 + appsClockService);
137 +
138 + ClockService<Application, InternalState> statesClockService = (app, state) ->
139 + new MultiValuedTimestamp<>(getUpdateTime(app.id().name()),
140 + sequence.incrementAndGet());
125 141
126 states = new EventuallyConsistentMapImpl<>("app-states", 142 states = new EventuallyConsistentMapImpl<>("app-states",
127 clusterService, 143 clusterService,
128 clusterCommunicator, 144 clusterCommunicator,
129 - intentSerializer, 145 + serializer,
130 - new WallclockClockManager<>()); 146 + statesClockService);
131 states.addListener(new InternalAppStatesListener()); 147 states.addListener(new InternalAppStatesListener());
132 148
133 permissions = new EventuallyConsistentMapImpl<>("app-permissions", 149 permissions = new EventuallyConsistentMapImpl<>("app-permissions",
134 clusterService, 150 clusterService,
135 clusterCommunicator, 151 clusterCommunicator,
136 - intentSerializer, 152 + serializer,
137 new WallclockClockManager<>()); 153 new WallclockClockManager<>());
138 154
139 - // FIXME: figure out load from disk; this will require resolving the dual authority problem
140 -
141 - executor.schedule(this::pruneUninstalledApps, LOAD_TIMEOUT_MS, MILLISECONDS);
142 -
143 log.info("Started"); 155 log.info("Started");
144 } 156 }
145 157
158 + /**
159 + * Loads the application inventory from the disk and activates apps if
160 + * they are marked to be active.
161 + */
146 private void loadFromDisk() { 162 private void loadFromDisk() {
147 for (String name : getApplicationNames()) { 163 for (String name : getApplicationNames()) {
148 - create(getApplicationDescription(name)); 164 + Application app = create(getApplicationDescription(name));
149 - // load app permissions 165 + if (app != null && isActive(app.id().name())) {
166 + activate(app.id());
167 + // load app permissions
168 + }
150 } 169 }
151 } 170 }
152 171
...@@ -162,6 +181,13 @@ public class GossipApplicationStore extends ApplicationArchive ...@@ -162,6 +181,13 @@ public class GossipApplicationStore extends ApplicationArchive
162 } 181 }
163 182
164 @Override 183 @Override
184 + public void setDelegate(ApplicationStoreDelegate delegate) {
185 + super.setDelegate(delegate);
186 + loadFromDisk();
187 +// executor.schedule(this::pruneUninstalledApps, LOAD_TIMEOUT_MS, MILLISECONDS);
188 + }
189 +
190 + @Override
165 public Set<Application> getApplications() { 191 public Set<Application> getApplications() {
166 return ImmutableSet.copyOf(apps.values()); 192 return ImmutableSet.copyOf(apps.values());
167 } 193 }
...@@ -403,5 +429,6 @@ public class GossipApplicationStore extends ApplicationArchive ...@@ -403,5 +429,6 @@ public class GossipApplicationStore extends ApplicationArchive
403 appDesc.origin(), appDesc.permissions(), 429 appDesc.origin(), appDesc.permissions(),
404 appDesc.featuresRepo(), appDesc.features()); 430 appDesc.featuresRepo(), appDesc.features());
405 } 431 }
432 +
406 } 433 }
407 434
......