Bug 2932: NPE on bundle activation
[controller.git] / opendaylight / netconf / config-netconf-connector / src / main / java / org / opendaylight / controller / netconf / confignetconfconnector / osgi / YangStoreService.java
index de151a896991f9ffc12a97df1a6bf41ece5e92a9..ac3873e6b89be414429773ee54ee53b05bec4f2c 100644 (file)
@@ -14,6 +14,7 @@ import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import java.lang.ref.SoftReference;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
@@ -21,9 +22,12 @@ import java.util.concurrent.Executors;
 import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.atomic.AtomicReference;
 import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.netconf.api.Capability;
+import org.opendaylight.controller.netconf.api.monitoring.CapabilityListener;
 import org.opendaylight.controller.netconf.notifications.BaseNetconfNotificationListener;
 import org.opendaylight.controller.netconf.notifications.BaseNotificationPublisherRegistration;
 import org.opendaylight.controller.netconf.notifications.NetconfNotificationCollector;
+import org.opendaylight.controller.netconf.util.capability.YangModuleCapability;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfCapabilityChange;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfCapabilityChangeBuilder;
@@ -77,6 +81,8 @@ public class YangStoreService implements YangStoreContext {
         }
     });
 
+    private final Set<CapabilityListener> listeners = Collections.synchronizedSet(new HashSet<CapabilityListener>());
+
     public YangStoreService(final SchemaContextProvider schemaContextProvider, final BundleContext context) {
         this(schemaContextProvider, new NotificationCollectorTracker(context));
     }
@@ -130,7 +136,34 @@ public class YangStoreService implements YangStoreContext {
         notificationExecutor.submit(new CapabilityChangeNotifier(previous));
     }
 
+    public AutoCloseable registerCapabilityListener(final CapabilityListener listener) {
+
+        YangStoreContext context = ref.get().get();
+
+        if(context == null) {
+            context = getYangStoreSnapshot();
+        }
+
+        this.listeners.add(listener);
+        listener.onCapabilitiesAdded(NetconfOperationServiceFactoryImpl.setupCapabilities(context));
+
+        return new AutoCloseable() {
+            @Override
+            public void close() throws Exception {
+                YangStoreService.this.listeners.remove(listener);
+            }
+        };
+    }
+
+    private static final Function<Module, Capability> MODULE_TO_CAPABILITY = new Function<Module, Capability>() {
+        @Override
+        public Capability apply(final Module module) {
+            return new YangModuleCapability(module, module.getSource());
+        }
+    };
+
     private final class CapabilityChangeNotifier implements Runnable {
+
         private final YangStoreSnapshot previous;
 
         public CapabilityChangeNotifier(final YangStoreSnapshot previous) {
@@ -142,7 +175,19 @@ public class YangStoreService implements YangStoreContext {
             final YangStoreContext current = getYangStoreSnapshot();
 
             if(current.equals(previous) == false) {
-                notificationPublisher.onCapabilityChanged(computeDiff(previous, current));
+                final Sets.SetView<Module> removed = Sets.difference(previous.getModules(), current.getModules());
+                final Sets.SetView<Module> added = Sets.difference(current.getModules(), previous.getModules());
+
+                // Notify notification manager
+                notificationPublisher.onCapabilityChanged(computeDiff(removed, added));
+
+                // Notify direct capability listener TODO would it not be better if the capability listeners went through notification manager ?
+                for (final CapabilityListener listener : listeners) {
+                    listener.onCapabilitiesAdded(Sets.newHashSet(Collections2.transform(added, MODULE_TO_CAPABILITY)));
+                }
+                for (final CapabilityListener listener : listeners) {
+                    listener.onCapabilitiesRemoved(Sets.newHashSet(Collections2.transform(removed, MODULE_TO_CAPABILITY)));
+                }
             }
         }
     }
@@ -150,15 +195,11 @@ public class YangStoreService implements YangStoreContext {
     private static final Function<Module, Uri> MODULE_TO_URI = new Function<Module, Uri>() {
         @Override
         public Uri apply(final Module input) {
-            final QName qName = QName.cachedReference(QName.create(input.getQNameModule(), input.getName()));
-            return new Uri(qName.toString());
+            return new Uri(new YangModuleCapability(input, input.getSource()).getCapabilityUri());
         }
     };
 
-    static NetconfCapabilityChange computeDiff(final YangStoreContext previous, final YangStoreContext current) {
-        final Sets.SetView<Module> removed = Sets.difference(previous.getModules(), current.getModules());
-        final Sets.SetView<Module> added = Sets.difference(current.getModules(), previous.getModules());
-
+    static NetconfCapabilityChange computeDiff(final Sets.SetView<Module> removed, final Sets.SetView<Module> added) {
         final NetconfCapabilityChangeBuilder netconfCapabilityChangeBuilder = new NetconfCapabilityChangeBuilder();
         netconfCapabilityChangeBuilder.setChangedBy(new ChangedByBuilder().setServerOrUser(new ServerBuilder().setServer(true).build()).build());
         netconfCapabilityChangeBuilder.setDeletedCapability(Lists.newArrayList(Collections2.transform(removed, MODULE_TO_URI)));