Thomas Vachuska

ONOS-3321 Added ability for applications to register a deactivation hook.

Change-Id: I284321474dbf9e39e8b61f7b907f637ba6b80aaf
...@@ -67,4 +67,11 @@ public interface ApplicationService ...@@ -67,4 +67,11 @@ public interface ApplicationService
67 */ 67 */
68 Set<Permission> getPermissions(ApplicationId appId); 68 Set<Permission> getPermissions(ApplicationId appId);
69 69
70 + /**
71 + * Registers application pre-deactivation processing hook.
72 + *
73 + * @param appId application identifier
74 + * @param hook pre-deactivation hook
75 + */
76 + void registerDeactivateHook(ApplicationId appId, Runnable hook);
70 } 77 }
......
...@@ -50,6 +50,7 @@ public interface CoreService { ...@@ -50,6 +50,7 @@ public interface CoreService {
50 50
51 /** 51 /**
52 * Returns an existing application id from a given id. 52 * Returns an existing application id from a given id.
53 + *
53 * @param id the short value of the id 54 * @param id the short value of the id
54 * @return an application id 55 * @return an application id
55 */ 56 */
...@@ -57,6 +58,7 @@ public interface CoreService { ...@@ -57,6 +58,7 @@ public interface CoreService {
57 58
58 /** 59 /**
59 * Returns an existing application id from a given id. 60 * Returns an existing application id from a given id.
61 + *
60 * @param name the name portion of the ID to look up 62 * @param name the name portion of the ID to look up
61 * @return an application id 63 * @return an application id
62 */ 64 */
...@@ -67,10 +69,21 @@ public interface CoreService { ...@@ -67,10 +69,21 @@ public interface CoreService {
67 * to follow the reverse DNS convention, e.g. 69 * to follow the reverse DNS convention, e.g.
68 * {@code org.flying.circus.app} 70 * {@code org.flying.circus.app}
69 * 71 *
70 - * @param identifier string identifier 72 + * @param name string identifier
73 + * @return the application id
74 + */
75 + ApplicationId registerApplication(String name);
76 +
77 + /**
78 + * Registers a new application by its name, which is expected
79 + * to follow the reverse DNS convention, e.g.
80 + * {@code org.flying.circus.app}, along with its pre-deactivation hook.
81 + *
82 + * @param name string identifier
83 + * @param preDeactivate pre-deactivation hook
71 * @return the application id 84 * @return the application id
72 */ 85 */
73 - ApplicationId registerApplication(String identifier); 86 + ApplicationId registerApplication(String name, Runnable preDeactivate);
74 87
75 /** 88 /**
76 * Returns an id generator for a given topic. 89 * Returns an id generator for a given topic.
......
...@@ -51,6 +51,10 @@ public class ApplicationServiceAdapter implements ApplicationService { ...@@ -51,6 +51,10 @@ public class ApplicationServiceAdapter implements ApplicationService {
51 } 51 }
52 52
53 @Override 53 @Override
54 + public void registerDeactivateHook(ApplicationId appId, Runnable hook) {
55 + }
56 +
57 + @Override
54 public void addListener(ApplicationListener listener) { 58 public void addListener(ApplicationListener listener) {
55 } 59 }
56 60
......
...@@ -43,7 +43,12 @@ public class CoreServiceAdapter implements CoreService { ...@@ -43,7 +43,12 @@ public class CoreServiceAdapter implements CoreService {
43 } 43 }
44 44
45 @Override 45 @Override
46 - public ApplicationId registerApplication(String identifier) { 46 + public ApplicationId registerApplication(String name) {
47 + return null;
48 + }
49 +
50 + @Override
51 + public ApplicationId registerApplication(String name, Runnable preDeactivate) {
47 return null; 52 return null;
48 } 53 }
49 54
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
15 */ 15 */
16 package org.onosproject.app.impl; 16 package org.onosproject.app.impl;
17 17
18 +import com.google.common.collect.Maps;
18 import org.apache.felix.scr.annotations.Activate; 19 import org.apache.felix.scr.annotations.Activate;
19 import org.apache.felix.scr.annotations.Component; 20 import org.apache.felix.scr.annotations.Component;
20 import org.apache.felix.scr.annotations.Deactivate; 21 import org.apache.felix.scr.annotations.Deactivate;
...@@ -30,20 +31,21 @@ import org.onosproject.app.ApplicationService; ...@@ -30,20 +31,21 @@ import org.onosproject.app.ApplicationService;
30 import org.onosproject.app.ApplicationState; 31 import org.onosproject.app.ApplicationState;
31 import org.onosproject.app.ApplicationStore; 32 import org.onosproject.app.ApplicationStore;
32 import org.onosproject.app.ApplicationStoreDelegate; 33 import org.onosproject.app.ApplicationStoreDelegate;
33 -import org.onosproject.event.AbstractListenerManager;
34 import org.onosproject.core.Application; 34 import org.onosproject.core.Application;
35 import org.onosproject.core.ApplicationId; 35 import org.onosproject.core.ApplicationId;
36 +import org.onosproject.event.AbstractListenerManager;
36 import org.onosproject.security.Permission; 37 import org.onosproject.security.Permission;
37 import org.onosproject.security.SecurityUtil; 38 import org.onosproject.security.SecurityUtil;
38 import org.slf4j.Logger; 39 import org.slf4j.Logger;
39 40
40 import java.io.InputStream; 41 import java.io.InputStream;
42 +import java.util.Map;
41 import java.util.Set; 43 import java.util.Set;
42 44
43 import static com.google.common.base.Preconditions.checkNotNull; 45 import static com.google.common.base.Preconditions.checkNotNull;
44 import static org.onosproject.app.ApplicationEvent.Type.*; 46 import static org.onosproject.app.ApplicationEvent.Type.*;
45 -import static org.onosproject.security.AppPermission.Type.*;
46 import static org.onosproject.security.AppGuard.checkPermission; 47 import static org.onosproject.security.AppGuard.checkPermission;
48 +import static org.onosproject.security.AppPermission.Type.APP_READ;
47 import static org.slf4j.LoggerFactory.getLogger; 49 import static org.slf4j.LoggerFactory.getLogger;
48 50
49 /** 51 /**
...@@ -69,6 +71,9 @@ public class ApplicationManager ...@@ -69,6 +71,9 @@ public class ApplicationManager
69 71
70 private boolean initializing; 72 private boolean initializing;
71 73
74 + // Application supplied hooks for pre-activation processing.
75 + private final Map<String, Runnable> deactivateHooks = Maps.newConcurrentMap();
76 +
72 @Activate 77 @Activate
73 public void activate() { 78 public void activate() {
74 eventDispatcher.addSink(ApplicationEvent.class, listenerRegistry); 79 eventDispatcher.addSink(ApplicationEvent.class, listenerRegistry);
...@@ -122,6 +127,14 @@ public class ApplicationManager ...@@ -122,6 +127,14 @@ public class ApplicationManager
122 } 127 }
123 128
124 @Override 129 @Override
130 + public void registerDeactivateHook(ApplicationId appId, Runnable hook) {
131 + checkPermission(APP_READ);
132 + checkNotNull(appId, APP_ID_NULL);
133 + checkNotNull(hook, "Hook cannot be null");
134 + deactivateHooks.put(appId.name(), hook);
135 + }
136 +
137 + @Override
125 public Application install(InputStream appDescStream) { 138 public Application install(InputStream appDescStream) {
126 checkNotNull(appDescStream, "Application archive stream cannot be null"); 139 checkNotNull(appDescStream, "Application archive stream cannot be null");
127 Application app = store.create(appDescStream); 140 Application app = store.create(appDescStream);
...@@ -235,6 +248,7 @@ public class ApplicationManager ...@@ -235,6 +248,7 @@ public class ApplicationManager
235 248
236 private synchronized boolean uninstallAppFeatures(Application app) throws Exception { 249 private synchronized boolean uninstallAppFeatures(Application app) throws Exception {
237 boolean changed = false; 250 boolean changed = false;
251 + invokeHook(deactivateHooks.get(app.id().name()), app.id());
238 for (String name : app.features()) { 252 for (String name : app.features()) {
239 Feature feature = featuresService.getFeature(name); 253 Feature feature = featuresService.getFeature(name);
240 if (feature != null && featuresService.isInstalled(feature)) { 254 if (feature != null && featuresService.isInstalled(feature)) {
...@@ -247,4 +261,16 @@ public class ApplicationManager ...@@ -247,4 +261,16 @@ public class ApplicationManager
247 return changed; 261 return changed;
248 } 262 }
249 263
264 + // Invokes the specified function, if not null.
265 + private void invokeHook(Runnable hook, ApplicationId appId) {
266 + if (hook != null) {
267 + try {
268 + hook.run();
269 + } catch (Exception e) {
270 + log.warn("Deactivate hook for application {} encountered an error",
271 + appId.name(), e);
272 + }
273 + }
274 + }
275 +
250 } 276 }
......
...@@ -24,6 +24,7 @@ import org.apache.felix.scr.annotations.Reference; ...@@ -24,6 +24,7 @@ import org.apache.felix.scr.annotations.Reference;
24 import org.apache.felix.scr.annotations.ReferenceCardinality; 24 import org.apache.felix.scr.annotations.ReferenceCardinality;
25 import org.apache.felix.scr.annotations.Service; 25 import org.apache.felix.scr.annotations.Service;
26 import org.onlab.util.SharedExecutors; 26 import org.onlab.util.SharedExecutors;
27 +import org.onosproject.app.ApplicationService;
27 import org.onosproject.cfg.ComponentConfigService; 28 import org.onosproject.cfg.ComponentConfigService;
28 import org.onosproject.core.ApplicationId; 29 import org.onosproject.core.ApplicationId;
29 import org.onosproject.core.ApplicationIdStore; 30 import org.onosproject.core.ApplicationIdStore;
...@@ -48,7 +49,7 @@ import java.util.Set; ...@@ -48,7 +49,7 @@ import java.util.Set;
48 import static com.google.common.base.Preconditions.checkNotNull; 49 import static com.google.common.base.Preconditions.checkNotNull;
49 import static com.google.common.base.Strings.isNullOrEmpty; 50 import static com.google.common.base.Strings.isNullOrEmpty;
50 import static org.onosproject.security.AppGuard.checkPermission; 51 import static org.onosproject.security.AppGuard.checkPermission;
51 -import static org.onosproject.security.AppPermission.Type.*; 52 +import static org.onosproject.security.AppPermission.Type.APP_READ;
52 53
53 54
54 55
...@@ -71,6 +72,9 @@ public class CoreManager implements CoreService { ...@@ -71,6 +72,9 @@ public class CoreManager implements CoreService {
71 protected IdBlockStore idBlockStore; 72 protected IdBlockStore idBlockStore;
72 73
73 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 74 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 + protected ApplicationService appService;
76 +
77 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
74 protected ComponentConfigService cfgService; 78 protected ComponentConfigService cfgService;
75 79
76 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
...@@ -111,28 +115,24 @@ public class CoreManager implements CoreService { ...@@ -111,28 +115,24 @@ public class CoreManager implements CoreService {
111 @Override 115 @Override
112 public Version version() { 116 public Version version() {
113 checkPermission(APP_READ); 117 checkPermission(APP_READ);
114 -
115 return version; 118 return version;
116 } 119 }
117 120
118 @Override 121 @Override
119 public Set<ApplicationId> getAppIds() { 122 public Set<ApplicationId> getAppIds() {
120 checkPermission(APP_READ); 123 checkPermission(APP_READ);
121 -
122 return applicationIdStore.getAppIds(); 124 return applicationIdStore.getAppIds();
123 } 125 }
124 126
125 @Override 127 @Override
126 public ApplicationId getAppId(Short id) { 128 public ApplicationId getAppId(Short id) {
127 checkPermission(APP_READ); 129 checkPermission(APP_READ);
128 -
129 return applicationIdStore.getAppId(id); 130 return applicationIdStore.getAppId(id);
130 } 131 }
131 132
132 @Override 133 @Override
133 public ApplicationId getAppId(String name) { 134 public ApplicationId getAppId(String name) {
134 checkPermission(APP_READ); 135 checkPermission(APP_READ);
135 -
136 return applicationIdStore.getAppId(name); 136 return applicationIdStore.getAppId(name);
137 } 137 }
138 138
...@@ -144,6 +144,13 @@ public class CoreManager implements CoreService { ...@@ -144,6 +144,13 @@ public class CoreManager implements CoreService {
144 } 144 }
145 145
146 @Override 146 @Override
147 + public ApplicationId registerApplication(String name, Runnable preDeactivate) {
148 + ApplicationId id = registerApplication(name);
149 + appService.registerDeactivateHook(id, preDeactivate);
150 + return id;
151 + }
152 +
153 + @Override
147 public IdGenerator getIdGenerator(String topic) { 154 public IdGenerator getIdGenerator(String topic) {
148 IdBlockAllocator allocator = new StoreBasedIdBlockAllocator(topic, idBlockStore); 155 IdBlockAllocator allocator = new StoreBasedIdBlockAllocator(topic, idBlockStore);
149 return new BlockAllocatorBasedIdGenerator(allocator); 156 return new BlockAllocatorBasedIdGenerator(allocator);
...@@ -185,10 +192,10 @@ public class CoreManager implements CoreService { ...@@ -185,10 +192,10 @@ public class CoreManager implements CoreService {
185 */ 192 */
186 private static Integer getIntegerProperty(Dictionary<?, ?> properties, 193 private static Integer getIntegerProperty(Dictionary<?, ?> properties,
187 String propertyName) { 194 String propertyName) {
188 - Integer value = null; 195 + Integer value;
189 try { 196 try {
190 String s = (String) properties.get(propertyName); 197 String s = (String) properties.get(propertyName);
191 - value = isNullOrEmpty(s) ? value : Integer.parseInt(s.trim()); 198 + value = isNullOrEmpty(s) ? null : Integer.parseInt(s.trim());
192 } catch (NumberFormatException | ClassCastException e) { 199 } catch (NumberFormatException | ClassCastException e) {
193 value = null; 200 value = null;
194 } 201 }
......
...@@ -24,11 +24,11 @@ import org.onosproject.app.ApplicationListener; ...@@ -24,11 +24,11 @@ import org.onosproject.app.ApplicationListener;
24 import org.onosproject.app.ApplicationState; 24 import org.onosproject.app.ApplicationState;
25 import org.onosproject.app.ApplicationStoreAdapter; 25 import org.onosproject.app.ApplicationStoreAdapter;
26 import org.onosproject.common.app.ApplicationArchive; 26 import org.onosproject.common.app.ApplicationArchive;
27 +import org.onosproject.common.event.impl.TestEventDispatcher;
27 import org.onosproject.core.Application; 28 import org.onosproject.core.Application;
28 import org.onosproject.core.ApplicationId; 29 import org.onosproject.core.ApplicationId;
29 import org.onosproject.core.DefaultApplication; 30 import org.onosproject.core.DefaultApplication;
30 import org.onosproject.core.DefaultApplicationId; 31 import org.onosproject.core.DefaultApplicationId;
31 -import org.onosproject.common.event.impl.TestEventDispatcher;
32 32
33 import java.io.InputStream; 33 import java.io.InputStream;
34 import java.net.URI; 34 import java.net.URI;
...@@ -36,7 +36,7 @@ import java.util.HashSet; ...@@ -36,7 +36,7 @@ import java.util.HashSet;
36 import java.util.Optional; 36 import java.util.Optional;
37 import java.util.Set; 37 import java.util.Set;
38 38
39 -import static org.junit.Assert.assertEquals; 39 +import static org.junit.Assert.*;
40 import static org.onosproject.app.ApplicationEvent.Type.*; 40 import static org.onosproject.app.ApplicationEvent.Type.*;
41 import static org.onosproject.app.ApplicationState.ACTIVE; 41 import static org.onosproject.app.ApplicationState.ACTIVE;
42 import static org.onosproject.app.ApplicationState.INSTALLED; 42 import static org.onosproject.app.ApplicationState.INSTALLED;
...@@ -53,6 +53,8 @@ public class ApplicationManagerTest { ...@@ -53,6 +53,8 @@ public class ApplicationManagerTest {
53 private ApplicationManager mgr = new ApplicationManager(); 53 private ApplicationManager mgr = new ApplicationManager();
54 private ApplicationListener listener = new TestListener(); 54 private ApplicationListener listener = new TestListener();
55 55
56 + private boolean deactivated = false;
57 +
56 @Before 58 @Before
57 public void setUp() { 59 public void setUp() {
58 injectEventDispatcher(mgr, new TestEventDispatcher()); 60 injectEventDispatcher(mgr, new TestEventDispatcher());
...@@ -88,6 +90,11 @@ public class ApplicationManagerTest { ...@@ -88,6 +90,11 @@ public class ApplicationManagerTest {
88 assertEquals("incorrect app count", 1, mgr.getApplications().size()); 90 assertEquals("incorrect app count", 1, mgr.getApplications().size());
89 assertEquals("incorrect app", app, mgr.getApplication(APP_ID)); 91 assertEquals("incorrect app", app, mgr.getApplication(APP_ID));
90 assertEquals("incorrect app state", INSTALLED, mgr.getState(APP_ID)); 92 assertEquals("incorrect app state", INSTALLED, mgr.getState(APP_ID));
93 + mgr.registerDeactivateHook(app.id(), this::deactivateHook);
94 + }
95 +
96 + private void deactivateHook() {
97 + deactivated = true;
91 } 98 }
92 99
93 @Test 100 @Test
...@@ -102,6 +109,7 @@ public class ApplicationManagerTest { ...@@ -102,6 +109,7 @@ public class ApplicationManagerTest {
102 install(); 109 install();
103 mgr.activate(APP_ID); 110 mgr.activate(APP_ID);
104 assertEquals("incorrect app state", ACTIVE, mgr.getState(APP_ID)); 111 assertEquals("incorrect app state", ACTIVE, mgr.getState(APP_ID));
112 + assertFalse("preDeactivate hook wrongly called", deactivated);
105 } 113 }
106 114
107 @Test 115 @Test
...@@ -109,6 +117,7 @@ public class ApplicationManagerTest { ...@@ -109,6 +117,7 @@ public class ApplicationManagerTest {
109 activate(); 117 activate();
110 mgr.deactivate(APP_ID); 118 mgr.deactivate(APP_ID);
111 assertEquals("incorrect app state", INSTALLED, mgr.getState(APP_ID)); 119 assertEquals("incorrect app state", INSTALLED, mgr.getState(APP_ID));
120 + assertTrue("preDeactivate hook not called", deactivated);
112 } 121 }
113 122
114 123
......
...@@ -23,10 +23,8 @@ import org.onlab.junit.TestUtils; ...@@ -23,10 +23,8 @@ import org.onlab.junit.TestUtils;
23 import org.onlab.packet.IpPrefix; 23 import org.onlab.packet.IpPrefix;
24 import org.onosproject.common.event.impl.TestEventDispatcher; 24 import org.onosproject.common.event.impl.TestEventDispatcher;
25 import org.onosproject.core.ApplicationId; 25 import org.onosproject.core.ApplicationId;
26 -import org.onosproject.core.CoreService; 26 +import org.onosproject.core.CoreServiceAdapter;
27 import org.onosproject.core.DefaultApplicationId; 27 import org.onosproject.core.DefaultApplicationId;
28 -import org.onosproject.core.IdGenerator;
29 -import org.onosproject.core.Version;
30 import org.onosproject.net.ConnectPoint; 28 import org.onosproject.net.ConnectPoint;
31 import org.onosproject.net.PortNumber; 29 import org.onosproject.net.PortNumber;
32 import org.onosproject.net.mcast.McastEvent; 30 import org.onosproject.net.mcast.McastEvent;
...@@ -35,7 +33,6 @@ import org.onosproject.net.mcast.McastRoute; ...@@ -35,7 +33,6 @@ import org.onosproject.net.mcast.McastRoute;
35 import org.onosproject.store.service.TestStorageService; 33 import org.onosproject.store.service.TestStorageService;
36 34
37 import java.util.List; 35 import java.util.List;
38 -import java.util.Set;
39 36
40 import static junit.framework.Assert.fail; 37 import static junit.framework.Assert.fail;
41 import static junit.framework.TestCase.assertEquals; 38 import static junit.framework.TestCase.assertEquals;
...@@ -48,16 +45,16 @@ import static org.onosproject.net.NetTestTools.injectEventDispatcher; ...@@ -48,16 +45,16 @@ import static org.onosproject.net.NetTestTools.injectEventDispatcher;
48 public class MulticastRouteManagerTest { 45 public class MulticastRouteManagerTest {
49 46
50 McastRoute r1 = new McastRoute(IpPrefix.valueOf("1.1.1.1/8"), 47 McastRoute r1 = new McastRoute(IpPrefix.valueOf("1.1.1.1/8"),
51 - IpPrefix.valueOf("1.1.1.2/8"), 48 + IpPrefix.valueOf("1.1.1.2/8"),
52 - McastRoute.Type.IGMP); 49 + McastRoute.Type.IGMP);
53 50
54 McastRoute r11 = new McastRoute(IpPrefix.valueOf("1.1.1.1/8"), 51 McastRoute r11 = new McastRoute(IpPrefix.valueOf("1.1.1.1/8"),
55 - IpPrefix.valueOf("1.1.1.2/8"), 52 + IpPrefix.valueOf("1.1.1.2/8"),
56 - McastRoute.Type.STATIC); 53 + McastRoute.Type.STATIC);
57 54
58 McastRoute r2 = new McastRoute(IpPrefix.valueOf("2.2.2.1/8"), 55 McastRoute r2 = new McastRoute(IpPrefix.valueOf("2.2.2.1/8"),
59 - IpPrefix.valueOf("2.2.2.2/8"), 56 + IpPrefix.valueOf("2.2.2.2/8"),
60 - McastRoute.Type.PIM); 57 + McastRoute.Type.PIM);
61 58
62 ConnectPoint cp1 = new ConnectPoint(did("1"), PortNumber.portNumber(1)); 59 ConnectPoint cp1 = new ConnectPoint(did("1"), PortNumber.portNumber(1));
63 60
...@@ -75,7 +72,7 @@ public class MulticastRouteManagerTest { ...@@ -75,7 +72,7 @@ public class MulticastRouteManagerTest {
75 injectEventDispatcher(manager, new TestEventDispatcher()); 72 injectEventDispatcher(manager, new TestEventDispatcher());
76 TestUtils.setField(manager, "storageService", new TestStorageService()); 73 TestUtils.setField(manager, "storageService", new TestStorageService());
77 TestUtils.setField(manager, "coreService", new TestCoreService()); 74 TestUtils.setField(manager, "coreService", new TestCoreService());
78 - events = Lists.newArrayList(); 75 + events = Lists.newArrayList();
79 manager.activate(); 76 manager.activate();
80 manager.addListener(listener); 77 manager.addListener(listener);
81 } 78 }
...@@ -152,49 +149,23 @@ public class MulticastRouteManagerTest { ...@@ -152,49 +149,23 @@ public class MulticastRouteManagerTest {
152 149
153 for (int i = 0; i < evs.length; i++) { 150 for (int i = 0; i < evs.length; i++) {
154 if (evs[i] != events.get(i).type()) { 151 if (evs[i] != events.get(i).type()) {
155 - fail(String.format("Mismtached events# obtained -> %s : expected %s", 152 + fail(String.format("Mismatched events# obtained -> %s : expected %s",
156 events, evs)); 153 events, evs));
157 } 154 }
158 } 155 }
159 } 156 }
160 157
161 class TestMulticastListener implements McastListener { 158 class TestMulticastListener implements McastListener {
162 -
163 @Override 159 @Override
164 public void event(McastEvent event) { 160 public void event(McastEvent event) {
165 events.add(event); 161 events.add(event);
166 } 162 }
167 } 163 }
168 164
169 - private class TestCoreService implements CoreService { 165 + private class TestCoreService extends CoreServiceAdapter {
170 - @Override
171 - public Version version() {
172 - return null;
173 - }
174 -
175 - @Override
176 - public Set<ApplicationId> getAppIds() {
177 - return null;
178 - }
179 -
180 - @Override
181 - public ApplicationId getAppId(Short id) {
182 - return null;
183 - }
184 -
185 - @Override
186 - public ApplicationId getAppId(String name) {
187 - return null;
188 - }
189 -
190 - @Override
191 - public ApplicationId registerApplication(String identifier) {
192 - return new DefaultApplicationId(0, identifier);
193 - }
194 -
195 @Override 166 @Override
196 - public IdGenerator getIdGenerator(String topic) { 167 + public ApplicationId registerApplication(String name) {
197 - return null; 168 + return new DefaultApplicationId(0, name);
198 } 169 }
199 } 170 }
200 } 171 }
......