2 * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
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
8 package org.opendaylight.restconf.common.formatters;
10 import java.io.IOException;
11 import java.time.Instant;
12 import java.time.OffsetDateTime;
13 import java.time.ZoneId;
14 import java.time.format.DateTimeFormatter;
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.XPathExpression;
22 import javax.xml.xpath.XPathExpressionException;
23 import javax.xml.xpath.XPathFactory;
24 import org.eclipse.jdt.annotation.NonNull;
25 import org.opendaylight.yangtools.concepts.Immutable;
26 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
27 import org.w3c.dom.Document;
29 public abstract class EventFormatter<T> implements Immutable {
30 private static final XPathFactory XPF = XPathFactory.newInstance();
32 // FIXME: NETCONF-369: XPath operates without namespace context, therefore we need an namespace-unaware builder.
33 // Once it is fixed we can use UntrustedXML instead.
34 private static final @NonNull DocumentBuilderFactory DBF;
37 final DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
38 f.setCoalescing(true);
39 f.setExpandEntityReferences(false);
40 f.setIgnoringElementContentWhitespace(true);
41 f.setIgnoringComments(true);
42 f.setXIncludeAware(false);
44 f.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
45 f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
46 f.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
47 f.setFeature("http://xml.org/sax/features/external-general-entities", false);
48 f.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
49 } catch (final ParserConfigurationException e) {
50 throw new ExceptionInInitializerError(e);
55 private final XPathExpression filter;
61 EventFormatter(final String xpathFilter) throws XPathExpressionException {
64 xpath = XPF.newXPath();
66 // FIXME: NETCONF-369: we need to bind the namespace context here and for that we need the SchemaContext
67 filter = xpath.compile(xpathFilter);
70 public final Optional<String> eventData(final EffectiveModelContext schemaContext, final T input, final Instant now,
71 boolean leafNodesOnly, boolean skipData)
73 if (!filterMatches(schemaContext, input, now)) {
74 return Optional.empty();
76 return Optional.of(createText(schemaContext, input, now, leafNodesOnly, skipData));
80 * Export the provided input into the provided document so we can verify whether a filter matches the content.
82 * @param doc the document to fill
83 * @param schemaContext context to use for the export
84 * @param input data to export
85 * @throws IOException if any IOException occurs during export to the document
87 abstract void fillDocument(Document doc, EffectiveModelContext schemaContext, T input) throws IOException;
90 * Format the input data into string representation of the data provided.
92 * @param schemaContext context to use for the export
93 * @param input input data
94 * @param now time the event happened
95 * @param leafNodesOnly option to include only leaves in the result
96 * @param skipData option to skip data in the result, only paths would be included
97 * @return String representation of the formatted data
98 * @throws Exception if the underlying formatters fail to export the data to the requested format
100 abstract String createText(EffectiveModelContext schemaContext, T input, Instant now, boolean leafNodesOnly,
101 boolean skipData) throws Exception;
103 private boolean filterMatches(final EffectiveModelContext schemaContext, final T input, final Instant now)
105 if (filter == null) {
111 doc = DBF.newDocumentBuilder().newDocument();
112 } catch (final ParserConfigurationException e) {
113 throw new IOException("Failed to create a new document", e);
115 fillDocument(doc, schemaContext, input);
119 eval = (Boolean) filter.evaluate(doc, XPathConstants.BOOLEAN);
120 } catch (final XPathExpressionException e) {
121 throw new IllegalStateException("Failed to evaluate expression " + filter, e);
124 return eval.booleanValue();
128 * Formats data specified by RFC3339.
130 * @param now time stamp
131 * @return Data specified by RFC3339.
133 static String toRFC3339(final Instant now) {
134 return DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(OffsetDateTime.ofInstant(now, ZoneId.systemDefault()));