469764628cfe452c39b4a6afd8959a9df3eea232
[netconf.git] / restconf / restconf-nb-bierman02 / src / main / java / org / opendaylight / netconf / sal / streams / listeners / AbstractQueryParams.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.netconf.sal.streams.listeners;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.VisibleForTesting;
13 import java.io.StringReader;
14 import java.time.Instant;
15 import java.util.Optional;
16 import javax.xml.XMLConstants;
17 import javax.xml.parsers.DocumentBuilderFactory;
18 import javax.xml.parsers.ParserConfigurationException;
19 import javax.xml.xpath.XPath;
20 import javax.xml.xpath.XPathConstants;
21 import javax.xml.xpath.XPathFactory;
22 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
23 import org.w3c.dom.Document;
24 import org.xml.sax.InputSource;
25
26 /**
27  * Features of query parameters part of both notifications.
28  *
29  */
30 abstract class AbstractQueryParams extends AbstractNotificationsData {
31     // FIXME: BUG-7956: switch to using UntrustedXML
32     private static final DocumentBuilderFactory DBF;
33
34     static {
35         final DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
36         f.setCoalescing(true);
37         f.setExpandEntityReferences(false);
38         f.setIgnoringElementContentWhitespace(true);
39         f.setIgnoringComments(true);
40         f.setXIncludeAware(false);
41         try {
42             f.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
43             f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
44             f.setFeature("http://xml.org/sax/features/external-general-entities", false);
45             f.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
46         } catch (final ParserConfigurationException e) {
47             throw new ExceptionInInitializerError(e);
48         }
49         DBF = f;
50     }
51
52     // FIXME: these should be final
53     private Instant start = null;
54     private Instant stop = null;
55     private String filter = null;
56     private boolean leafNodesOnly = false;
57     private boolean skipNotificationData = false;
58
59     @VisibleForTesting
60     public final Instant getStart() {
61         return start;
62     }
63
64     /**
65      * Set query parameters for listener.
66      *
67      * @param start
68      *            start-time of getting notification
69      * @param stop
70      *            stop-time of getting notification
71      * @param filter
72      *            indicate which subset of all possible events are of interest
73      * @param leafNodesOnly
74      *            if true, notifications will contain changes to leaf nodes only
75      * @param skipNotificationData
76      *            if true, notification will not contain changed data
77      */
78     @SuppressWarnings("checkstyle:hiddenField")
79     public void setQueryParams(final Instant start, final Optional<Instant> stop, final Optional<String> filter,
80                                final boolean leafNodesOnly, final boolean skipNotificationData) {
81         this.start = requireNonNull(start);
82         this.stop = stop.orElse(null);
83         this.filter = filter.orElse(null);
84         this.leafNodesOnly = leafNodesOnly;
85         this.skipNotificationData = skipNotificationData;
86     }
87
88     /**
89      * Check whether this query should only notify about leaf node changes.
90      *
91      * @return true if this query should only notify about leaf node changes
92      */
93     public boolean getLeafNodesOnly() {
94         return leafNodesOnly;
95     }
96
97     /**
98      * Check whether this query should notify changes without data.
99      *
100      * @return true if this query should notify about changes with  data
101      */
102     public boolean isSkipNotificationData() {
103         return skipNotificationData;
104     }
105
106     @SuppressWarnings("checkstyle:IllegalCatch")
107     <T extends BaseListenerInterface> boolean checkStartStop(final Instant now, final T listener) {
108         if (this.stop != null) {
109             if (this.start.compareTo(now) < 0 && this.stop.compareTo(now) > 0) {
110                 return true;
111             }
112             if (this.stop.compareTo(now) < 0) {
113                 try {
114                     listener.close();
115                 } catch (final Exception e) {
116                     throw new RestconfDocumentedException("Problem with unregister listener." + e);
117                 }
118             }
119         } else if (this.start != null) {
120             if (this.start.compareTo(now) < 0) {
121                 this.start = null;
122                 return true;
123             }
124         } else {
125             return true;
126         }
127         return false;
128     }
129
130     /**
131      * Check if is filter used and then prepare and post data do client.
132      *
133      * @param xml   data of notification
134      */
135     @SuppressWarnings("checkstyle:IllegalCatch")
136     boolean checkFilter(final String xml) {
137         if (this.filter == null) {
138             return true;
139         }
140
141         try {
142             return parseFilterParam(xml);
143         } catch (final Exception e) {
144             throw new RestconfDocumentedException("Problem while parsing filter.", e);
145         }
146     }
147
148     /**
149      * Parse and evaluate filter value by xml.
150      *
151      * @return true or false - depends on filter expression and data of
152      *         notifiaction
153      * @throws Exception if operation fails
154      */
155     private boolean parseFilterParam(final String xml) throws Exception {
156         final Document docOfXml = DBF.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
157         final XPath xPath = XPathFactory.newInstance().newXPath();
158         // FIXME: BUG-7956: xPath.setNamespaceContext(nsContext);
159         return (boolean) xPath.compile(this.filter).evaluate(docOfXml, XPathConstants.BOOLEAN);
160     }
161 }