Initial source code
[oam/tr069-adapter.git] / mapper / src / main / java / org / commscope / tr069adapter / mapper / util / NetconfToTr069MapperUtil.java
1 /*\r
2  * ============LICENSE_START========================================================================\r
3  * ONAP : tr-069-adapter\r
4  * =================================================================================================\r
5  * Copyright (C) 2020 CommScope Inc Intellectual Property.\r
6  * =================================================================================================\r
7  * This tr-069-adapter software file is distributed by CommScope Inc under the Apache License,\r
8  * Version 2.0 (the "License"); you may not use this file except in compliance with the License. You\r
9  * may obtain a copy of the License at\r
10  *\r
11  * http://www.apache.org/licenses/LICENSE-2.0\r
12  *\r
13  * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\r
14  * either express or implied. See the License for the specific language governing permissions and\r
15  * limitations under the License.\r
16  * ===============LICENSE_END=======================================================================\r
17  */\r
18 \r
19 package org.commscope.tr069adapter.mapper.util;\r
20 \r
21 import java.io.Serializable;\r
22 import java.io.StringReader;\r
23 import java.io.StringWriter;\r
24 import java.util.ArrayList;\r
25 import java.util.Collections;\r
26 import java.util.Comparator;\r
27 import java.util.HashMap;\r
28 import java.util.List;\r
29 import java.util.Map;\r
30 import java.util.StringTokenizer;\r
31 \r
32 import javax.xml.XMLConstants;\r
33 import javax.xml.parsers.DocumentBuilder;\r
34 import javax.xml.parsers.DocumentBuilderFactory;\r
35 import javax.xml.parsers.ParserConfigurationException;\r
36 import javax.xml.transform.Transformer;\r
37 import javax.xml.transform.TransformerFactory;\r
38 import javax.xml.transform.dom.DOMSource;\r
39 import javax.xml.transform.stream.StreamResult;\r
40 \r
41 import org.commscope.tr069adapter.acs.common.DeviceRPCRequest;\r
42 import org.commscope.tr069adapter.acs.common.DeviceRPCResponse;\r
43 import org.commscope.tr069adapter.acs.common.OperationOptions;\r
44 import org.commscope.tr069adapter.acs.common.ParameterDTO;\r
45 import org.commscope.tr069adapter.acs.common.dto.TR069DeviceDetails;\r
46 import org.commscope.tr069adapter.acs.common.dto.TR069OperationCode;\r
47 import org.commscope.tr069adapter.acs.common.dto.TR069OperationDetails;\r
48 import org.commscope.tr069adapter.mapper.ErrorCodeMetaData;\r
49 import org.commscope.tr069adapter.mapper.model.ErrorCodeDetails;\r
50 import org.commscope.tr069adapter.mapper.model.NetConfResponse;\r
51 import org.slf4j.Logger;\r
52 import org.slf4j.LoggerFactory;\r
53 import org.springframework.beans.factory.annotation.Autowired;\r
54 import org.springframework.stereotype.Component;\r
55 import org.w3c.dom.Document;\r
56 import org.w3c.dom.Element;\r
57 import org.w3c.dom.Node;\r
58 import org.w3c.dom.NodeList;\r
59 import org.xml.sax.InputSource;\r
60 \r
61 @Component\r
62 public class NetconfToTr069MapperUtil {\r
63 \r
64   @Autowired\r
65   MOMetaDataUtil metaDataUtil;\r
66 \r
67   private static final Logger logger = LoggerFactory.getLogger(NetconfToTr069MapperUtil.class);\r
68   private static final String INDEX_STR = "index";\r
69   private static final String INDEX_REGEX = "[0-9]{1,}";\r
70 \r
71   @Autowired\r
72   private ErrorCodeUtil errorCodeUtil;\r
73 \r
74   public static Element convertStringToDocument(String xmlStr) {\r
75     try {\r
76       Document doc = convertStringToDocumentXml(xmlStr);\r
77       if (null != doc)\r
78         return doc.getDocumentElement();\r
79     } catch (Exception e) {\r
80       logger.error("Error while converting String to element {}", e.getMessage());\r
81     }\r
82     return null;\r
83   }\r
84 \r
85   public static Document convertStringToDocumentXml(String xmlStr) {\r
86     try {\r
87       DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();\r
88       factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");\r
89       factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");\r
90       DocumentBuilder builder;\r
91       builder = factory.newDocumentBuilder();\r
92       return builder.parse(new InputSource(new StringReader(xmlStr)));\r
93     } catch (Exception e) {\r
94       logger.error("Error while converting String to element {}", e.getMessage());\r
95     }\r
96     return null;\r
97   }\r
98 \r
99   public static DeviceRPCRequest prepareTR069Request(String deviceId, Element operationElement,\r
100       String netconfTag, TR069OperationCode opCode) {\r
101     Node requestDataNode = getDeviceDataNode(operationElement, netconfTag);\r
102     if (requestDataNode == null) {\r
103       logger.debug("No matching device parameters found in the netconf request XML.");\r
104       return null;\r
105     }\r
106     Map<String, String> map = getParameterMapForNode(requestDataNode, -1);\r
107     List<ParameterDTO> params = new ArrayList<>();\r
108 \r
109     for (java.util.Map.Entry<String, String> entry : map.entrySet()) {\r
110       if (!entry.getKey().equalsIgnoreCase("filter")) {\r
111         String moName = entry.getKey();\r
112         String value = entry.getValue();\r
113         if (moName.endsWith("." + INDEX_STR)\r
114             && (TR069OperationCode.GET_PARAMETER_VALUES.equals(opCode)\r
115                 || TR069OperationCode.DELETE_OBJECT.equals(opCode))) {\r
116           logger.debug("Index node found : {}", moName);\r
117           moName = moName.replaceFirst("." + INDEX_STR, ".");\r
118           value = null;\r
119 \r
120         }\r
121 \r
122         ParameterDTO param = new ParameterDTO(moName, value);\r
123         params.add(param);\r
124       }\r
125     }\r
126 \r
127     TR069OperationDetails opDetails = new TR069OperationDetails();\r
128     opDetails.setOpCode(opCode);\r
129     opDetails.setParmeters(params);\r
130 \r
131     DeviceRPCRequest deviceRPCRequest = new DeviceRPCRequest();\r
132     TR069DeviceDetails tr069DeviceDetails = new TR069DeviceDetails();\r
133     tr069DeviceDetails.setDeviceId(deviceId);\r
134     deviceRPCRequest.setOpDetails(opDetails);\r
135     deviceRPCRequest.setDeviceDetails(tr069DeviceDetails);\r
136     OperationOptions options = new OperationOptions();\r
137     options.setExecutionTimeout(60l);\r
138     deviceRPCRequest.setOptions(options);\r
139     return deviceRPCRequest;\r
140   }\r
141 \r
142   public NetConfResponse getNetconfResponse(DeviceRPCResponse opResult) {\r
143     NetConfResponse netConfResponse = new NetConfResponse();\r
144     ErrorCodeMetaData errorCodeMetaData =\r
145         errorCodeUtil.getErrorCodeMetaData(opResult.getFaultKey());\r
146     ErrorCodeDetails errorCode = new ErrorCodeDetails();\r
147     if (errorCodeMetaData != null) {\r
148       errorCode.setFaultCode(opResult.getFaultKey());\r
149       errorCode.setErrorMessage(errorCodeMetaData.getErrorMessage());\r
150       errorCode.setErrorType(errorCodeMetaData.getErrorType());\r
151       errorCode.setErrorTag(errorCodeMetaData.getErrorTag());\r
152       errorCode.setErrorSeverity(errorCodeMetaData.getErrorSeverity());\r
153       netConfResponse.setErrorCode(errorCode);\r
154       netConfResponse.setErrorMessage(opResult.getFaultString());\r
155     } else if (opResult.getFaultKey() != null && opResult.getFaultString() != null) {\r
156       errorCode.setFaultCode(opResult.getFaultKey());\r
157       errorCode.setErrorMessage(opResult.getFaultString());\r
158       errorCode.setErrorType("application");\r
159       errorCode.setErrorTag("operation-failed");\r
160       errorCode.setErrorSeverity("ERROR");\r
161       netConfResponse.setErrorCode(errorCode);\r
162       netConfResponse.setErrorMessage(opResult.getFaultString());\r
163     }\r
164     netConfResponse.setNetconfResponseXml(\r
165         getNetconfResponseXML(opResult.getOperationResponse().getParameterDTOs()));\r
166     return netConfResponse;\r
167   }\r
168 \r
169   private String getNetconfResponseXML(List<ParameterDTO> parameters) {\r
170     if (null == parameters || parameters.isEmpty()) {\r
171 \r
172       return null;\r
173     }\r
174     Collections.sort(parameters, new SortByParamterName());\r
175 \r
176     String result = null;\r
177     try {\r
178       DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();\r
179       docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");\r
180       docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");\r
181       DocumentBuilder docBuilder = docFactory.newDocumentBuilder();\r
182       Document doc = docBuilder.newDocument();\r
183 \r
184       Map<String, Element> parentNodeMap = new HashMap<>();\r
185       Element dataNode = null; // root of all nodes\r
186 \r
187       for (ParameterDTO paramDto : parameters) {\r
188         String paramName =\r
189             metaDataUtil.getNetconfNameByTR69NameWithIndexes(paramDto.getParamName());\r
190         String paramValue = paramDto.getParamValue();\r
191         if (paramValue == null || paramValue.trim().isEmpty()) {\r
192           logger.debug("Values is empty so skipping this parameter.");\r
193           continue;\r
194         }\r
195         StringTokenizer tokenizer = new StringTokenizer(paramName, ".");\r
196         String parentNodeName = null;\r
197         String parentNodeKey = null;\r
198         Element parentNode = null;\r
199         while (tokenizer.hasMoreElements()) {\r
200           String nodeName = (String) tokenizer.nextElement();\r
201           if (null == parentNodeName) { // construct first node or\r
202                                         // Device node\r
203             parentNodeName = nodeName;\r
204             parentNodeKey = nodeName;\r
205             // check if the node already exists in parentNodeMap\r
206             parentNode = parentNodeMap.get(parentNodeKey);\r
207             if (null == dataNode) {\r
208               dataNode = parentNode;\r
209             }\r
210 \r
211           } else if (nodeName.matches(INDEX_REGEX)) { // construct\r
212                                                       // tabular and\r
213                                                       // index nodes\r
214 \r
215             // get parent tabular node from parent MAP\r
216             StringBuilder bld = new StringBuilder(parentNodeKey);\r
217             bld.append(".");\r
218             bld.append(nodeName);\r
219             parentNodeKey = bld.toString();\r
220             Element node = parentNodeMap.get(parentNodeKey);\r
221 \r
222             // create a tabular parent node if doesn't exit in MAP\r
223             if (null == node) {\r
224               if (metaDataUtil.getMetaDataByNetConfName(parentNodeKey + ".") != null\r
225                   && metaDataUtil.getMetaDataByNetConfName(parentNodeKey + ".").getURI() != null) {\r
226                 node = doc.createElementNS(\r
227                     metaDataUtil.getMetaDataByNetConfName(parentNodeKey + ".").getURI(),\r
228                     parentNodeName);\r
229               } else {\r
230                 node = doc.createElement(parentNodeName);\r
231               }\r
232               parentNodeMap.put(parentNodeKey, node);\r
233 \r
234               // update current tabular parent node.\r
235               if (null != parentNode)\r
236                 parentNode.appendChild(node);\r
237 \r
238               // prepare and add index node to tabular parent node\r
239               Element indexNode = doc.createElement(INDEX_STR);\r
240               indexNode.setTextContent(nodeName);\r
241               node.appendChild(indexNode);\r
242             }\r
243             parentNode = node; // move parent to child\r
244             parentNodeName = nodeName;\r
245           } else if (parentNodeName.matches(INDEX_REGEX)) { // move to\r
246                                                             // next\r
247                                                             // node\r
248                                                             // if\r
249                                                             // tabular\r
250                                                             // attribute\r
251                                                             // is\r
252                                                             // found\r
253             StringBuilder bld = new StringBuilder(parentNodeName);\r
254             bld.append(".");\r
255             bld.append(nodeName);\r
256             parentNodeKey = bld.toString();\r
257             parentNodeName = nodeName;\r
258           } else {\r
259             // construct intermediate nodes\r
260             Element node = parentNodeMap.get(parentNodeKey);\r
261             if (null == node) {\r
262               if (metaDataUtil.getMetaDataByNetConfName(parentNodeKey) != null\r
263                   && metaDataUtil.getMetaDataByNetConfName(parentNodeKey).getURI() != null) {\r
264                 node = doc.createElementNS(\r
265                     metaDataUtil.getMetaDataByNetConfName(parentNodeKey).getURI(), parentNodeName);\r
266                 if (dataNode == null)\r
267                   dataNode = node;\r
268               } else {\r
269                 node = doc.createElement(parentNodeName);\r
270               }\r
271               parentNodeMap.put(parentNodeKey, node);\r
272               if (null != parentNode)\r
273                 parentNode.appendChild(node);\r
274 \r
275             }\r
276             StringBuilder bld = new StringBuilder(parentNodeKey);\r
277             bld.append(".");\r
278             bld.append(nodeName);\r
279             parentNodeKey = bld.toString();\r
280             parentNodeName = nodeName;\r
281             parentNode = node;\r
282           }\r
283         }\r
284         // construct leaf node\r
285         Element leafNode = doc.createElement(parentNodeName);\r
286         leafNode.setTextContent(paramValue);\r
287         if (null != parentNode)\r
288           parentNode.appendChild(leafNode);\r
289       }\r
290 \r
291       if (null != dataNode) {\r
292         result = NetconfToTr069MapperUtil.convertDocumentToString(dataNode);\r
293       }\r
294     } catch (ParserConfigurationException pce) {\r
295       logger.error("Exception : {}", pce.getMessage());\r
296     }\r
297 \r
298     return result;\r
299   }\r
300 \r
301   public static String convertDocumentToString(Element element) {\r
302     String strxml = null;\r
303     try {\r
304       TransformerFactory transformerFactory = TransformerFactory.newInstance();\r
305       transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");\r
306       transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");\r
307       Transformer transformer = transformerFactory.newTransformer();\r
308       DOMSource source = new DOMSource(element);\r
309       StreamResult result = new StreamResult(new StringWriter());\r
310       transformer.transform(source, result);\r
311       strxml = result.getWriter().toString();\r
312     } catch (Exception e) {\r
313       logger.error("Error while converting Element to String {}", e.toString());\r
314     }\r
315     logger.debug("Converted XML is : {} ", strxml);\r
316     return strxml;\r
317   }\r
318 \r
319   // TODO: Need to optimize the Node element logic for better performance\r
320   private static Map<String, String> getParameterMapForNode(Node moRNode, int numberOfChilds) {\r
321     Map<String, String> result = new HashMap<>();\r
322     if (moRNode.getNodeType() == Node.ELEMENT_NODE) {\r
323       NodeList childs = moRNode.getChildNodes();\r
324       boolean hasChildElements = false;\r
325       if (childs.getLength() > 0) {\r
326         int counter = 0;\r
327         for (int i = 0; i < childs.getLength(); i++) {\r
328           Node cNode = childs.item(i);\r
329           if (cNode != null && cNode.getNodeType() == Node.ELEMENT_NODE) {\r
330             counter++;\r
331           }\r
332         }\r
333 \r
334         for (int i = 0; i < childs.getLength(); i++) {\r
335           Node cNode = childs.item(i);\r
336           if (cNode != null && cNode.getNodeType() == Node.ELEMENT_NODE) {\r
337             hasChildElements = true;\r
338             Map<String, String> subResult = getParameterMapForNode(cNode, counter);\r
339             result.putAll(subResult);\r
340           }\r
341         }\r
342       }\r
343       if (!hasChildElements) {\r
344         String moName = getMOName(moRNode);\r
345         if ((null != moName && !moName.endsWith("." + INDEX_STR))\r
346             || (null != moName && numberOfChilds == 1)) {\r
347           result.put(moName, moRNode.getTextContent());\r
348         }\r
349       }\r
350     }\r
351 \r
352     return result;\r
353   }\r
354 \r
355   private static String getMOName(Node moRNode) {\r
356     String result = removeNS(moRNode.getNodeName());\r
357     Node pNode = moRNode;\r
358     while (true) {\r
359       pNode = pNode.getParentNode();\r
360       if (pNode == null || pNode.getNodeType() != Node.ELEMENT_NODE\r
361           || pNode.getNodeName().equals("edit-config") || pNode.getNodeName().equals("config")\r
362           || pNode.getNodeName().equals("get-config") || pNode.getNodeName().equals("filter")\r
363           || pNode.getNodeName().equals("get")) {\r
364         return result;\r
365       } else {\r
366         String indexStr = getMOIndex(pNode);\r
367         StringBuilder bld = new StringBuilder(removeNS(pNode.getNodeName()));\r
368         bld.append(".");\r
369         if (indexStr == null) {\r
370           bld.append(result);\r
371           result = bld.toString();\r
372         } else {\r
373           bld.append(indexStr);\r
374           bld.append(".");\r
375           bld.append(result);\r
376           result = bld.toString();\r
377         }\r
378       }\r
379     }\r
380   }\r
381 \r
382   private static Node getDeviceDataNode(Element el, String filter) {\r
383     try {\r
384       NodeList nodeList = el.getElementsByTagName(filter);\r
385       if (nodeList.getLength() > 0) {\r
386         nodeList = nodeList.item(0).getChildNodes();\r
387         for (int i = 0; i < nodeList.getLength(); i++) {\r
388           Node node = nodeList.item(i);\r
389           if (node.getNodeType() == Node.ELEMENT_NODE) {\r
390             return node;\r
391           }\r
392         }\r
393       }\r
394     } catch (Exception e) {\r
395       logger.error("exception occured while parsing the request xml {}", e.getMessage());\r
396     }\r
397     return null;\r
398   }\r
399 \r
400   private static String removeNS(String nodeName) {\r
401     // remove name space\r
402     int li = nodeName.lastIndexOf(':');\r
403     nodeName = nodeName.substring(li + 1);\r
404     return nodeName;\r
405   }\r
406 \r
407   private static String getMOIndex(Node pNode) {\r
408     if (null != pNode) {\r
409       NodeList nodeList = pNode.getChildNodes();\r
410       for (int i = 0; i < nodeList.getLength(); i++) {\r
411         Node childNode = nodeList.item(i);\r
412         String nodeName = removeNS(childNode.getNodeName());\r
413         if (nodeName.equalsIgnoreCase(INDEX_STR)) {\r
414           return childNode.getTextContent();\r
415         }\r
416       }\r
417     }\r
418     return null;\r
419   }\r
420 \r
421 }\r
422 \r
423 \r
424 class SortByParamterName implements Comparator<ParameterDTO>, Serializable {\r
425   private static final long serialVersionUID = 3010693349267067945L;\r
426 \r
427   public int compare(ParameterDTO a, ParameterDTO b) {\r
428     return a.getParamName().compareTo(b.getParamName());\r
429   }\r
430 }\r