Improve AbstractQueryParams XML parsing
[netconf.git] / restconf / sal-rest-connector / 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 java.io.StringReader;
11 import java.util.Date;
12 import javax.xml.XMLConstants;
13 import javax.xml.parsers.DocumentBuilderFactory;
14 import javax.xml.parsers.ParserConfigurationException;
15 import javax.xml.xpath.XPath;
16 import javax.xml.xpath.XPathConstants;
17 import javax.xml.xpath.XPathFactory;
18 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
19 import org.w3c.dom.Document;
20 import org.xml.sax.InputSource;
21
22 /**
23  * Features of query parameters part of both notifications
24  *
25  */
26 abstract class AbstractQueryParams extends AbstractNotificationsData {
27     // FIXME: BUG-7956: switch to using UntrustedXML
28     private static final DocumentBuilderFactory DBF;
29     static {
30         final DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
31         f.setCoalescing(true);
32         f.setExpandEntityReferences(false);
33         f.setIgnoringElementContentWhitespace(true);
34         f.setIgnoringComments(true);
35         f.setXIncludeAware(false);
36         try {
37             f.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
38             f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
39             f.setFeature("http://xml.org/sax/features/external-general-entities", false);
40             f.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
41         } catch (final ParserConfigurationException e) {
42             throw new ExceptionInInitializerError(e);
43         }
44         DBF = f;
45     }
46
47     // FIXME: these should be final
48     private Date start = null;
49     private Date stop = null;
50     private String filter = null;
51
52     /**
53      * Set query parameters for listener
54      *
55      * @param start
56      *            - start-time of getting notification
57      * @param stop
58      *            - stop-time of getting notification
59      * @param filter
60      *            - indicate which subset of all possible events are of interest
61      */
62     public void setQueryParams(final Date start, final Date stop, final String filter) {
63         this.start = start;
64         this.stop = stop;
65         this.filter = filter;
66     }
67
68     /**
69      * Checking query parameters on specific notification
70      *
71      * @param xml
72      *            - data of notification
73      * @param listener
74      *            - listener of notification
75      * @return true if notification meets the requirements of query parameters,
76      *         false otherwise
77      */
78     protected <T extends BaseListenerInterface> boolean checkQueryParams(final String xml, final T listener) {
79         final Date now = new Date();
80         if (this.stop != null) {
81             if ((this.start.compareTo(now) < 0) && (this.stop.compareTo(now) > 0)) {
82                 return checkFilter(xml);
83             }
84             if (this.stop.compareTo(now) < 0) {
85                 try {
86                     listener.close();
87                 } catch (final Exception e) {
88                     throw new RestconfDocumentedException("Problem with unregister listener." + e);
89                 }
90             }
91         } else if (this.start != null) {
92             if (this.start.compareTo(now) < 0) {
93                 this.start = null;
94                 return checkFilter(xml);
95             }
96         } else {
97             return checkFilter(xml);
98         }
99         return false;
100     }
101
102     /**
103      * Check if is filter used and then prepare and post data do client
104      *
105      * @param change
106      *            - data of notification
107      */
108     private boolean checkFilter(final String xml) {
109         if (this.filter == null) {
110             return true;
111         }
112
113         try {
114             return parseFilterParam(xml);
115         } catch (final Exception e) {
116             throw new RestconfDocumentedException("Problem while parsing filter.", e);
117         }
118     }
119
120     /**
121      * Parse and evaluate filter value by xml
122      *
123      * @return true or false - depends on filter expression and data of
124      *         notifiaction
125      * @throws Exception
126      */
127     private boolean parseFilterParam(final String xml) throws Exception {
128         final Document docOfXml = DBF.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
129         final XPath xPath = XPathFactory.newInstance().newXPath();
130         // FIXME: BUG-7956: xPath.setNamespaceContext(nsContext);
131         return (boolean) xPath.compile(this.filter).evaluate(docOfXml, XPathConstants.BOOLEAN);
132     }
133 }