diff options
author | Maros Marsalek <mmarsale@cisco.com> | 2016-07-27 11:05:51 +0200 |
---|---|---|
committer | Maros Marsalek <mmarsale@cisco.com> | 2016-07-29 16:32:07 +0200 |
commit | 0578156b721fa01c8c645b8f9625ecebdb6449e4 (patch) | |
tree | 49d36f24e5d984a8c9f151b1440de88619f8b7de /infra/notification/impl/src/main/java | |
parent | 007d4542388ca89be409ce1a4a4c7a36ddcb538f (diff) |
HONEYCOMB-130: Separate v3po plugin from HC infra
Creating folders:
- common/
- infra/
- v3po/
- vpp-common/
Change-Id: I2c39e1b17e39e7c0f0628f44aa5fe08563fa06e4
Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
Diffstat (limited to 'infra/notification/impl/src/main/java')
9 files changed, 603 insertions, 0 deletions
diff --git a/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollector.java b/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollector.java new file mode 100644 index 000000000..e7d54e318 --- /dev/null +++ b/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollector.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fd.honeycomb.v3po.notification.impl; + +import io.fd.honeycomb.v3po.notification.NotificationCollector; +import java.util.Collection; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService; +import org.opendaylight.yangtools.yang.binding.Notification; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Notification collector based on MD-SAL's {@link NotificationPublishService}. + */ +public final class HoneycombNotificationCollector implements NotificationCollector, AutoCloseable { + + private static final Logger LOG = LoggerFactory.getLogger(HoneycombNotificationCollector.class); + + private final NotificationPublishService bindingDOMNotificationPublishServiceAdapter; + private final NotificationProducerRegistry notificationProducerRegistry; + + public HoneycombNotificationCollector( + @Nonnull final NotificationPublishService bindingDOMNotificationPublishServiceAdapter, + @Nonnull final NotificationProducerRegistry notificationProducerRegistry) { + this.bindingDOMNotificationPublishServiceAdapter = bindingDOMNotificationPublishServiceAdapter; + this.notificationProducerRegistry = notificationProducerRegistry; + } + + @Override + public void close() throws Exception { + LOG.trace("Closing"); + } + + @Override + public void onNotification(@Nonnull final Notification notification) { + LOG.debug("Notification: {} pushed into collector", notification.getClass().getSimpleName()); + LOG.trace("Notification: {} pushed into collector", notification); + try { + bindingDOMNotificationPublishServiceAdapter.putNotification(notification); + } catch (InterruptedException e) { + LOG.warn("Interrupted", e); + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } + } + + @Override + @Nonnull + public Collection<Class<? extends Notification>> getNotificationTypes() { + return notificationProducerRegistry.getNotificationTypes(); + } +} diff --git a/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistry.java b/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistry.java new file mode 100644 index 000000000..8fba700bd --- /dev/null +++ b/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistry.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fd.honeycomb.v3po.notification.impl; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import io.fd.honeycomb.v3po.notification.ManagedNotificationProducer; +import io.fd.honeycomb.v3po.notification.NotificationProducer; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.concurrent.ThreadSafe; +import org.opendaylight.yangtools.yang.binding.Notification; +import org.opendaylight.yangtools.yang.common.QName; + +/** + * Holds the collection of registered notification producers. + * Provides additional information about the types of notifications produced per producer and overall. + */ +@ThreadSafe +public final class NotificationProducerRegistry { + + private final Set<Class<? extends Notification>> notificationTypes; + private final Map<QName, ManagedNotificationProducer> notificationQNameToProducer; + private final Multimap<ManagedNotificationProducer, QName> notificationProducerQNames; + + public NotificationProducerRegistry(final List<ManagedNotificationProducer> notificationProducersDependency) { + this.notificationTypes = toTypes(notificationProducersDependency); + this.notificationQNameToProducer = toQNameMap(notificationProducersDependency); + this.notificationProducerQNames = toQNameMapReversed(notificationProducersDependency); + } + + private static Multimap<ManagedNotificationProducer, QName> toQNameMapReversed(final List<ManagedNotificationProducer> notificationProducers) { + final Multimap<ManagedNotificationProducer, QName> multimap = HashMultimap.create(); + + for (ManagedNotificationProducer producer : notificationProducers) { + for (Class<? extends Notification> aClass : producer.getNotificationTypes()) { + multimap.put(producer, getQName(aClass)); + } + } + return multimap; + } + + private static Set<Class<? extends Notification>> toTypes(final List<ManagedNotificationProducer> notificationProducersDependency) { + // Get all notification types registered from HC notification producers + return notificationProducersDependency + .stream() + .flatMap(producer -> producer.getNotificationTypes().stream()) + .collect(Collectors.toSet()); + } + + + private static Map<QName, ManagedNotificationProducer> toQNameMap(final List<ManagedNotificationProducer> producerDependencies) { + // Only a single notification producer per notification type is allowed + final Map<QName, ManagedNotificationProducer> qNamesToProducers = Maps.newHashMap(); + for (ManagedNotificationProducer notificationProducer : producerDependencies) { + for (QName qName : typesToQNames(notificationProducer.getNotificationTypes())) { + final NotificationProducer previousProducer = qNamesToProducers.put(qName, notificationProducer); + checkArgument(previousProducer == null, "2 producers of the same notification type: %s. " + + "Producer 1: {} Producer 2: {}" , qName, previousProducer, notificationProducer); + } + } + return qNamesToProducers; + } + + + private static Set<QName> typesToQNames(final Collection<Class<? extends Notification>> notificationTypes) { + return notificationTypes + .stream() + .map(NotificationProducerRegistry::getQName) + .collect(Collectors.toSet()); + } + + + public static QName getQName(final Class<? extends Notification> aClass) { + try { + return (QName) aClass.getField("QNAME").get(null); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new IllegalArgumentException("Unable to retrieve QName for notification of type: " + aClass, e); + } + } + + Set<Class<? extends Notification>> getNotificationTypes() { + return notificationTypes; + } + + Map<QName, ManagedNotificationProducer> getNotificationQNameToProducer() { + return notificationQNameToProducer; + } + + Multimap<ManagedNotificationProducer, QName> getNotificationProducerQNames() { + return notificationProducerQNames; + } +} diff --git a/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTracker.java b/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTracker.java new file mode 100644 index 000000000..cefb50ac9 --- /dev/null +++ b/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTracker.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fd.honeycomb.v3po.notification.impl; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.collect.Sets; +import io.fd.honeycomb.v3po.notification.ManagedNotificationProducer; +import io.fd.honeycomb.v3po.notification.NotificationCollector; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import javax.annotation.concurrent.ThreadSafe; +import org.opendaylight.controller.md.sal.dom.spi.DOMNotificationSubscriptionListener; +import org.opendaylight.controller.md.sal.dom.spi.DOMNotificationSubscriptionListenerRegistry; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Starts & stops notification producer dependencies on demand. + * Uses {@link DOMNotificationSubscriptionListenerRegistry} to receive subscription change notifications. + */ +@ThreadSafe +public final class NotificationProducerTracker + implements DOMNotificationSubscriptionListener, AutoCloseable { + + private static final Logger LOG = LoggerFactory.getLogger(NotificationProducerTracker.class); + + private final ListenerRegistration<NotificationProducerTracker> subscriptionListener; + private final NotificationProducerRegistry registry; + private final NotificationCollector collector; + + private final Set<ManagedNotificationProducer> alreadyStartedProducers = new HashSet<>(); + + public NotificationProducerTracker(@Nonnull final NotificationProducerRegistry registry, + @Nonnull final NotificationCollector collector, + @Nonnull final DOMNotificationSubscriptionListenerRegistry notificationRouter) { + this.registry = registry; + this.collector = collector; + this.subscriptionListener = notificationRouter.registerSubscriptionListener(this); + } + + @Override + public synchronized void onSubscriptionChanged(final Set<SchemaPath> set) { + LOG.debug("Subscriptions changed. Current subscriptions: {}", set); + final Set<QName> currentSubscriptions = set.stream().map(SchemaPath::getLastComponent).collect(Collectors.toSet()); + final Set<QName> startedQNames = getStartedQNames(alreadyStartedProducers); + final Sets.SetView<QName> newSubscriptions = Sets.difference(currentSubscriptions, startedQNames); + LOG.debug("Subscriptions changed. New subscriptions: {}", newSubscriptions); + final Sets.SetView<QName> deletedSubscriptions = Sets.difference(startedQNames, currentSubscriptions); + LOG.debug("Subscriptions changed. Deleted subscriptions: {}", deletedSubscriptions); + + newSubscriptions.stream().forEach(newSub -> { + if(!registry.getNotificationQNameToProducer().containsKey(newSub)) { + return; + } + final ManagedNotificationProducer producer = registry.getNotificationQNameToProducer().get(newSub); + if(alreadyStartedProducers.contains(producer)) { + return; + } + LOG.debug("Starting notification producer: {}", producer); + producer.start(collector); + alreadyStartedProducers.add(producer); + }); + + deletedSubscriptions.stream().forEach(newSub -> { + checkState(registry.getNotificationQNameToProducer().containsKey(newSub)); + final ManagedNotificationProducer producer = registry.getNotificationQNameToProducer().get(newSub); + checkState(alreadyStartedProducers.contains(producer)); + LOG.debug("Stopping notification producer: {}", producer); + producer.stop(); + alreadyStartedProducers.remove(producer); + }); + + } + + private Set<QName> getStartedQNames(final Set<ManagedNotificationProducer> alreadyStartedProducers) { + return alreadyStartedProducers.stream() + .flatMap(p -> registry.getNotificationProducerQNames().get(p).stream()) + .collect(Collectors.toSet()); + } + + @Override + public synchronized void close() throws Exception { + LOG.trace("Closing"); + subscriptionListener.close(); + // Stop all producers + LOG.debug("Stopping all producers: {}", alreadyStartedProducers); + alreadyStartedProducers.forEach(ManagedNotificationProducer::stop); + alreadyStartedProducers.clear(); + } +} diff --git a/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModule.java b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModule.java new file mode 100644 index 000000000..9a9c7def0 --- /dev/null +++ b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModule.java @@ -0,0 +1,27 @@ +package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601; + +import org.opendaylight.controller.config.api.JmxAttributeValidationException; +import org.opendaylight.controller.md.sal.dom.broker.impl.DOMNotificationRouter; + +public class HoneycombDomNotificationServiceModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.AbstractHoneycombDomNotificationServiceModule { + public HoneycombDomNotificationServiceModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + super(identifier, dependencyResolver); + } + + public HoneycombDomNotificationServiceModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.HoneycombDomNotificationServiceModule oldModule, java.lang.AutoCloseable oldInstance) { + super(identifier, dependencyResolver, oldModule, oldInstance); + } + + @Override + public void customValidation() { + JmxAttributeValidationException.checkCondition(getQueueDepth() > 0, "Queue depth must be > 0", queueDepthJmxAttribute); + } + + @Override + public java.lang.AutoCloseable createInstance() { + // Create DOMNotificationRouter to do the heavy lifting for HoneycombNotificationCollector + // It creates executor internally + return DOMNotificationRouter.create(getQueueDepth()); + } + +} diff --git a/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModuleFactory.java b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModuleFactory.java new file mode 100644 index 000000000..d3603acb3 --- /dev/null +++ b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModuleFactory.java @@ -0,0 +1,13 @@ +/* +* Generated file +* +* Generated from: yang module name: notification-impl yang module local name: honeycomb-dom-notification-service +* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator +* Generated at: Wed Jun 08 09:49:08 CEST 2016 +* +* Do not modify this file unless it is present under src/main directory +*/ +package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601; +public class HoneycombDomNotificationServiceModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.AbstractHoneycombDomNotificationServiceModuleFactory { + +} diff --git a/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModule.java b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModule.java new file mode 100644 index 000000000..4a9440cbc --- /dev/null +++ b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModule.java @@ -0,0 +1,93 @@ +package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601; + +import io.fd.honeycomb.v3po.notification.NotificationCollector; +import io.fd.honeycomb.v3po.notification.NotificationProducer; +import io.fd.honeycomb.v3po.notification.impl.HoneycombNotificationCollector; +import io.fd.honeycomb.v3po.notification.impl.NotificationProducerRegistry; +import io.fd.honeycomb.v3po.notification.impl.NotificationProducerTracker; +import java.util.Collection; +import org.opendaylight.controller.md.sal.binding.impl.BindingDOMNotificationPublishServiceAdapter; +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; +import org.opendaylight.controller.md.sal.dom.broker.impl.DOMNotificationRouter; +import org.opendaylight.yangtools.yang.binding.Notification; + +public class HoneycombNotificationManagerModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.AbstractHoneycombNotificationManagerModule { + + public HoneycombNotificationManagerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + super(identifier, dependencyResolver); + } + + public HoneycombNotificationManagerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.HoneycombNotificationManagerModule oldModule, java.lang.AutoCloseable oldInstance) { + super(identifier, dependencyResolver, oldModule, oldInstance); + } + + @Override + public void customValidation() { + // add custom validation form module attributes here. + } + + @Override + public java.lang.AutoCloseable createInstance() { + final DOMNotificationRouter notificationRouter = getDomNotificationServiceDependency(); + + // Create the registry to keep track of what's registered + final NotificationProducerRegistry notificationProducerRegistry = + new NotificationProducerRegistry(getNotificationProducersDependency()); + + // Create BA version of notification service (implementation is free from ODL) + final BindingToNormalizedNodeCodec codec = getRuntimeMappingCodecDependency(); + final BindingDOMNotificationPublishServiceAdapter bindingDOMNotificationPublishServiceAdapter = + new BindingDOMNotificationPublishServiceAdapter(codec, notificationRouter); + + // Create Collector on top of BA notification service and registry + final HoneycombNotificationCollector honeycombNotificationCollector = + new HoneycombNotificationCollector(bindingDOMNotificationPublishServiceAdapter, notificationProducerRegistry); + + // Create tracker, responsible for starting and stopping registered notification producers whenever necessary + final NotificationProducerTracker notificationProducerTracker = + new NotificationProducerTracker(notificationProducerRegistry, honeycombNotificationCollector, + notificationRouter); + + // TODO wire with restconf + // DOMNotificationService is already provided by DOMBroker injected into RESTCONF, however RESTCONF + // only supports data-change notification, nothing else. So currently its impossible. + + return new CloseableCollector(honeycombNotificationCollector, () -> { + // Close all resources in order opposite to instantiation + notificationProducerTracker.close(); + honeycombNotificationCollector.close(); + bindingDOMNotificationPublishServiceAdapter.close(); + // notificationProducerRegistry; no close, it's just a collection + }); + } + + /** + * NotificationCollector wrapper in which close method execution can be injected + */ + private class CloseableCollector implements AutoCloseable, NotificationCollector, NotificationProducer { + + private final HoneycombNotificationCollector honeycombNotificationCollector; + private final AutoCloseable resources; + + CloseableCollector(final HoneycombNotificationCollector honeycombNotificationCollector, + final AutoCloseable resources) { + this.honeycombNotificationCollector = honeycombNotificationCollector; + this.resources = resources; + } + + @Override + public void close() throws Exception { + resources.close(); + } + + @Override + public void onNotification(final Notification notification) { + honeycombNotificationCollector.onNotification(notification); + } + + @Override + public Collection<Class<? extends Notification>> getNotificationTypes() { + return honeycombNotificationCollector.getNotificationTypes(); + } + } +} diff --git a/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModuleFactory.java b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModuleFactory.java new file mode 100644 index 000000000..b12b70017 --- /dev/null +++ b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModuleFactory.java @@ -0,0 +1,13 @@ +/* +* Generated file +* +* Generated from: yang module name: notification-impl yang module local name: honeycomb-notification-manager +* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator +* Generated at: Wed Jun 01 16:08:01 CEST 2016 +* +* Do not modify this file unless it is present under src/main directory +*/ +package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601; +public class HoneycombNotificationManagerModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.AbstractHoneycombNotificationManagerModuleFactory { + +} diff --git a/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModule.java b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModule.java new file mode 100644 index 000000000..4d85d64c5 --- /dev/null +++ b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModule.java @@ -0,0 +1,157 @@ +package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601; + +import com.google.common.annotations.VisibleForTesting; +import io.fd.honeycomb.v3po.notification.NotificationCollector; +import io.fd.honeycomb.v3po.notification.impl.NotificationProducerRegistry; +import java.io.IOException; +import java.util.Set; +import java.util.stream.Collectors; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import javax.xml.transform.dom.DOMResult; +import org.opendaylight.controller.config.api.JmxAttributeValidationException; +import org.opendaylight.controller.config.util.xml.XmlUtil; +import org.opendaylight.controller.md.sal.dom.api.DOMNotification; +import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener; +import org.opendaylight.controller.sal.core.api.model.SchemaService; +import org.opendaylight.netconf.notifications.NetconfNotification; +import org.opendaylight.netconf.notifications.NotificationPublisherRegistration; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.StreamNameType; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netmod.notification.rev080714.netconf.streams.StreamBuilder; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter; +import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public class HoneycombNotificationToNetconfTranslatorModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.AbstractHoneycombNotificationToNetconfTranslatorModule { + + private static final Logger LOG = LoggerFactory.getLogger(HoneycombNotificationToNetconfTranslatorModule.class); + + public HoneycombNotificationToNetconfTranslatorModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + super(identifier, dependencyResolver); + } + + public HoneycombNotificationToNetconfTranslatorModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.HoneycombNotificationToNetconfTranslatorModule oldModule, java.lang.AutoCloseable oldInstance) { + super(identifier, dependencyResolver, oldModule, oldInstance); + } + + @Override + public void customValidation() { + JmxAttributeValidationException.checkCondition(!getNetconfStreamName().isEmpty(), + "Stream name cannot be empty", netconfStreamNameJmxAttribute); + JmxAttributeValidationException.checkCondition(!getNetconfStreamDescription().isEmpty(), + "Stream description cannot be empty", netconfStreamDescriptionJmxAttribute); + } + + @Override + public java.lang.AutoCloseable createInstance() { + final SchemaService schemaService = getSchemaServiceDependency(); + final StreamNameType streamType = new StreamNameType(getNetconfStreamName()); + final NotificationCollector hcNotificationCollector = getHoneycombNotificationCollectorDependency(); + + // Register as NETCONF notification publisher under configured name + final NotificationPublisherRegistration netconfNotificationProducerReg = + getNetconfNotificationCollectorDependency().registerNotificationPublisher(new StreamBuilder() + .setName(streamType) + .setReplaySupport(false) + .setDescription(getNetconfStreamDescription()).build()); + + // Notification Translator, get notification from HC producers and put into NETCONF notification collector + final DOMNotificationListener domNotificationListener = + notification -> { + LOG.debug("Propagating notification: {} into NETCONF", notification.getType()); + netconfNotificationProducerReg.onNotification(streamType, notificationToXml(notification, schemaService.getGlobalContext())); + }; + + // NotificationManager is used to provide list of available notifications (which are all of the notifications registered) + // TODO make available notifications configurable here so that any number of notification streams for NETCONF + // can be configured on top of a single notification manager + LOG.debug("Current notifications to be exposed over NETCONF: {}", hcNotificationCollector.getNotificationTypes()); + final Set<SchemaPath> currentNotificationSchemaPaths = hcNotificationCollector.getNotificationTypes() + .stream() + .map(NotificationProducerRegistry::getQName) + .map(qName -> SchemaPath.create(true, qName)) + .collect(Collectors.toSet()); + + // Register as listener to HC's DOM notification service + // TODO This should only be triggered when NETCONF notifications are activated + // Because this way we actually start all notification producers + // final Collection<QName> notificationQNames = + final ListenerRegistration<DOMNotificationListener> domNotificationListenerReg = getDomNotificationServiceDependency() + .registerNotificationListener(domNotificationListener, currentNotificationSchemaPaths); + + LOG.info("Exposing NETCONF notification stream: {}", streamType.getValue()); + return () -> { + domNotificationListenerReg.close(); + netconfNotificationProducerReg.close(); + }; + } + + @VisibleForTesting + static NetconfNotification notificationToXml(final DOMNotification domNotification, final SchemaContext ctx) { + LOG.trace("Transforming notification: {} into XML", domNotification.getType()); + + final SchemaPath type = domNotification.getType(); + final QName notificationQName = type.getLastComponent(); + final DOMResult result = prepareDomResultForRpcRequest(notificationQName); + + try { + writeNormalizedRpc(domNotification, result, type, ctx); + } catch (final XMLStreamException | IOException | IllegalStateException e) { + LOG.warn("Unable to transform notification: {} into XML", domNotification.getType(), e); + throw new IllegalArgumentException("Unable to serialize " + type, e); + } + + final Document node = result.getNode().getOwnerDocument(); + return new NetconfNotification(node); + } + + private static DOMResult prepareDomResultForRpcRequest(final QName notificationQName) { + final Document document = XmlUtil.newDocument(); + final Element notificationElement = + document.createElementNS(notificationQName.getNamespace().toString(), notificationQName.getLocalName()); + document.appendChild(notificationElement); + return new DOMResult(notificationElement); + } + + private static final XMLOutputFactory XML_FACTORY; + + static { + XML_FACTORY = XMLOutputFactory.newFactory(); + XML_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, false); + } + + private static void writeNormalizedRpc(final DOMNotification normalized, final DOMResult result, + final SchemaPath schemaPath, final SchemaContext baseNetconfCtx) + throws IOException, XMLStreamException { + final XMLStreamWriter writer = XML_FACTORY.createXMLStreamWriter(result); + try { + try (final NormalizedNodeStreamWriter normalizedNodeStreamWriter = + XMLStreamNormalizedNodeStreamWriter.create(writer, baseNetconfCtx, schemaPath)) { + try (final NormalizedNodeWriter normalizedNodeWriter = + NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter)) { + for (DataContainerChild<?, ?> dataContainerChild : normalized.getBody().getValue()) { + normalizedNodeWriter.write(dataContainerChild); + } + normalizedNodeWriter.flush(); + } + } + } finally { + try { + writer.close(); + } catch (final Exception e) { + LOG.warn("Unable to close resource properly. Ignoring", e); + } + } + } + +} diff --git a/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModuleFactory.java b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModuleFactory.java new file mode 100644 index 000000000..336223040 --- /dev/null +++ b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModuleFactory.java @@ -0,0 +1,13 @@ +/* +* Generated file +* +* Generated from: yang module name: notification-impl yang module local name: honeycomb-notification-to-netconf-translator +* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator +* Generated at: Thu Jun 02 14:39:23 CEST 2016 +* +* Do not modify this file unless it is present under src/main directory +*/ +package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601; +public class HoneycombNotificationToNetconfTranslatorModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.AbstractHoneycombNotificationToNetconfTranslatorModuleFactory { + +} |