Committed by
Gerrit Code Review
Adding persistence to the gossip application store.
Change-Id: Ib1382f9d1009297dde902f0d3e0daf27596587c5
Showing
2 changed files
with
63 additions
and
15 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,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 | ... | ... |
-
Please register or login to post a comment