Merge "Bug 2517: Catch RuntimeExceptions thrown from the DCL in DataChangeListener"
[controller.git] / opendaylight / md-sal / sal-rest-connector / src / main / java / org / opendaylight / controller / sal / streams / listeners / ListenerAdapter.java
index e526ec13be75fad83ff6c33b95dfe9ba0d809298..8481a9f0c8129f0a90f379dc5ca409d1bfc6b552 100644 (file)
@@ -38,17 +38,18 @@ import javax.xml.transform.TransformerException;
 import javax.xml.transform.TransformerFactory;
 import javax.xml.transform.dom.DOMSource;
 import javax.xml.transform.stream.StreamResult;
-import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
-import org.opendaylight.controller.sal.core.api.data.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
 import org.opendaylight.controller.sal.rest.impl.XmlMapper;
 import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeWithValue;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -59,7 +60,7 @@ import org.w3c.dom.Node;
 /**
  * {@link ListenerAdapter} is responsible to track events, which occurred by changing data in data source.
  */
-public class ListenerAdapter implements DataChangeListener {
+public class ListenerAdapter implements DOMDataChangeListener {
 
     private static final Logger LOG = LoggerFactory.getLogger(ListenerAdapter.class);
     private static final DocumentBuilderFactory DBF = DocumentBuilderFactory.newInstance();
@@ -69,8 +70,8 @@ public class ListenerAdapter implements DataChangeListener {
     private final XmlMapper xmlMapper = new XmlMapper();
     private final SimpleDateFormat rfc3339 = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ssZ");
 
-    private final InstanceIdentifier path;
-    private ListenerRegistration<DataChangeListener> registration;
+    private final YangInstanceIdentifier path;
+    private ListenerRegistration<DOMDataChangeListener> registration;
     private final String streamName;
     private Set<Channel> subscribers = new ConcurrentSet<>();
     private final EventBus eventBus;
@@ -84,7 +85,7 @@ public class ListenerAdapter implements DataChangeListener {
      * @param streamName
      *            The name of the stream.
      */
-    ListenerAdapter(final InstanceIdentifier path, final String streamName) {
+    ListenerAdapter(final YangInstanceIdentifier path, final String streamName) {
         Preconditions.checkNotNull(path);
         Preconditions.checkArgument(streamName != null && !streamName.isEmpty());
         this.path = path;
@@ -95,12 +96,13 @@ public class ListenerAdapter implements DataChangeListener {
     }
 
     @Override
-    public void onDataChanged(final DataChangeEvent<InstanceIdentifier, CompositeNode> change) {
-        if (!change.getCreatedConfigurationData().isEmpty() || !change.getCreatedOperationalData().isEmpty()
-                || !change.getUpdatedConfigurationData().isEmpty() || !change.getUpdatedOperationalData().isEmpty()
-                || !change.getRemovedConfigurationData().isEmpty() || !change.getRemovedOperationalData().isEmpty()) {
-            String xml = prepareXmlFrom(change);
-            Event event = new Event(EventType.NOTIFY);
+    public void onDataChanged(final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change) {
+        // TODO Auto-generated method stub
+
+        if (!change.getCreatedData().isEmpty() || !change.getUpdatedData().isEmpty()
+                || !change.getRemovedPaths().isEmpty()) {
+            final String xml = prepareXmlFrom(change);
+            final Event event = new Event(EventType.NOTIFY);
             event.setData(xml);
             eventBus.post(event);
         }
@@ -113,7 +115,7 @@ public class ListenerAdapter implements DataChangeListener {
         @Subscribe
         public void recordCustomerChange(final Event event) {
             if (event.getType() == EventType.REGISTER) {
-                Channel subscriber = event.getSubscriber();
+                final Channel subscriber = event.getSubscriber();
                 if (!subscribers.contains(subscriber)) {
                     subscribers.add(subscriber);
                 }
@@ -121,7 +123,7 @@ public class ListenerAdapter implements DataChangeListener {
                 subscribers.remove(event.getSubscriber());
                 Notificator.removeListenerIfNoSubscriberExists(ListenerAdapter.this);
             } else if (event.getType() == EventType.NOTIFY) {
-                for (Channel subscriber : subscribers) {
+                for (final Channel subscriber : subscribers) {
                     if (subscriber.isActive()) {
                         LOG.debug("Data are sent to subscriber {}:", subscriber.remoteAddress());
                         subscriber.writeAndFlush(new TextWebSocketFrame(event.getData()));
@@ -216,34 +218,34 @@ public class ListenerAdapter implements DataChangeListener {
      *            DataChangeEvent
      * @return Data in printable form.
      */
-    private String prepareXmlFrom(final DataChangeEvent<InstanceIdentifier, CompositeNode> change) {
-        Document doc = createDocument();
-        Element notificationElement = doc.createElementNS("urn:ietf:params:xml:ns:netconf:notification:1.0",
+    private String prepareXmlFrom(final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change) {
+        final Document doc = createDocument();
+        final Element notificationElement = doc.createElementNS("urn:ietf:params:xml:ns:netconf:notification:1.0",
                 "notification");
         doc.appendChild(notificationElement);
 
-        Element eventTimeElement = doc.createElement("eventTime");
+        final Element eventTimeElement = doc.createElement("eventTime");
         eventTimeElement.setTextContent(toRFC3339(new Date()));
         notificationElement.appendChild(eventTimeElement);
 
-        Element dataChangedNotificationEventElement = doc.createElementNS(
+        final Element dataChangedNotificationEventElement = doc.createElementNS(
                 "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "data-changed-notification");
         addValuesToDataChangedNotificationEventElement(doc, dataChangedNotificationEventElement, change);
         notificationElement.appendChild(dataChangedNotificationEventElement);
 
         try {
-            ByteArrayOutputStream out = new ByteArrayOutputStream();
-            Transformer transformer = FACTORY.newTransformer();
+            final ByteArrayOutputStream out = new ByteArrayOutputStream();
+            final Transformer transformer = FACTORY.newTransformer();
             transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
             transformer.setOutputProperty(OutputKeys.METHOD, "xml");
             transformer.setOutputProperty(OutputKeys.INDENT, "yes");
             transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
             transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
             transformer.transform(new DOMSource(doc), new StreamResult(new OutputStreamWriter(out, Charsets.UTF_8)));
-            byte[] charData = out.toByteArray();
+            final byte[] charData = out.toByteArray();
             return new String(charData, "UTF-8");
         } catch (TransformerException | UnsupportedEncodingException e) {
-            String msg = "Error during transformation of Document into String";
+            final String msg = "Error during transformation of Document into String";
             LOG.error(msg, e);
             return msg;
         }
@@ -262,14 +264,13 @@ public class ListenerAdapter implements DataChangeListener {
 
     /**
      * Creates {@link Document} document.
-     *
      * @return {@link Document} document.
      */
     private Document createDocument() {
         final DocumentBuilder bob;
         try {
             bob = DBF.newDocumentBuilder();
-        } catch (ParserConfigurationException e) {
+        } catch (final ParserConfigurationException e) {
             return null;
         }
         return bob.newDocument();
@@ -287,23 +288,15 @@ public class ListenerAdapter implements DataChangeListener {
      */
     private void addValuesToDataChangedNotificationEventElement(final Document doc,
             final Element dataChangedNotificationEventElement,
-            final DataChangeEvent<InstanceIdentifier, CompositeNode> change) {
-        addValuesFromDataToElement(doc, change.getCreatedConfigurationData(), dataChangedNotificationEventElement,
-                Store.CONFIG, Operation.CREATED);
-        addValuesFromDataToElement(doc, change.getCreatedOperationalData(), dataChangedNotificationEventElement,
-                Store.OPERATION, Operation.CREATED);
-        if (change.getCreatedConfigurationData().isEmpty()) {
-            addValuesFromDataToElement(doc, change.getUpdatedConfigurationData(), dataChangedNotificationEventElement,
-                    Store.CONFIG, Operation.UPDATED);
-        }
-        if (change.getCreatedOperationalData().isEmpty()) {
-            addValuesFromDataToElement(doc, change.getUpdatedOperationalData(), dataChangedNotificationEventElement,
-                    Store.OPERATION, Operation.UPDATED);
+            final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change) {
+        addValuesFromDataToElement(doc, change.getCreatedData().keySet(), dataChangedNotificationEventElement,
+                Operation.CREATED);
+        if (change.getCreatedData().isEmpty()) {
+            addValuesFromDataToElement(doc, change.getUpdatedData().keySet(), dataChangedNotificationEventElement,
+                    Operation.UPDATED);
         }
-        addValuesFromDataToElement(doc, change.getRemovedConfigurationData(), dataChangedNotificationEventElement,
-                Store.CONFIG, Operation.DELETED);
-        addValuesFromDataToElement(doc, change.getRemovedOperationalData(), dataChangedNotificationEventElement,
-                Store.OPERATION, Operation.DELETED);
+        addValuesFromDataToElement(doc, change.getRemovedPaths(), dataChangedNotificationEventElement,
+                Operation.DELETED);
     }
 
     /**
@@ -312,7 +305,7 @@ public class ListenerAdapter implements DataChangeListener {
      * @param doc
      *            {@link Document}
      * @param data
-     *            Set of {@link InstanceIdentifier}.
+     *            Set of {@link YangInstanceIdentifier}.
      * @param element
      *            {@link Element}
      * @param store
@@ -320,13 +313,13 @@ public class ListenerAdapter implements DataChangeListener {
      * @param operation
      *            {@link Operation}
      */
-    private void addValuesFromDataToElement(final Document doc, final Set<InstanceIdentifier> data,
-            final Element element, final Store store, final Operation operation) {
+    private void addValuesFromDataToElement(final Document doc, final Set<YangInstanceIdentifier> data, final Element element,
+            final Operation operation) {
         if (data == null || data.isEmpty()) {
             return;
         }
-        for (InstanceIdentifier path : data) {
-            Node node = createDataChangeEventElement(doc, path, null, store, operation);
+        for (final YangInstanceIdentifier path : data) {
+            final Node node = createDataChangeEventElement(doc, path, null, operation);
             element.appendChild(node);
         }
     }
@@ -337,7 +330,7 @@ public class ListenerAdapter implements DataChangeListener {
      * @param doc
      *            {@link Document}
      * @param data
-     *            Map of {@link InstanceIdentifier} and {@link CompositeNode}.
+     *            Map of {@link YangInstanceIdentifier} and {@link CompositeNode}.
      * @param element
      *            {@link Element}
      * @param store
@@ -345,13 +338,13 @@ public class ListenerAdapter implements DataChangeListener {
      * @param operation
      *            {@link Operation}
      */
-    private void addValuesFromDataToElement(final Document doc, final Map<InstanceIdentifier, CompositeNode> data,
-            final Element element, final Store store, final Operation operation) {
+    private void addValuesFromDataToElement(final Document doc, final Map<YangInstanceIdentifier, CompositeNode> data, final Element element,
+            final Operation operation) {
         if (data == null || data.isEmpty()) {
             return;
         }
-        for (Entry<InstanceIdentifier, CompositeNode> entry : data.entrySet()) {
-            Node node = createDataChangeEventElement(doc, entry.getKey(), entry.getValue(), store, operation);
+        for (final Entry<YangInstanceIdentifier, CompositeNode> entry : data.entrySet()) {
+            final Node node = createDataChangeEventElement(doc, entry.getKey(), entry.getValue(), operation);
             element.appendChild(node);
         }
     }
@@ -371,26 +364,25 @@ public class ListenerAdapter implements DataChangeListener {
      *            {@link Operation}
      * @return {@link Node} node represented by changed event element.
      */
-    private Node createDataChangeEventElement(final Document doc, final InstanceIdentifier path,
-            final CompositeNode data, final Store store, final Operation operation) {
-        Element dataChangeEventElement = doc.createElement("data-change-event");
-
-        Element pathElement = doc.createElement("path");
+    private Node createDataChangeEventElement(final Document doc, final YangInstanceIdentifier path, final CompositeNode data,
+            final Operation operation) {
+        final Element dataChangeEventElement = doc.createElement("data-change-event");
+        final Element pathElement = doc.createElement("path");
         addPathAsValueToElement(path, pathElement);
         dataChangeEventElement.appendChild(pathElement);
 
-        Element storeElement = doc.createElement("store");
-        storeElement.setTextContent(store.value);
-        dataChangeEventElement.appendChild(storeElement);
+        // Element storeElement = doc.createElement("store");
+        // storeElement.setTextContent(store.value);
+        // dataChangeEventElement.appendChild(storeElement);
 
-        Element operationElement = doc.createElement("operation");
+        final Element operationElement = doc.createElement("operation");
         operationElement.setTextContent(operation.value);
         dataChangeEventElement.appendChild(operationElement);
 
         if (data != null) {
-            Element dataElement = doc.createElement("data");
-            Node dataAnyXml = translateToXml(path, data);
-            Node adoptedNode = doc.adoptNode(dataAnyXml);
+            final Element dataElement = doc.createElement("data");
+            final Node dataAnyXml = translateToXml(path, data);
+            final Node adoptedNode = doc.adoptNode(dataAnyXml);
             dataElement.appendChild(adoptedNode);
             dataChangeEventElement.appendChild(dataElement);
         }
@@ -407,8 +399,8 @@ public class ListenerAdapter implements DataChangeListener {
      *            {@link CompositeNode}
      * @return Data in XML format.
      */
-    private Node translateToXml(final InstanceIdentifier path, final CompositeNode data) {
-        DataNodeContainer schemaNode = ControllerContext.getInstance().getDataNodeContainerFor(path);
+    private Node translateToXml(final YangInstanceIdentifier path, final CompositeNode data) {
+        final DataNodeContainer schemaNode = ControllerContext.getInstance().getDataNodeContainerFor(path);
         if (schemaNode == null) {
             LOG.info(
                     "Path '{}' contains node with unsupported type (supported type is Container or List) or some node was not found.",
@@ -416,9 +408,9 @@ public class ListenerAdapter implements DataChangeListener {
             return null;
         }
         try {
-            Document xml = xmlMapper.write(data, schemaNode);
+            final Document xml = xmlMapper.write(data, schemaNode);
             return xml.getFirstChild();
-        } catch (UnsupportedDataTypeException e) {
+        } catch (final UnsupportedDataTypeException e) {
             LOG.error("Error occured during translation of notification to XML.", e);
             return null;
         }
@@ -432,20 +424,20 @@ public class ListenerAdapter implements DataChangeListener {
      * @param element
      *            {@link Element}
      */
-    private void addPathAsValueToElement(final InstanceIdentifier path, final Element element) {
+    private void addPathAsValueToElement(final YangInstanceIdentifier path, final Element element) {
         // Map< key = namespace, value = prefix>
-        Map<String, String> prefixes = new HashMap<>();
-        InstanceIdentifier instanceIdentifier = path;
-        StringBuilder textContent = new StringBuilder();
+        final Map<String, String> prefixes = new HashMap<>();
+        final YangInstanceIdentifier instanceIdentifier = path;
+        final StringBuilder textContent = new StringBuilder();
 
         // FIXME: BUG-1281: this is duplicated code from yangtools (BUG-1275)
-        for (PathArgument pathArgument : instanceIdentifier.getPathArguments()) {
+        for (final PathArgument pathArgument : instanceIdentifier.getPathArguments()) {
             textContent.append("/");
             writeIdentifierWithNamespacePrefix(element, textContent, pathArgument.getNodeType(), prefixes);
             if (pathArgument instanceof NodeIdentifierWithPredicates) {
-                Map<QName, Object> predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues();
-                for (QName keyValue : predicates.keySet()) {
-                    String predicateValue = String.valueOf(predicates.get(keyValue));
+                final Map<QName, Object> predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues();
+                for (final QName keyValue : predicates.keySet()) {
+                    final String predicateValue = String.valueOf(predicates.get(keyValue));
                     textContent.append("[");
                     writeIdentifierWithNamespacePrefix(element, textContent, keyValue, prefixes);
                     textContent.append("='");
@@ -477,13 +469,10 @@ public class ListenerAdapter implements DataChangeListener {
      */
     private static void writeIdentifierWithNamespacePrefix(final Element element, final StringBuilder textContent,
             final QName qName, final Map<String, String> prefixes) {
-        String namespace = qName.getNamespace().toString();
+        final String namespace = qName.getNamespace().toString();
         String prefix = prefixes.get(namespace);
         if (prefix == null) {
-            prefix = qName.getPrefix();
-            if (prefix == null || prefix.isEmpty() || prefixes.containsValue(prefix)) {
-                prefix = generateNewPrefix(prefixes.values());
-            }
+            prefix = generateNewPrefix(prefixes.values());
         }
 
         element.setAttribute("xmlns:" + prefix, namespace);
@@ -503,11 +492,11 @@ public class ListenerAdapter implements DataChangeListener {
      */
     private static String generateNewPrefix(final Collection<String> prefixes) {
         StringBuilder result = null;
-        Random random = new Random();
+        final Random random = new Random();
         do {
             result = new StringBuilder();
             for (int i = 0; i < 4; i++) {
-                int randomNumber = 0x61 + (Math.abs(random.nextInt()) % 26);
+                final int randomNumber = 0x61 + (Math.abs(random.nextInt()) % 26);
                 result.append(Character.toChars(randomNumber));
             }
         } while (prefixes.contains(result.toString()));
@@ -520,7 +509,7 @@ public class ListenerAdapter implements DataChangeListener {
      *
      * @return Path pointed to data in data store.
      */
-    public InstanceIdentifier getPath() {
+    public YangInstanceIdentifier getPath() {
         return path;
     }
 
@@ -530,7 +519,7 @@ public class ListenerAdapter implements DataChangeListener {
      * @param registration
      *            ListenerRegistration<DataChangeListener>
      */
-    public void setRegistration(final ListenerRegistration<DataChangeListener> registration) {
+    public void setRegistration(final ListenerRegistration<DOMDataChangeListener> registration) {
         this.registration = registration;
     }
 
@@ -573,7 +562,7 @@ public class ListenerAdapter implements DataChangeListener {
         if (!subscriber.isActive()) {
             LOG.debug("Channel is not active between websocket server and subscriber {}" + subscriber.remoteAddress());
         }
-        Event event = new Event(EventType.REGISTER);
+        final Event event = new Event(EventType.REGISTER);
         event.setSubscriber(subscriber);
         eventBus.post(event);
     }
@@ -586,7 +575,7 @@ public class ListenerAdapter implements DataChangeListener {
      */
     public void removeSubscriber(final Channel subscriber) {
         LOG.debug("Subscriber {} is removed.", subscriber.remoteAddress());
-        Event event = new Event(EventType.DEREGISTER);
+        final Event event = new Event(EventType.DEREGISTER);
         event.setSubscriber(subscriber);
         eventBus.post(event);
     }