Madan Jampani
Committed by Gerrit Code Review

Ensure ApplicationService::uninstall returns only after the features have been uninstalled

Change-Id: Iff9c1c0196bc8616858589c67ce4c876e99a8120
...@@ -15,8 +15,12 @@ ...@@ -15,8 +15,12 @@
15 */ 15 */
16 package org.onosproject.app.impl; 16 package org.onosproject.app.impl;
17 17
18 +import com.google.common.cache.Cache;
19 +import com.google.common.cache.CacheBuilder;
18 import com.google.common.collect.HashMultimap; 20 import com.google.common.collect.HashMultimap;
19 import com.google.common.collect.Multimap; 21 import com.google.common.collect.Multimap;
22 +import com.google.common.util.concurrent.Uninterruptibles;
23 +
20 import org.apache.felix.scr.annotations.Activate; 24 import org.apache.felix.scr.annotations.Activate;
21 import org.apache.felix.scr.annotations.Component; 25 import org.apache.felix.scr.annotations.Component;
22 import org.apache.felix.scr.annotations.Deactivate; 26 import org.apache.felix.scr.annotations.Deactivate;
...@@ -41,6 +45,8 @@ import org.slf4j.Logger; ...@@ -41,6 +45,8 @@ import org.slf4j.Logger;
41 45
42 import java.io.InputStream; 46 import java.io.InputStream;
43 import java.util.Set; 47 import java.util.Set;
48 +import java.util.concurrent.CountDownLatch;
49 +import java.util.concurrent.TimeUnit;
44 50
45 import static com.google.common.base.Preconditions.checkNotNull; 51 import static com.google.common.base.Preconditions.checkNotNull;
46 import static org.onosproject.app.ApplicationEvent.Type.APP_ACTIVATED; 52 import static org.onosproject.app.ApplicationEvent.Type.APP_ACTIVATED;
...@@ -63,6 +69,7 @@ public class ApplicationManager ...@@ -63,6 +69,7 @@ public class ApplicationManager
63 private final Logger log = getLogger(getClass()); 69 private final Logger log = getLogger(getClass());
64 70
65 private static final String APP_ID_NULL = "Application ID cannot be null"; 71 private static final String APP_ID_NULL = "Application ID cannot be null";
72 + private static final long DEFAULT_OPERATION_TIMEOUT_MILLIS = 2000;
66 73
67 private final ApplicationStoreDelegate delegate = new InternalStoreDelegate(); 74 private final ApplicationStoreDelegate delegate = new InternalStoreDelegate();
68 75
...@@ -76,6 +83,10 @@ public class ApplicationManager ...@@ -76,6 +83,10 @@ public class ApplicationManager
76 83
77 // Application supplied hooks for pre-activation processing. 84 // Application supplied hooks for pre-activation processing.
78 private final Multimap<String, Runnable> deactivateHooks = HashMultimap.create(); 85 private final Multimap<String, Runnable> deactivateHooks = HashMultimap.create();
86 + private final Cache<ApplicationId, CountDownLatch> pendingUninstalls =
87 + CacheBuilder.newBuilder()
88 + .expireAfterWrite(DEFAULT_OPERATION_TIMEOUT_MILLIS * 2, TimeUnit.MILLISECONDS)
89 + .build();
79 90
80 @Activate 91 @Activate
81 public void activate() { 92 public void activate() {
...@@ -148,11 +159,16 @@ public class ApplicationManager ...@@ -148,11 +159,16 @@ public class ApplicationManager
148 @Override 159 @Override
149 public void uninstall(ApplicationId appId) { 160 public void uninstall(ApplicationId appId) {
150 checkNotNull(appId, APP_ID_NULL); 161 checkNotNull(appId, APP_ID_NULL);
162 + CountDownLatch latch = new CountDownLatch(1);
151 try { 163 try {
164 + pendingUninstalls.put(appId, latch);
152 store.remove(appId); 165 store.remove(appId);
153 } catch (Exception e) { 166 } catch (Exception e) {
167 + pendingUninstalls.invalidate(appId);
168 + latch.countDown();
154 log.warn("Unable to purge application directory for {}", appId.name()); 169 log.warn("Unable to purge application directory for {}", appId.name());
155 } 170 }
171 + Uninterruptibles.awaitUninterruptibly(latch, DEFAULT_OPERATION_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
156 } 172 }
157 173
158 @Override 174 @Override
...@@ -182,6 +198,7 @@ public class ApplicationManager ...@@ -182,6 +198,7 @@ public class ApplicationManager
182 public void notify(ApplicationEvent event) { 198 public void notify(ApplicationEvent event) {
183 ApplicationEvent.Type type = event.type(); 199 ApplicationEvent.Type type = event.type();
184 Application app = event.subject(); 200 Application app = event.subject();
201 + CountDownLatch latch = pendingUninstalls.getIfPresent(app.id());
185 try { 202 try {
186 if (type == APP_ACTIVATED) { 203 if (type == APP_ACTIVATED) {
187 if (installAppFeatures(app)) { 204 if (installAppFeatures(app)) {
...@@ -205,9 +222,13 @@ public class ApplicationManager ...@@ -205,9 +222,13 @@ public class ApplicationManager
205 222
206 } 223 }
207 post(event); 224 post(event);
208 -
209 } catch (Exception e) { 225 } catch (Exception e) {
210 log.warn("Unable to perform operation on application " + app.id().name(), e); 226 log.warn("Unable to perform operation on application " + app.id().name(), e);
227 + } finally {
228 + if (latch != null) {
229 + latch.countDown();
230 + pendingUninstalls.invalidate(app.id());
231 + }
211 } 232 }
212 } 233 }
213 } 234 }
......