Clean up namespace handling
[netconf.git] / restconf / restconf-nb / src / main / java / org / opendaylight / restconf / nb / rfc8040 / streams / listeners / AbstractNotificationsData.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.restconf.nb.rfc8040.streams.listeners;
9
10 import com.google.common.util.concurrent.ListenableFuture;
11 import java.io.ByteArrayOutputStream;
12 import java.io.IOException;
13 import java.nio.charset.StandardCharsets;
14 import java.time.Instant;
15 import java.time.OffsetDateTime;
16 import java.time.ZoneId;
17 import java.time.format.DateTimeFormatter;
18 import javax.xml.stream.XMLOutputFactory;
19 import javax.xml.stream.XMLStreamException;
20 import javax.xml.stream.XMLStreamWriter;
21 import javax.xml.transform.OutputKeys;
22 import javax.xml.transform.Transformer;
23 import javax.xml.transform.TransformerException;
24 import javax.xml.transform.TransformerFactory;
25 import javax.xml.transform.dom.DOMResult;
26 import javax.xml.transform.dom.DOMSource;
27 import javax.xml.transform.stream.StreamResult;
28 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
29 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
30 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
31 import org.opendaylight.restconf.nb.rfc8040.Rfc8040;
32 import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
33 import org.opendaylight.yangtools.util.xml.UntrustedXML;
34 import org.opendaylight.yangtools.yang.common.QName;
35 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
37 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
38 import org.opendaylight.yangtools.yang.data.codec.xml.XMLStreamNormalizedNodeStreamWriter;
39 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
40 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43 import org.w3c.dom.Document;
44 import org.w3c.dom.Element;
45
46 /**
47  * Abstract class for processing and preparing data.
48  */
49 abstract class AbstractNotificationsData {
50     private static final Logger LOG = LoggerFactory.getLogger(AbstractNotificationsData.class);
51     private static final TransformerFactory TF = TransformerFactory.newInstance();
52     private static final XMLOutputFactory OF = XMLOutputFactory.newInstance();
53
54     private final String localName;
55
56     protected SchemaContextHandler schemaHandler;
57     private DOMDataBroker dataBroker;
58
59     AbstractNotificationsData(final QName lastQName) {
60         localName = lastQName.getLocalName();
61     }
62
63     /**
64      * Data broker for delete data in DS on close().
65      *
66      * @param dataBroker creating new write transaction for delete data on close
67      * @param schemaHandler for formatting notifications
68      */
69     @SuppressWarnings("checkstyle:hiddenField")
70     // FIXME: this is pure lifecycle nightmare just because ...
71     public void setCloseVars(final DOMDataBroker dataBroker, final SchemaContextHandler schemaHandler) {
72         this.dataBroker = dataBroker;
73         this.schemaHandler = schemaHandler;
74     }
75
76     /**
77      * Delete data in DS.
78      */
79     // FIXME: here we touch datastore, which probably should be done by whoever instantiated us or created the resource,
80     //        or they should be giving us the transaction
81     protected ListenableFuture<?> deleteDataInDS() {
82         final DOMDataTreeWriteTransaction wTx = dataBroker.newWriteOnlyTransaction();
83         wTx.delete(LogicalDatastoreType.OPERATIONAL, Rfc8040.restconfStateStreamPath(localName));
84         return wTx.commit();
85     }
86
87     /**
88      * Formats data specified by RFC3339.
89      *
90      * @param now time stamp
91      * @return Data specified by RFC3339.
92      */
93     protected static String toRFC3339(final Instant now) {
94         return DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(OffsetDateTime.ofInstant(now, ZoneId.systemDefault()));
95     }
96
97     /**
98      * Creates {@link Document} document.
99      *
100      * @return {@link Document} document.
101      */
102     protected static Document createDocument() {
103         return UntrustedXML.newDocumentBuilder().newDocument();
104     }
105
106     /**
107      * Write normalized node to {@link DOMResult}.
108      *
109      * @param normalized
110      *            data
111      * @param context
112      *            actual schema context
113      * @param schemaPath
114      *            schema path of data
115      * @return {@link DOMResult}
116      */
117     protected DOMResult writeNormalizedNode(final NormalizedNode normalized, final EffectiveModelContext context,
118             final SchemaPath schemaPath) throws IOException, XMLStreamException {
119         final Document doc = UntrustedXML.newDocumentBuilder().newDocument();
120         final DOMResult result = new DOMResult(doc);
121         NormalizedNodeWriter normalizedNodeWriter = null;
122         NormalizedNodeStreamWriter normalizedNodeStreamWriter = null;
123         XMLStreamWriter writer = null;
124
125         try {
126             writer = OF.createXMLStreamWriter(result);
127             normalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(writer, context, schemaPath);
128             normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter);
129
130             normalizedNodeWriter.write(normalized);
131
132             normalizedNodeWriter.flush();
133         } finally {
134             if (normalizedNodeWriter != null) {
135                 normalizedNodeWriter.close();
136             }
137             if (normalizedNodeStreamWriter != null) {
138                 normalizedNodeStreamWriter.close();
139             }
140             if (writer != null) {
141                 writer.close();
142             }
143         }
144
145         return result;
146     }
147
148     /**
149      * Generating base element of every notification.
150      *
151      * @param doc
152      *            base {@link Document}
153      * @return element of {@link Document}
154      */
155     protected Element basePartDoc(final Document doc) {
156         final Element notificationElement = doc.createElementNS(
157             NotificationFormatter.NOTIFICATION_NAMESPACE, NotificationFormatter.NOTIFICATION_ELEMENT);
158
159         doc.appendChild(notificationElement);
160
161         final Element eventTimeElement = doc.createElement("eventTime");
162         eventTimeElement.setTextContent(toRFC3339(Instant.now()));
163         notificationElement.appendChild(eventTimeElement);
164
165         return notificationElement;
166     }
167
168     /**
169      * Generating of {@link Document} transforming to string.
170      *
171      * @param doc
172      *            {@link Document} with data
173      * @return - string from {@link Document}
174      */
175     protected String transformDoc(final Document doc) {
176         final ByteArrayOutputStream out = new ByteArrayOutputStream();
177
178         try {
179             final Transformer transformer = TF.newTransformer();
180             transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
181             transformer.setOutputProperty(OutputKeys.METHOD, "xml");
182             transformer.setOutputProperty(OutputKeys.INDENT, "yes");
183             transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
184             transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
185             transformer.transform(new DOMSource(doc), new StreamResult(out));
186         } catch (final TransformerException e) {
187             // FIXME: this should raise an exception
188             final String msg = "Error during transformation of Document into String";
189             LOG.error(msg, e);
190             return msg;
191         }
192
193         return new String(out.toByteArray(), StandardCharsets.UTF_8);
194     }
195 }