ONOS-3321 Added ability for applications to register a deactivation hook.
Change-Id: I284321474dbf9e39e8b61f7b907f637ba6b80aaf
diff --git a/core/api/src/main/java/org/onosproject/app/ApplicationService.java b/core/api/src/main/java/org/onosproject/app/ApplicationService.java
index 73dcc86..1e543b8 100644
--- a/core/api/src/main/java/org/onosproject/app/ApplicationService.java
+++ b/core/api/src/main/java/org/onosproject/app/ApplicationService.java
@@ -67,4 +67,11 @@
*/
Set<Permission> getPermissions(ApplicationId appId);
+ /**
+ * Registers application pre-deactivation processing hook.
+ *
+ * @param appId application identifier
+ * @param hook pre-deactivation hook
+ */
+ void registerDeactivateHook(ApplicationId appId, Runnable hook);
}
diff --git a/core/api/src/main/java/org/onosproject/core/CoreService.java b/core/api/src/main/java/org/onosproject/core/CoreService.java
index 303ad39..0825a6d 100644
--- a/core/api/src/main/java/org/onosproject/core/CoreService.java
+++ b/core/api/src/main/java/org/onosproject/core/CoreService.java
@@ -50,6 +50,7 @@
/**
* Returns an existing application id from a given id.
+ *
* @param id the short value of the id
* @return an application id
*/
@@ -57,6 +58,7 @@
/**
* Returns an existing application id from a given id.
+ *
* @param name the name portion of the ID to look up
* @return an application id
*/
@@ -67,10 +69,21 @@
* to follow the reverse DNS convention, e.g.
* {@code org.flying.circus.app}
*
- * @param identifier string identifier
+ * @param name string identifier
* @return the application id
*/
- ApplicationId registerApplication(String identifier);
+ ApplicationId registerApplication(String name);
+
+ /**
+ * Registers a new application by its name, which is expected
+ * to follow the reverse DNS convention, e.g.
+ * {@code org.flying.circus.app}, along with its pre-deactivation hook.
+ *
+ * @param name string identifier
+ * @param preDeactivate pre-deactivation hook
+ * @return the application id
+ */
+ ApplicationId registerApplication(String name, Runnable preDeactivate);
/**
* Returns an id generator for a given topic.
diff --git a/core/api/src/test/java/org/onosproject/app/ApplicationServiceAdapter.java b/core/api/src/test/java/org/onosproject/app/ApplicationServiceAdapter.java
index 479cc59..96324a9 100644
--- a/core/api/src/test/java/org/onosproject/app/ApplicationServiceAdapter.java
+++ b/core/api/src/test/java/org/onosproject/app/ApplicationServiceAdapter.java
@@ -51,6 +51,10 @@
}
@Override
+ public void registerDeactivateHook(ApplicationId appId, Runnable hook) {
+ }
+
+ @Override
public void addListener(ApplicationListener listener) {
}
diff --git a/core/api/src/test/java/org/onosproject/core/CoreServiceAdapter.java b/core/api/src/test/java/org/onosproject/core/CoreServiceAdapter.java
index 0f6abd6..6d45e8c 100644
--- a/core/api/src/test/java/org/onosproject/core/CoreServiceAdapter.java
+++ b/core/api/src/test/java/org/onosproject/core/CoreServiceAdapter.java
@@ -43,7 +43,12 @@
}
@Override
- public ApplicationId registerApplication(String identifier) {
+ public ApplicationId registerApplication(String name) {
+ return null;
+ }
+
+ @Override
+ public ApplicationId registerApplication(String name, Runnable preDeactivate) {
return null;
}
diff --git a/core/net/src/main/java/org/onosproject/app/impl/ApplicationManager.java b/core/net/src/main/java/org/onosproject/app/impl/ApplicationManager.java
index 161659f..a9e928e 100644
--- a/core/net/src/main/java/org/onosproject/app/impl/ApplicationManager.java
+++ b/core/net/src/main/java/org/onosproject/app/impl/ApplicationManager.java
@@ -15,6 +15,7 @@
*/
package org.onosproject.app.impl;
+import com.google.common.collect.Maps;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
@@ -30,20 +31,21 @@
import org.onosproject.app.ApplicationState;
import org.onosproject.app.ApplicationStore;
import org.onosproject.app.ApplicationStoreDelegate;
-import org.onosproject.event.AbstractListenerManager;
import org.onosproject.core.Application;
import org.onosproject.core.ApplicationId;
+import org.onosproject.event.AbstractListenerManager;
import org.onosproject.security.Permission;
import org.onosproject.security.SecurityUtil;
import org.slf4j.Logger;
import java.io.InputStream;
+import java.util.Map;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onosproject.app.ApplicationEvent.Type.*;
-import static org.onosproject.security.AppPermission.Type.*;
import static org.onosproject.security.AppGuard.checkPermission;
+import static org.onosproject.security.AppPermission.Type.APP_READ;
import static org.slf4j.LoggerFactory.getLogger;
/**
@@ -69,6 +71,9 @@
private boolean initializing;
+ // Application supplied hooks for pre-activation processing.
+ private final Map<String, Runnable> deactivateHooks = Maps.newConcurrentMap();
+
@Activate
public void activate() {
eventDispatcher.addSink(ApplicationEvent.class, listenerRegistry);
@@ -122,6 +127,14 @@
}
@Override
+ public void registerDeactivateHook(ApplicationId appId, Runnable hook) {
+ checkPermission(APP_READ);
+ checkNotNull(appId, APP_ID_NULL);
+ checkNotNull(hook, "Hook cannot be null");
+ deactivateHooks.put(appId.name(), hook);
+ }
+
+ @Override
public Application install(InputStream appDescStream) {
checkNotNull(appDescStream, "Application archive stream cannot be null");
Application app = store.create(appDescStream);
@@ -235,6 +248,7 @@
private synchronized boolean uninstallAppFeatures(Application app) throws Exception {
boolean changed = false;
+ invokeHook(deactivateHooks.get(app.id().name()), app.id());
for (String name : app.features()) {
Feature feature = featuresService.getFeature(name);
if (feature != null && featuresService.isInstalled(feature)) {
@@ -247,4 +261,16 @@
return changed;
}
+ // Invokes the specified function, if not null.
+ private void invokeHook(Runnable hook, ApplicationId appId) {
+ if (hook != null) {
+ try {
+ hook.run();
+ } catch (Exception e) {
+ log.warn("Deactivate hook for application {} encountered an error",
+ appId.name(), e);
+ }
+ }
+ }
+
}
diff --git a/core/net/src/main/java/org/onosproject/core/impl/CoreManager.java b/core/net/src/main/java/org/onosproject/core/impl/CoreManager.java
index f4d560a..ec99c18 100644
--- a/core/net/src/main/java/org/onosproject/core/impl/CoreManager.java
+++ b/core/net/src/main/java/org/onosproject/core/impl/CoreManager.java
@@ -24,6 +24,7 @@
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onlab.util.SharedExecutors;
+import org.onosproject.app.ApplicationService;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.ApplicationIdStore;
@@ -48,7 +49,7 @@
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.onosproject.security.AppGuard.checkPermission;
-import static org.onosproject.security.AppPermission.Type.*;
+import static org.onosproject.security.AppPermission.Type.APP_READ;
@@ -71,6 +72,9 @@
protected IdBlockStore idBlockStore;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ApplicationService appService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ComponentConfigService cfgService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -111,28 +115,24 @@
@Override
public Version version() {
checkPermission(APP_READ);
-
return version;
}
@Override
public Set<ApplicationId> getAppIds() {
checkPermission(APP_READ);
-
return applicationIdStore.getAppIds();
}
@Override
public ApplicationId getAppId(Short id) {
checkPermission(APP_READ);
-
return applicationIdStore.getAppId(id);
}
@Override
public ApplicationId getAppId(String name) {
checkPermission(APP_READ);
-
return applicationIdStore.getAppId(name);
}
@@ -144,6 +144,13 @@
}
@Override
+ public ApplicationId registerApplication(String name, Runnable preDeactivate) {
+ ApplicationId id = registerApplication(name);
+ appService.registerDeactivateHook(id, preDeactivate);
+ return id;
+ }
+
+ @Override
public IdGenerator getIdGenerator(String topic) {
IdBlockAllocator allocator = new StoreBasedIdBlockAllocator(topic, idBlockStore);
return new BlockAllocatorBasedIdGenerator(allocator);
@@ -185,10 +192,10 @@
*/
private static Integer getIntegerProperty(Dictionary<?, ?> properties,
String propertyName) {
- Integer value = null;
+ Integer value;
try {
String s = (String) properties.get(propertyName);
- value = isNullOrEmpty(s) ? value : Integer.parseInt(s.trim());
+ value = isNullOrEmpty(s) ? null : Integer.parseInt(s.trim());
} catch (NumberFormatException | ClassCastException e) {
value = null;
}
diff --git a/core/net/src/test/java/org/onosproject/app/impl/ApplicationManagerTest.java b/core/net/src/test/java/org/onosproject/app/impl/ApplicationManagerTest.java
index 1ce31ac..5461cf0 100644
--- a/core/net/src/test/java/org/onosproject/app/impl/ApplicationManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/app/impl/ApplicationManagerTest.java
@@ -24,11 +24,11 @@
import org.onosproject.app.ApplicationState;
import org.onosproject.app.ApplicationStoreAdapter;
import org.onosproject.common.app.ApplicationArchive;
+import org.onosproject.common.event.impl.TestEventDispatcher;
import org.onosproject.core.Application;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.DefaultApplication;
import org.onosproject.core.DefaultApplicationId;
-import org.onosproject.common.event.impl.TestEventDispatcher;
import java.io.InputStream;
import java.net.URI;
@@ -36,7 +36,7 @@
import java.util.Optional;
import java.util.Set;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.*;
import static org.onosproject.app.ApplicationEvent.Type.*;
import static org.onosproject.app.ApplicationState.ACTIVE;
import static org.onosproject.app.ApplicationState.INSTALLED;
@@ -53,6 +53,8 @@
private ApplicationManager mgr = new ApplicationManager();
private ApplicationListener listener = new TestListener();
+ private boolean deactivated = false;
+
@Before
public void setUp() {
injectEventDispatcher(mgr, new TestEventDispatcher());
@@ -88,6 +90,11 @@
assertEquals("incorrect app count", 1, mgr.getApplications().size());
assertEquals("incorrect app", app, mgr.getApplication(APP_ID));
assertEquals("incorrect app state", INSTALLED, mgr.getState(APP_ID));
+ mgr.registerDeactivateHook(app.id(), this::deactivateHook);
+ }
+
+ private void deactivateHook() {
+ deactivated = true;
}
@Test
@@ -102,6 +109,7 @@
install();
mgr.activate(APP_ID);
assertEquals("incorrect app state", ACTIVE, mgr.getState(APP_ID));
+ assertFalse("preDeactivate hook wrongly called", deactivated);
}
@Test
@@ -109,6 +117,7 @@
activate();
mgr.deactivate(APP_ID);
assertEquals("incorrect app state", INSTALLED, mgr.getState(APP_ID));
+ assertTrue("preDeactivate hook not called", deactivated);
}
diff --git a/incubator/net/src/test/java/org/onosproject/incubator/net/mcast/impl/MulticastRouteManagerTest.java b/incubator/net/src/test/java/org/onosproject/incubator/net/mcast/impl/MulticastRouteManagerTest.java
index 545e21d..bec9cde 100644
--- a/incubator/net/src/test/java/org/onosproject/incubator/net/mcast/impl/MulticastRouteManagerTest.java
+++ b/incubator/net/src/test/java/org/onosproject/incubator/net/mcast/impl/MulticastRouteManagerTest.java
@@ -23,10 +23,8 @@
import org.onlab.packet.IpPrefix;
import org.onosproject.common.event.impl.TestEventDispatcher;
import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
+import org.onosproject.core.CoreServiceAdapter;
import org.onosproject.core.DefaultApplicationId;
-import org.onosproject.core.IdGenerator;
-import org.onosproject.core.Version;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.PortNumber;
import org.onosproject.net.mcast.McastEvent;
@@ -35,7 +33,6 @@
import org.onosproject.store.service.TestStorageService;
import java.util.List;
-import java.util.Set;
import static junit.framework.Assert.fail;
import static junit.framework.TestCase.assertEquals;
@@ -48,16 +45,16 @@
public class MulticastRouteManagerTest {
McastRoute r1 = new McastRoute(IpPrefix.valueOf("1.1.1.1/8"),
- IpPrefix.valueOf("1.1.1.2/8"),
- McastRoute.Type.IGMP);
+ IpPrefix.valueOf("1.1.1.2/8"),
+ McastRoute.Type.IGMP);
McastRoute r11 = new McastRoute(IpPrefix.valueOf("1.1.1.1/8"),
- IpPrefix.valueOf("1.1.1.2/8"),
- McastRoute.Type.STATIC);
+ IpPrefix.valueOf("1.1.1.2/8"),
+ McastRoute.Type.STATIC);
McastRoute r2 = new McastRoute(IpPrefix.valueOf("2.2.2.1/8"),
- IpPrefix.valueOf("2.2.2.2/8"),
- McastRoute.Type.PIM);
+ IpPrefix.valueOf("2.2.2.2/8"),
+ McastRoute.Type.PIM);
ConnectPoint cp1 = new ConnectPoint(did("1"), PortNumber.portNumber(1));
@@ -75,7 +72,7 @@
injectEventDispatcher(manager, new TestEventDispatcher());
TestUtils.setField(manager, "storageService", new TestStorageService());
TestUtils.setField(manager, "coreService", new TestCoreService());
- events = Lists.newArrayList();
+ events = Lists.newArrayList();
manager.activate();
manager.addListener(listener);
}
@@ -152,49 +149,23 @@
for (int i = 0; i < evs.length; i++) {
if (evs[i] != events.get(i).type()) {
- fail(String.format("Mismtached events# obtained -> %s : expected %s",
+ fail(String.format("Mismatched events# obtained -> %s : expected %s",
events, evs));
}
}
}
class TestMulticastListener implements McastListener {
-
@Override
public void event(McastEvent event) {
events.add(event);
}
}
- private class TestCoreService implements CoreService {
+ private class TestCoreService extends CoreServiceAdapter {
@Override
- public Version version() {
- return null;
- }
-
- @Override
- public Set<ApplicationId> getAppIds() {
- return null;
- }
-
- @Override
- public ApplicationId getAppId(Short id) {
- return null;
- }
-
- @Override
- public ApplicationId getAppId(String name) {
- return null;
- }
-
- @Override
- public ApplicationId registerApplication(String identifier) {
- return new DefaultApplicationId(0, identifier);
- }
-
- @Override
- public IdGenerator getIdGenerator(String topic) {
- return null;
+ public ApplicationId registerApplication(String name) {
+ return new DefaultApplicationId(0, name);
}
}
}