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;
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;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.changed.by.parms.ChangedByBuilder;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.changed.by.parms.changed.by.server.or.user.ServerBuilder;
+import org.opendaylight.yangtools.sal.binding.generator.util.BindingRuntimeContext;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier;
*
* We synchronize with GC as usual, using a SoftReference.
*
- * The atomic reference is used to synchronize with {@link #refresh()}, e.g. when
+ * The atomic reference is used to synchronize with {@link #refresh(org.opendaylight.yangtools.sal.binding.generator.util.BindingRuntimeContext)}, e.g. when
* refresh happens, it will push a SoftReference(null), e.g. simulate the GC. Now
* that may happen while the getter is already busy acting on the old schema context,
* so it needs to understand that a refresh has happened and retry. To do that, it
private final AtomicReference<SoftReference<YangStoreSnapshot>> ref =
new AtomicReference<>(new SoftReference<YangStoreSnapshot>(null));
+ private final AtomicReference<SoftReference<BindingRuntimeContext>> refBindingContext =
+ new AtomicReference<>(new SoftReference<BindingRuntimeContext>(null));
+
private final SchemaContextProvider schemaContextProvider;
private final BaseNetconfNotificationListener notificationPublisher;
}
});
+ private final Set<CapabilityListener> listeners = Collections.synchronizedSet(new HashSet<CapabilityListener>());
+
public YangStoreService(final SchemaContextProvider schemaContextProvider, final BundleContext context) {
this(schemaContextProvider, new NotificationCollectorTracker(context));
}
while (ret == null) {
// We need to be compute a new value
- ret = new YangStoreSnapshot(schemaContextProvider.getSchemaContext());
+ ret = new YangStoreSnapshot(schemaContextProvider.getSchemaContext(), refBindingContext.get().get());
if (!ref.compareAndSet(r, new SoftReference<>(ret))) {
LOG.debug("Concurrent refresh detected, recomputing snapshot");
return getYangStoreSnapshot().getModuleSource(moduleIdentifier);
}
- public void refresh() {
+ @Override
+ public EnumResolver getEnumResolver() {
+ return getYangStoreSnapshot().getEnumResolver();
+ }
+
+ public void refresh(final BindingRuntimeContext runtimeContext) {
final YangStoreSnapshot previous = ref.get().get();
ref.set(new SoftReference<YangStoreSnapshot>(null));
+ refBindingContext.set(new SoftReference<>(runtimeContext));
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) {
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)));
+ }
}
}
}
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)));