Committed by
Gerrit Code Review
Adding persistence to the gossip application store.
Change-Id: Ib1382f9d1009297dde902f0d3e0daf27596587c5
Showing
2 changed files
with
62 additions
and
14 deletions
... | @@ -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,37 +124,50 @@ public class GossipApplicationStore extends ApplicationArchive | ... | @@ -118,37 +124,50 @@ 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)); |
165 | + if (app != null && isActive(app.id().name())) { | ||
166 | + activate(app.id()); | ||
149 | // load app permissions | 167 | // load app permissions |
150 | } | 168 | } |
151 | } | 169 | } |
170 | + } | ||
152 | 171 | ||
153 | @Deactivate | 172 | @Deactivate |
154 | public void deactivate() { | 173 | public void deactivate() { |
... | @@ -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 | ... | ... |
-
Please register or login to post a comment