Bug 4883 - implement query parameter - filter
[netconf.git] / restconf / sal-rest-connector / src / main / java / org / opendaylight / netconf / sal / streams / listeners / NotificationListenerAdapter.java
index 42f0996052e022be82289226aa13bd18fe294878..1d32b39e23bb077c59120d7bcd5a78c09db0fe35 100644 (file)
@@ -18,11 +18,14 @@ import io.netty.util.internal.ConcurrentSet;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
+import java.io.StringReader;
 import java.io.UnsupportedEncodingException;
 import java.util.Collection;
 import java.util.Date;
 import java.util.Set;
 import java.util.concurrent.Executors;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.stream.XMLOutputFactory;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
@@ -33,11 +36,15 @@ import javax.xml.transform.TransformerFactory;
 import javax.xml.transform.dom.DOMResult;
 import javax.xml.transform.dom.DOMSource;
 import javax.xml.transform.stream.StreamResult;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFactory;
 import org.json.JSONObject;
 import org.json.XML;
 import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
@@ -55,6 +62,7 @@ import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
+import org.xml.sax.InputSource;
 
 /**
  * {@link NotificationListenerAdapter} is responsible to track events on
@@ -74,6 +82,9 @@ public class NotificationListenerAdapter implements DOMNotificationListener {
 
     private final SchemaPath path;
     private final String outputType;
+    private Date start = null;
+    private Date stop = null;
+    private String filter;
 
     /**
      * Set path of listener and stream name, register event bus.
@@ -98,7 +109,72 @@ public class NotificationListenerAdapter implements DOMNotificationListener {
 
     @Override
     public void onNotification(final DOMNotification notification) {
+        final Date now = new Date();
+        if (this.stop != null) {
+            if ((this.start.compareTo(now) < 0) && (this.stop.compareTo(now) > 0)) {
+                checkFilter(notification);
+            }
+            if (this.stop.compareTo(now) < 0) {
+                try {
+                    this.close();
+                } catch (final Exception e) {
+                    throw new RestconfDocumentedException("Problem with unregister listener." + e);
+                }
+            }
+        } else if (this.start != null) {
+            if (this.start.compareTo(now) < 0) {
+                this.start = null;
+                checkFilter(notification);
+            }
+        } else {
+            checkFilter(notification);
+        }
+    }
+
+    /**
+     * Check if is filter used and then prepare and post data do client
+     *
+     * @param notification
+     *            - data of notification
+     */
+    private void checkFilter(final DOMNotification notification) {
         final String xml = prepareXmlFrom(notification);
+        if (this.filter == null) {
+            prepareAndPostData(xml);
+        } else {
+            try {
+                if (parseFilterParam(xml)) {
+                    prepareAndPostData(xml);
+                }
+            } catch (final Exception e) {
+                throw new RestconfDocumentedException("Problem while parsing filter.", e);
+            }
+        }
+    }
+
+    /**
+     * Parse and evaluate filter value by xml
+     *
+     * @param xml
+     *            - notification data in xml
+     * @return true or false - depends on filter expression and data of
+     *         notifiaction
+     * @throws Exception
+     */
+    private boolean parseFilterParam(final String xml) throws Exception {
+        final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        final DocumentBuilder builder = factory.newDocumentBuilder();
+        final Document docOfXml = builder.parse(new InputSource(new StringReader(xml)));
+        final XPath xPath = XPathFactory.newInstance().newXPath();
+        return (boolean) xPath.compile(this.filter).evaluate(docOfXml, XPathConstants.BOOLEAN);
+    }
+
+    /**
+     * Prepare data of notification and data to client
+     *
+     * @param xml
+     */
+    private void prepareAndPostData(final String xml) {
         final Event event = new Event(EventType.NOTIFY);
         if (this.outputType.equals("JSON")) {
             final JSONObject jsonObject = XML.toJSONObject(xml);
@@ -200,16 +276,17 @@ public class NotificationListenerAdapter implements DOMNotificationListener {
     private String prepareXmlFrom(final DOMNotification notification) {
         final SchemaContext schemaContext = ControllerContext.getInstance().getGlobalSchema();
         final Document doc = ListenerAdapter.createDocument();
-        final Element notificationElement = doc.createElementNS("urn:ietf:params:xml:ns:netconf:notification:1.0",
+        final Element notificationElement =
+                doc.createElementNS("urn:ietf:params:xml:ns:netconf:notification:1.0",
                 "notification");
         doc.appendChild(notificationElement);
 
         final Element eventTimeElement = doc.createElement("eventTime");
         eventTimeElement.setTextContent(ListenerAdapter.toRFC3339(new Date()));
         notificationElement.appendChild(eventTimeElement);
-
+        final String notificationNamespace = notification.getType().getLastComponent().getNamespace().toString();
         final Element notificationEventElement = doc.createElementNS(
-                "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "create-notification-stream");
+                notificationNamespace, "event");
         addValuesToNotificationEventElement(doc, notificationEventElement, notification, schemaContext);
         notificationElement.appendChild(notificationEventElement);
 
@@ -243,9 +320,7 @@ public class NotificationListenerAdapter implements DOMNotificationListener {
             final DOMResult domResult = writeNormalizedNode(body,
                     YangInstanceIdentifier.create(body.getIdentifier()), schemaContext);
             final Node result = doc.importNode(domResult.getNode().getFirstChild(), true);
-            final Element dataElement = doc.createElement("notification");
-            dataElement.appendChild(result);
-            element.appendChild(dataElement);
+            element.appendChild(result);
         } catch (final IOException e) {
             LOG.error("Error in writer ", e);
         } catch (final XMLStreamException e) {
@@ -385,6 +460,22 @@ public class NotificationListenerAdapter implements DOMNotificationListener {
      * Type of the event.
      */
     private enum EventType {
-        REGISTER, DEREGISTER, NOTIFY;
+        REGISTER, DEREGISTER, NOTIFY
+    }
+
+    /**
+     * Set query parameters for listener
+     *
+     * @param start
+     *            - start-time of getting notification
+     * @param stop
+     *            - stop-time of getting notification
+     * @param filter
+     *            - indicate which subset of all possible events are of interest
+     */
+    public void setQueryParams(final Date start, final Date stop, final String filter) {
+        this.start = start;
+        this.stop = stop;
+        this.filter = filter;
     }
 }