blob: 8ef0d5bd3a7e7e9588e4aab93f78db8cc676019b [file] [log] [blame]
Thomas Vachuska90b453f2015-01-30 18:57:14 -08001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.store.app;
17
Yuta HIGUCHI80292052015-02-10 23:11:59 -080018import com.google.common.base.Charsets;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080019import com.google.common.collect.ImmutableSet;
Thomas Vachuska761f0042015-11-11 19:10:17 -080020import com.google.common.collect.Maps;
21import com.google.common.collect.Multimap;
22import com.google.common.collect.Sets;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080023import org.apache.felix.scr.annotations.Activate;
24import org.apache.felix.scr.annotations.Component;
25import org.apache.felix.scr.annotations.Deactivate;
26import org.apache.felix.scr.annotations.Reference;
27import org.apache.felix.scr.annotations.ReferenceCardinality;
28import org.apache.felix.scr.annotations.Service;
29import org.onlab.util.KryoNamespace;
30import org.onosproject.app.ApplicationDescription;
31import org.onosproject.app.ApplicationEvent;
32import org.onosproject.app.ApplicationException;
33import org.onosproject.app.ApplicationState;
34import org.onosproject.app.ApplicationStore;
Thomas Vachuskacf960112015-03-06 22:36:51 -080035import org.onosproject.app.ApplicationStoreDelegate;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080036import org.onosproject.cluster.ClusterService;
37import org.onosproject.cluster.ControllerNode;
38import org.onosproject.common.app.ApplicationArchive;
39import org.onosproject.core.Application;
40import org.onosproject.core.ApplicationId;
41import org.onosproject.core.ApplicationIdStore;
Thomas Vachuska761f0042015-11-11 19:10:17 -080042import org.onosproject.core.CoreService;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080043import org.onosproject.core.DefaultApplication;
Changhoon Yoonb856b812015-08-10 03:47:19 +090044import org.onosproject.security.Permission;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080045import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080046import org.onosproject.store.cluster.messaging.MessageSubject;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080047import org.onosproject.store.serializers.KryoNamespaces;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070048import org.onosproject.store.service.EventuallyConsistentMap;
49import org.onosproject.store.service.EventuallyConsistentMapEvent;
50import org.onosproject.store.service.EventuallyConsistentMapListener;
Madan Jampani3e033bd2015-04-08 13:03:49 -070051import org.onosproject.store.service.LogicalClockService;
Thomas Vachuskad0d58542015-06-03 12:38:44 -070052import org.onosproject.store.service.MultiValuedTimestamp;
Madan Jampani01e05fb2015-08-13 13:29:36 -070053import org.onosproject.store.service.StorageException;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070054import org.onosproject.store.service.StorageService;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080055import org.slf4j.Logger;
56
57import java.io.ByteArrayInputStream;
Madan Jampani01e05fb2015-08-13 13:29:36 -070058import java.io.IOException;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080059import java.io.InputStream;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080060import java.util.Set;
61import java.util.concurrent.CountDownLatch;
Madan Jampani2af244a2015-02-22 13:12:01 -080062import java.util.concurrent.ExecutorService;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080063import java.util.concurrent.Executors;
64import java.util.concurrent.ScheduledExecutorService;
Madan Jampani2bfa94c2015-04-11 05:03:49 -070065import java.util.function.Function;
66
Thomas Vachuska761f0042015-11-11 19:10:17 -080067import static com.google.common.collect.Multimaps.newSetMultimap;
68import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080069import static com.google.common.io.ByteStreams.toByteArray;
70import static java.util.concurrent.TimeUnit.MILLISECONDS;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080071import static org.onlab.util.Tools.groupedThreads;
Thomas Vachuskaadba1522015-06-04 15:08:30 -070072import static org.onlab.util.Tools.randomDelay;
Thomas Vachuskad0d58542015-06-03 12:38:44 -070073import static org.onosproject.app.ApplicationEvent.Type.*;
74import static org.onosproject.store.app.GossipApplicationStore.InternalState.*;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070075import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.PUT;
76import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.REMOVE;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080077import static org.slf4j.LoggerFactory.getLogger;
78
79/**
80 * Manages inventory of applications in a distributed data store that uses
81 * optimistic replication and gossip based anti-entropy techniques.
82 */
83@Component(immediate = true)
84@Service
85public class GossipApplicationStore extends ApplicationArchive
86 implements ApplicationStore {
87
88 private final Logger log = getLogger(getClass());
89
90 private static final MessageSubject APP_BITS_REQUEST = new MessageSubject("app-bits-request");
91
Thomas Vachuskaadba1522015-06-04 15:08:30 -070092 private static final int MAX_LOAD_RETRIES = 5;
Thomas Vachuskad0d58542015-06-03 12:38:44 -070093 private static final int RETRY_DELAY_MS = 2_000;
94
Thomas Vachuska90b453f2015-01-30 18:57:14 -080095 private static final int FETCH_TIMEOUT_MS = 10_000;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080096
97 public enum InternalState {
98 INSTALLED, ACTIVATED, DEACTIVATED
99 }
100
Madan Jampani6b5b7172015-02-23 13:02:26 -0800101 private ScheduledExecutorService executor;
Madan Jampani2af244a2015-02-22 13:12:01 -0800102 private ExecutorService messageHandlingExecutor;
103
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800104 private EventuallyConsistentMap<ApplicationId, Application> apps;
105 private EventuallyConsistentMap<Application, InternalState> states;
106 private EventuallyConsistentMap<Application, Set<Permission>> permissions;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected ClusterCommunicationService clusterCommunicator;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected ClusterService clusterService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700115 protected StorageService storageService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Madan Jampani3e033bd2015-04-08 13:03:49 -0700118 protected LogicalClockService clockService;
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800119
Madan Jampani3e033bd2015-04-08 13:03:49 -0700120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected ApplicationIdStore idStore;
Thomas Vachuskacf960112015-03-06 22:36:51 -0800122
Thomas Vachuska761f0042015-11-11 19:10:17 -0800123 // Multimap to track which apps are required by others apps
124 // app -> { required-by, ... }
125 // Apps explicitly activated will be required by the CORE app
126 private final Multimap<ApplicationId, ApplicationId> requiredBy =
127 synchronizedSetMultimap(newSetMultimap(Maps.newHashMap(), Sets::newHashSet));
128
129 private ApplicationId coreAppId;
130
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800131 @Activate
132 public void activate() {
Thomas Vachuskacf960112015-03-06 22:36:51 -0800133 KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800134 .register(KryoNamespaces.API)
Thomas Vachuskacf960112015-03-06 22:36:51 -0800135 .register(MultiValuedTimestamp.class)
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800136 .register(InternalState.class);
137
Madan Jampani6b5b7172015-02-23 13:02:26 -0800138 executor = Executors.newSingleThreadScheduledExecutor(groupedThreads("onos/app", "store"));
139
Madan Jampani2af244a2015-02-22 13:12:01 -0800140 messageHandlingExecutor = Executors.newSingleThreadExecutor(
141 groupedThreads("onos/store/app", "message-handler"));
142
Madan Jampani01e05fb2015-08-13 13:29:36 -0700143 clusterCommunicator.<String, byte[]>addSubscriber(APP_BITS_REQUEST,
Thomas Vachuska761f0042015-11-11 19:10:17 -0800144 bytes -> new String(bytes, Charsets.UTF_8),
145 name -> {
146 try {
147 return toByteArray(getApplicationInputStream(name));
148 } catch (IOException e) {
149 throw new StorageException(e);
150 }
151 },
152 Function.identity(),
153 messageHandlingExecutor);
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800154
Thomas Vachuskacf960112015-03-06 22:36:51 -0800155 // FIXME: Consider consolidating into a single map.
156
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700157 apps = storageService.<ApplicationId, Application>eventuallyConsistentMapBuilder()
158 .withName("apps")
159 .withSerializer(serializer)
Madan Jampanibcf1a482015-06-24 19:05:56 -0700160 .withTimestampProvider((k, v) -> clockService.getTimestamp())
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700161 .build();
Thomas Vachuskacf960112015-03-06 22:36:51 -0800162
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700163 states = storageService.<Application, InternalState>eventuallyConsistentMapBuilder()
164 .withName("app-states")
165 .withSerializer(serializer)
Madan Jampanibcf1a482015-06-24 19:05:56 -0700166 .withTimestampProvider((k, v) -> clockService.getTimestamp())
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700167 .build();
168
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800169 states.addListener(new InternalAppStatesListener());
170
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700171 permissions = storageService.<Application, Set<Permission>>eventuallyConsistentMapBuilder()
172 .withName("app-permissions")
173 .withSerializer(serializer)
Madan Jampanibcf1a482015-06-24 19:05:56 -0700174 .withTimestampProvider((k, v) -> clockService.getTimestamp())
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700175 .build();
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800176
Thomas Vachuska761f0042015-11-11 19:10:17 -0800177 coreAppId = getId(CoreService.CORE_APP_NAME);
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800178 log.info("Started");
179 }
180
Thomas Vachuskacf960112015-03-06 22:36:51 -0800181 /**
182 * Loads the application inventory from the disk and activates apps if
183 * they are marked to be active.
184 */
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800185 private void loadFromDisk() {
Charles Chane889e2d2015-11-17 12:01:02 -0800186 getApplicationNames().forEach(appName -> {
187 Application app = loadFromDisk(appName);
188 if (app != null && isActive(app.id().name())) {
189 activate(app.id(), false);
190 // TODO Load app permissions
191 }
192 });
193 }
194
195 private Application loadFromDisk(String appName) {
196 for (int i = 0; i < MAX_LOAD_RETRIES; i++) {
197 try {
198 // Directly return if app already exists
199 ApplicationId appId = getId(appName);
200 if (appId != null) {
Ray Milkey64591a62016-01-12 09:50:45 -0800201 Application application = getApplication(appId);
202 if (application != null) {
203 return application;
204 }
Thomas Vachuska62f04a42015-04-22 14:38:34 -0700205 }
Charles Chane889e2d2015-11-17 12:01:02 -0800206
207 ApplicationDescription appDesc = getApplicationDescription(appName);
208 boolean success = appDesc.requiredApps().stream()
209 .noneMatch(requiredApp -> loadFromDisk(requiredApp) == null);
210 return success ? create(appDesc, false) : null;
211 } catch (Exception e) {
212 log.warn("Unable to load application {} from disk; retrying", appName);
213 randomDelay(RETRY_DELAY_MS); //FIXME: This is a deliberate hack; fix in Falcon
Thomas Vachuskacf960112015-03-06 22:36:51 -0800214 }
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800215 }
Charles Chane889e2d2015-11-17 12:01:02 -0800216 return null;
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800217 }
218
219 @Deactivate
220 public void deactivate() {
Madan Jampani2af244a2015-02-22 13:12:01 -0800221 clusterCommunicator.removeSubscriber(APP_BITS_REQUEST);
222 messageHandlingExecutor.shutdown();
Madan Jampani6b5b7172015-02-23 13:02:26 -0800223 executor.shutdown();
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800224 apps.destroy();
225 states.destroy();
226 permissions.destroy();
227 log.info("Stopped");
228 }
229
230 @Override
Thomas Vachuskacf960112015-03-06 22:36:51 -0800231 public void setDelegate(ApplicationStoreDelegate delegate) {
232 super.setDelegate(delegate);
233 loadFromDisk();
Thomas Vachuskacf960112015-03-06 22:36:51 -0800234 }
235
236 @Override
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800237 public Set<Application> getApplications() {
238 return ImmutableSet.copyOf(apps.values());
239 }
240
241 @Override
242 public ApplicationId getId(String name) {
243 return idStore.getAppId(name);
244 }
245
246 @Override
247 public Application getApplication(ApplicationId appId) {
248 return apps.get(appId);
249 }
250
251 @Override
252 public ApplicationState getState(ApplicationId appId) {
253 Application app = apps.get(appId);
254 InternalState s = app == null ? null : states.get(app);
255 return s == null ? null : s == ACTIVATED ?
256 ApplicationState.ACTIVE : ApplicationState.INSTALLED;
257 }
258
259 @Override
260 public Application create(InputStream appDescStream) {
261 ApplicationDescription appDesc = saveApplication(appDescStream);
Thomas Vachuska761f0042015-11-11 19:10:17 -0800262 if (hasPrerequisites(appDesc)) {
263 return create(appDesc, true);
264 }
265 throw new ApplicationException("Missing dependencies for app " + appDesc.name());
266 }
267
268 private boolean hasPrerequisites(ApplicationDescription app) {
269 return !app.requiredApps().stream().map(n -> getId(n))
270 .anyMatch(id -> id == null || getApplication(id) == null);
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800271 }
272
Thomas Vachuska161baf52015-03-27 16:15:39 -0700273 private Application create(ApplicationDescription appDesc, boolean updateTime) {
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800274 Application app = registerApp(appDesc);
Thomas Vachuska161baf52015-03-27 16:15:39 -0700275 if (updateTime) {
276 updateTime(app.id().name());
277 }
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800278 apps.put(app.id(), app);
279 states.put(app, INSTALLED);
280 return app;
281 }
282
283 @Override
284 public void remove(ApplicationId appId) {
285 Application app = apps.get(appId);
286 if (app != null) {
Thomas Vachuska761f0042015-11-11 19:10:17 -0800287 uninstallDependentApps(app);
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800288 apps.remove(appId);
289 states.remove(app);
290 permissions.remove(app);
291 }
292 }
293
Thomas Vachuska761f0042015-11-11 19:10:17 -0800294 // Uninstalls all apps that depend on the given app.
295 private void uninstallDependentApps(Application app) {
296 getApplications().stream()
297 .filter(a -> a.requiredApps().contains(app.id().name()))
298 .forEach(a -> remove(a.id()));
299 }
300
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800301 @Override
302 public void activate(ApplicationId appId) {
Thomas Vachuska761f0042015-11-11 19:10:17 -0800303 activate(appId, coreAppId);
304 }
305
306 private void activate(ApplicationId appId, ApplicationId forAppId) {
307 requiredBy.put(appId, forAppId);
Thomas Vachuska161baf52015-03-27 16:15:39 -0700308 activate(appId, true);
309 }
310
Thomas Vachuska761f0042015-11-11 19:10:17 -0800311
Thomas Vachuska161baf52015-03-27 16:15:39 -0700312 private void activate(ApplicationId appId, boolean updateTime) {
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800313 Application app = apps.get(appId);
314 if (app != null) {
Thomas Vachuska161baf52015-03-27 16:15:39 -0700315 if (updateTime) {
316 updateTime(appId.name());
317 }
Thomas Vachuska761f0042015-11-11 19:10:17 -0800318 activateRequiredApps(app);
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800319 states.put(app, ACTIVATED);
320 }
321 }
322
Thomas Vachuska761f0042015-11-11 19:10:17 -0800323 // Activates all apps required by this application.
324 private void activateRequiredApps(Application app) {
325 app.requiredApps().stream().map(this::getId).forEach(id -> activate(id, app.id()));
326 }
327
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800328 @Override
329 public void deactivate(ApplicationId appId) {
Thomas Vachuska761f0042015-11-11 19:10:17 -0800330 deactivateDependentApps(getApplication(appId));
331 deactivate(appId, coreAppId);
332 }
333
334 private void deactivate(ApplicationId appId, ApplicationId forAppId) {
335 requiredBy.remove(appId, forAppId);
336 if (requiredBy.get(appId).isEmpty()) {
337 Application app = apps.get(appId);
338 if (app != null) {
339 updateTime(appId.name());
340 states.put(app, DEACTIVATED);
341 deactivateRequiredApps(app);
342 }
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800343 }
344 }
345
Thomas Vachuska761f0042015-11-11 19:10:17 -0800346 // Deactivates all apps that require this application.
347 private void deactivateDependentApps(Application app) {
348 getApplications().stream()
349 .filter(a -> states.get(a) == ACTIVATED)
350 .filter(a -> a.requiredApps().contains(app.id().name()))
351 .forEach(a -> deactivate(a.id()));
352 }
353
354 // Deactivates all apps required by this application.
355 private void deactivateRequiredApps(Application app) {
356 app.requiredApps().stream().map(this::getId).map(this::getApplication)
357 .filter(a -> states.get(a) == ACTIVATED)
358 .forEach(a -> deactivate(a.id(), app.id()));
359 }
360
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800361 @Override
362 public Set<Permission> getPermissions(ApplicationId appId) {
363 Application app = apps.get(appId);
364 return app != null ? permissions.get(app) : null;
365 }
366
367 @Override
368 public void setPermissions(ApplicationId appId, Set<Permission> permissions) {
369 Application app = getApplication(appId);
370 if (app != null) {
371 this.permissions.put(app, permissions);
372 delegate.notify(new ApplicationEvent(APP_PERMISSIONS_CHANGED, app));
373 }
374 }
375
376 /**
377 * Listener to application state distributed map changes.
378 */
379 private final class InternalAppStatesListener
380 implements EventuallyConsistentMapListener<Application, InternalState> {
381 @Override
382 public void event(EventuallyConsistentMapEvent<Application, InternalState> event) {
Thomas Vachuska4fcdae72015-03-24 10:22:54 -0700383 // If we do not have a delegate, refuse to process any events entirely.
384 // This is to allow the anti-entropy to kick in and process the events
385 // perhaps a bit later, but with opportunity to notify delegate.
386 if (delegate == null) {
387 return;
388 }
389
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800390 Application app = event.key();
391 InternalState state = event.value();
392
393 if (event.type() == PUT) {
394 if (state == INSTALLED) {
395 fetchBitsIfNeeded(app);
396 delegate.notify(new ApplicationEvent(APP_INSTALLED, app));
397
398 } else if (state == ACTIVATED) {
399 installAppIfNeeded(app);
400 setActive(app.id().name());
401 delegate.notify(new ApplicationEvent(APP_ACTIVATED, app));
402
403 } else if (state == DEACTIVATED) {
404 clearActive(app.id().name());
405 delegate.notify(new ApplicationEvent(APP_DEACTIVATED, app));
406 }
407 } else if (event.type() == REMOVE) {
408 delegate.notify(new ApplicationEvent(APP_UNINSTALLED, app));
409 purgeApplication(app.id().name());
410 }
411 }
412 }
413
414 /**
415 * Determines if the application bits are available locally.
416 */
417 private boolean appBitsAvailable(Application app) {
418 try {
419 ApplicationDescription appDesc = getApplicationDescription(app.id().name());
420 return appDesc.version().equals(app.version());
421 } catch (ApplicationException e) {
422 return false;
423 }
424 }
425
426 /**
427 * Fetches the bits from the cluster peers if necessary.
428 */
429 private void fetchBitsIfNeeded(Application app) {
430 if (!appBitsAvailable(app)) {
431 fetchBits(app);
432 }
433 }
434
435 /**
436 * Installs the application if necessary from the application peers.
437 */
438 private void installAppIfNeeded(Application app) {
439 if (!appBitsAvailable(app)) {
440 fetchBits(app);
441 delegate.notify(new ApplicationEvent(APP_INSTALLED, app));
442 }
443 }
444
445 /**
446 * Fetches the bits from the cluster peers.
447 */
448 private void fetchBits(Application app) {
449 ControllerNode localNode = clusterService.getLocalNode();
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800450 CountDownLatch latch = new CountDownLatch(1);
451
452 // FIXME: send message with name & version to make sure we don't get served old bits
453
454 log.info("Downloading bits for application {}", app.id().name());
455 for (ControllerNode node : clusterService.getNodes()) {
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700456 if (latch.getCount() == 0) {
457 break;
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800458 }
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700459 if (node.equals(localNode)) {
460 continue;
461 }
462 clusterCommunicator.sendAndReceive(app.id().name(),
Thomas Vachuskad0d58542015-06-03 12:38:44 -0700463 APP_BITS_REQUEST,
464 s -> s.getBytes(Charsets.UTF_8),
465 Function.identity(),
466 node.id())
467 .whenCompleteAsync((bits, error) -> {
468 if (error == null && latch.getCount() > 0) {
469 saveApplication(new ByteArrayInputStream(bits));
470 log.info("Downloaded bits for application {} from node {}",
471 app.id().name(), node.id());
472 latch.countDown();
473 } else if (error != null) {
474 log.warn("Unable to fetch bits for application {} from node {}",
475 app.id().name(), node.id());
476 }
477 }, executor);
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800478 }
479
480 try {
481 if (!latch.await(FETCH_TIMEOUT_MS, MILLISECONDS)) {
482 log.warn("Unable to fetch bits for application {}", app.id().name());
483 }
484 } catch (InterruptedException e) {
485 log.warn("Interrupted while fetching bits for application {}", app.id().name());
486 }
487 }
488
489 /**
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800490 * Prunes applications which are not in the map, but are on disk.
491 */
492 private void pruneUninstalledApps() {
493 for (String name : getApplicationNames()) {
494 if (getApplication(getId(name)) == null) {
495 Application app = registerApp(getApplicationDescription(name));
496 delegate.notify(new ApplicationEvent(APP_UNINSTALLED, app));
497 purgeApplication(app.id().name());
498 }
499 }
500 }
501
502 /**
503 * Produces a registered application from the supplied description.
504 */
505 private Application registerApp(ApplicationDescription appDesc) {
506 ApplicationId appId = idStore.registerApplication(appDesc.name());
507 return new DefaultApplication(appId, appDesc.version(), appDesc.description(),
Changhoon Yoonbdeb88a2015-05-12 20:35:31 +0900508 appDesc.origin(), appDesc.role(), appDesc.permissions(),
Thomas Vachuska761f0042015-11-11 19:10:17 -0800509 appDesc.featuresRepo(), appDesc.features(),
510 appDesc.requiredApps());
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800511 }
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800512}