--- /dev/null
+/*\r
+ * ============LICENSE_START========================================================================\r
+ * ONAP : tr-069-adapter\r
+ * =================================================================================================\r
+ * Copyright (C) 2020 CommScope Inc Intellectual Property.\r
+ * =================================================================================================\r
+ * This tr-069-adapter software file is distributed by CommScope Inc under the Apache License,\r
+ * Version 2.0 (the "License"); you may not use this file except in compliance with the License. You\r
+ * may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\r
+ * either express or implied. See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ * ===============LICENSE_END=======================================================================\r
+ */\r
+\r
+package org.commscope.tr069adapter.mapper.util;\r
+\r
+import java.io.Serializable;\r
+import java.io.StringReader;\r
+import java.io.StringWriter;\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.Comparator;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.StringTokenizer;\r
+\r
+import javax.xml.XMLConstants;\r
+import javax.xml.parsers.DocumentBuilder;\r
+import javax.xml.parsers.DocumentBuilderFactory;\r
+import javax.xml.parsers.ParserConfigurationException;\r
+import javax.xml.transform.Transformer;\r
+import javax.xml.transform.TransformerFactory;\r
+import javax.xml.transform.dom.DOMSource;\r
+import javax.xml.transform.stream.StreamResult;\r
+\r
+import org.commscope.tr069adapter.acs.common.DeviceRPCRequest;\r
+import org.commscope.tr069adapter.acs.common.DeviceRPCResponse;\r
+import org.commscope.tr069adapter.acs.common.OperationOptions;\r
+import org.commscope.tr069adapter.acs.common.ParameterDTO;\r
+import org.commscope.tr069adapter.acs.common.dto.TR069DeviceDetails;\r
+import org.commscope.tr069adapter.acs.common.dto.TR069OperationCode;\r
+import org.commscope.tr069adapter.acs.common.dto.TR069OperationDetails;\r
+import org.commscope.tr069adapter.mapper.ErrorCodeMetaData;\r
+import org.commscope.tr069adapter.mapper.model.ErrorCodeDetails;\r
+import org.commscope.tr069adapter.mapper.model.NetConfResponse;\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+import org.springframework.beans.factory.annotation.Autowired;\r
+import org.springframework.stereotype.Component;\r
+import org.w3c.dom.Document;\r
+import org.w3c.dom.Element;\r
+import org.w3c.dom.Node;\r
+import org.w3c.dom.NodeList;\r
+import org.xml.sax.InputSource;\r
+\r
+@Component\r
+public class NetconfToTr069MapperUtil {\r
+\r
+ @Autowired\r
+ MOMetaDataUtil metaDataUtil;\r
+\r
+ private static final Logger logger = LoggerFactory.getLogger(NetconfToTr069MapperUtil.class);\r
+ private static final String INDEX_STR = "index";\r
+ private static final String INDEX_REGEX = "[0-9]{1,}";\r
+\r
+ @Autowired\r
+ private ErrorCodeUtil errorCodeUtil;\r
+\r
+ public static Element convertStringToDocument(String xmlStr) {\r
+ try {\r
+ Document doc = convertStringToDocumentXml(xmlStr);\r
+ if (null != doc)\r
+ return doc.getDocumentElement();\r
+ } catch (Exception e) {\r
+ logger.error("Error while converting String to element {}", e.getMessage());\r
+ }\r
+ return null;\r
+ }\r
+\r
+ public static Document convertStringToDocumentXml(String xmlStr) {\r
+ try {\r
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();\r
+ factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");\r
+ factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");\r
+ DocumentBuilder builder;\r
+ builder = factory.newDocumentBuilder();\r
+ return builder.parse(new InputSource(new StringReader(xmlStr)));\r
+ } catch (Exception e) {\r
+ logger.error("Error while converting String to element {}", e.getMessage());\r
+ }\r
+ return null;\r
+ }\r
+\r
+ public static DeviceRPCRequest prepareTR069Request(String deviceId, Element operationElement,\r
+ String netconfTag, TR069OperationCode opCode) {\r
+ Node requestDataNode = getDeviceDataNode(operationElement, netconfTag);\r
+ if (requestDataNode == null) {\r
+ logger.debug("No matching device parameters found in the netconf request XML.");\r
+ return null;\r
+ }\r
+ Map<String, String> map = getParameterMapForNode(requestDataNode, -1);\r
+ List<ParameterDTO> params = new ArrayList<>();\r
+\r
+ for (java.util.Map.Entry<String, String> entry : map.entrySet()) {\r
+ if (!entry.getKey().equalsIgnoreCase("filter")) {\r
+ String moName = entry.getKey();\r
+ String value = entry.getValue();\r
+ if (moName.endsWith("." + INDEX_STR)\r
+ && (TR069OperationCode.GET_PARAMETER_VALUES.equals(opCode)\r
+ || TR069OperationCode.DELETE_OBJECT.equals(opCode))) {\r
+ logger.debug("Index node found : {}", moName);\r
+ moName = moName.replaceFirst("." + INDEX_STR, ".");\r
+ value = null;\r
+\r
+ }\r
+\r
+ ParameterDTO param = new ParameterDTO(moName, value);\r
+ params.add(param);\r
+ }\r
+ }\r
+\r
+ TR069OperationDetails opDetails = new TR069OperationDetails();\r
+ opDetails.setOpCode(opCode);\r
+ opDetails.setParmeters(params);\r
+\r
+ DeviceRPCRequest deviceRPCRequest = new DeviceRPCRequest();\r
+ TR069DeviceDetails tr069DeviceDetails = new TR069DeviceDetails();\r
+ tr069DeviceDetails.setDeviceId(deviceId);\r
+ deviceRPCRequest.setOpDetails(opDetails);\r
+ deviceRPCRequest.setDeviceDetails(tr069DeviceDetails);\r
+ OperationOptions options = new OperationOptions();\r
+ options.setExecutionTimeout(60l);\r
+ deviceRPCRequest.setOptions(options);\r
+ return deviceRPCRequest;\r
+ }\r
+\r
+ public NetConfResponse getNetconfResponse(DeviceRPCResponse opResult) {\r
+ NetConfResponse netConfResponse = new NetConfResponse();\r
+ ErrorCodeMetaData errorCodeMetaData =\r
+ errorCodeUtil.getErrorCodeMetaData(opResult.getFaultKey());\r
+ ErrorCodeDetails errorCode = new ErrorCodeDetails();\r
+ if (errorCodeMetaData != null) {\r
+ errorCode.setFaultCode(opResult.getFaultKey());\r
+ errorCode.setErrorMessage(errorCodeMetaData.getErrorMessage());\r
+ errorCode.setErrorType(errorCodeMetaData.getErrorType());\r
+ errorCode.setErrorTag(errorCodeMetaData.getErrorTag());\r
+ errorCode.setErrorSeverity(errorCodeMetaData.getErrorSeverity());\r
+ netConfResponse.setErrorCode(errorCode);\r
+ netConfResponse.setErrorMessage(opResult.getFaultString());\r
+ } else if (opResult.getFaultKey() != null && opResult.getFaultString() != null) {\r
+ errorCode.setFaultCode(opResult.getFaultKey());\r
+ errorCode.setErrorMessage(opResult.getFaultString());\r
+ errorCode.setErrorType("application");\r
+ errorCode.setErrorTag("operation-failed");\r
+ errorCode.setErrorSeverity("ERROR");\r
+ netConfResponse.setErrorCode(errorCode);\r
+ netConfResponse.setErrorMessage(opResult.getFaultString());\r
+ }\r
+ netConfResponse.setNetconfResponseXml(\r
+ getNetconfResponseXML(opResult.getOperationResponse().getParameterDTOs()));\r
+ return netConfResponse;\r
+ }\r
+\r
+ private String getNetconfResponseXML(List<ParameterDTO> parameters) {\r
+ if (null == parameters || parameters.isEmpty()) {\r
+\r
+ return null;\r
+ }\r
+ Collections.sort(parameters, new SortByParamterName());\r
+\r
+ String result = null;\r
+ try {\r
+ DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();\r
+ docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");\r
+ docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");\r
+ DocumentBuilder docBuilder = docFactory.newDocumentBuilder();\r
+ Document doc = docBuilder.newDocument();\r
+\r
+ Map<String, Element> parentNodeMap = new HashMap<>();\r
+ Element dataNode = null; // root of all nodes\r
+\r
+ for (ParameterDTO paramDto : parameters) {\r
+ String paramName =\r
+ metaDataUtil.getNetconfNameByTR69NameWithIndexes(paramDto.getParamName());\r
+ String paramValue = paramDto.getParamValue();\r
+ if (paramValue == null || paramValue.trim().isEmpty()) {\r
+ logger.debug("Values is empty so skipping this parameter.");\r
+ continue;\r
+ }\r
+ StringTokenizer tokenizer = new StringTokenizer(paramName, ".");\r
+ String parentNodeName = null;\r
+ String parentNodeKey = null;\r
+ Element parentNode = null;\r
+ while (tokenizer.hasMoreElements()) {\r
+ String nodeName = (String) tokenizer.nextElement();\r
+ if (null == parentNodeName) { // construct first node or\r
+ // Device node\r
+ parentNodeName = nodeName;\r
+ parentNodeKey = nodeName;\r
+ // check if the node already exists in parentNodeMap\r
+ parentNode = parentNodeMap.get(parentNodeKey);\r
+ if (null == dataNode) {\r
+ dataNode = parentNode;\r
+ }\r
+\r
+ } else if (nodeName.matches(INDEX_REGEX)) { // construct\r
+ // tabular and\r
+ // index nodes\r
+\r
+ // get parent tabular node from parent MAP\r
+ StringBuilder bld = new StringBuilder(parentNodeKey);\r
+ bld.append(".");\r
+ bld.append(nodeName);\r
+ parentNodeKey = bld.toString();\r
+ Element node = parentNodeMap.get(parentNodeKey);\r
+\r
+ // create a tabular parent node if doesn't exit in MAP\r
+ if (null == node) {\r
+ if (metaDataUtil.getMetaDataByNetConfName(parentNodeKey + ".") != null\r
+ && metaDataUtil.getMetaDataByNetConfName(parentNodeKey + ".").getURI() != null) {\r
+ node = doc.createElementNS(\r
+ metaDataUtil.getMetaDataByNetConfName(parentNodeKey + ".").getURI(),\r
+ parentNodeName);\r
+ } else {\r
+ node = doc.createElement(parentNodeName);\r
+ }\r
+ parentNodeMap.put(parentNodeKey, node);\r
+\r
+ // update current tabular parent node.\r
+ if (null != parentNode)\r
+ parentNode.appendChild(node);\r
+\r
+ // prepare and add index node to tabular parent node\r
+ Element indexNode = doc.createElement(INDEX_STR);\r
+ indexNode.setTextContent(nodeName);\r
+ node.appendChild(indexNode);\r
+ }\r
+ parentNode = node; // move parent to child\r
+ parentNodeName = nodeName;\r
+ } else if (parentNodeName.matches(INDEX_REGEX)) { // move to\r
+ // next\r
+ // node\r
+ // if\r
+ // tabular\r
+ // attribute\r
+ // is\r
+ // found\r
+ StringBuilder bld = new StringBuilder(parentNodeName);\r
+ bld.append(".");\r
+ bld.append(nodeName);\r
+ parentNodeKey = bld.toString();\r
+ parentNodeName = nodeName;\r
+ } else {\r
+ // construct intermediate nodes\r
+ Element node = parentNodeMap.get(parentNodeKey);\r
+ if (null == node) {\r
+ if (metaDataUtil.getMetaDataByNetConfName(parentNodeKey) != null\r
+ && metaDataUtil.getMetaDataByNetConfName(parentNodeKey).getURI() != null) {\r
+ node = doc.createElementNS(\r
+ metaDataUtil.getMetaDataByNetConfName(parentNodeKey).getURI(), parentNodeName);\r
+ if (dataNode == null)\r
+ dataNode = node;\r
+ } else {\r
+ node = doc.createElement(parentNodeName);\r
+ }\r
+ parentNodeMap.put(parentNodeKey, node);\r
+ if (null != parentNode)\r
+ parentNode.appendChild(node);\r
+\r
+ }\r
+ StringBuilder bld = new StringBuilder(parentNodeKey);\r
+ bld.append(".");\r
+ bld.append(nodeName);\r
+ parentNodeKey = bld.toString();\r
+ parentNodeName = nodeName;\r
+ parentNode = node;\r
+ }\r
+ }\r
+ // construct leaf node\r
+ Element leafNode = doc.createElement(parentNodeName);\r
+ leafNode.setTextContent(paramValue);\r
+ if (null != parentNode)\r
+ parentNode.appendChild(leafNode);\r
+ }\r
+\r
+ if (null != dataNode) {\r
+ result = NetconfToTr069MapperUtil.convertDocumentToString(dataNode);\r
+ }\r
+ } catch (ParserConfigurationException pce) {\r
+ logger.error("Exception : {}", pce.getMessage());\r
+ }\r
+\r
+ return result;\r
+ }\r
+\r
+ public static String convertDocumentToString(Element element) {\r
+ String strxml = null;\r
+ try {\r
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();\r
+ transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");\r
+ transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");\r
+ Transformer transformer = transformerFactory.newTransformer();\r
+ DOMSource source = new DOMSource(element);\r
+ StreamResult result = new StreamResult(new StringWriter());\r
+ transformer.transform(source, result);\r
+ strxml = result.getWriter().toString();\r
+ } catch (Exception e) {\r
+ logger.error("Error while converting Element to String {}", e.toString());\r
+ }\r
+ logger.debug("Converted XML is : {} ", strxml);\r
+ return strxml;\r
+ }\r
+\r
+ // TODO: Need to optimize the Node element logic for better performance\r
+ private static Map<String, String> getParameterMapForNode(Node moRNode, int numberOfChilds) {\r
+ Map<String, String> result = new HashMap<>();\r
+ if (moRNode.getNodeType() == Node.ELEMENT_NODE) {\r
+ NodeList childs = moRNode.getChildNodes();\r
+ boolean hasChildElements = false;\r
+ if (childs.getLength() > 0) {\r
+ int counter = 0;\r
+ for (int i = 0; i < childs.getLength(); i++) {\r
+ Node cNode = childs.item(i);\r
+ if (cNode != null && cNode.getNodeType() == Node.ELEMENT_NODE) {\r
+ counter++;\r
+ }\r
+ }\r
+\r
+ for (int i = 0; i < childs.getLength(); i++) {\r
+ Node cNode = childs.item(i);\r
+ if (cNode != null && cNode.getNodeType() == Node.ELEMENT_NODE) {\r
+ hasChildElements = true;\r
+ Map<String, String> subResult = getParameterMapForNode(cNode, counter);\r
+ result.putAll(subResult);\r
+ }\r
+ }\r
+ }\r
+ if (!hasChildElements) {\r
+ String moName = getMOName(moRNode);\r
+ if ((null != moName && !moName.endsWith("." + INDEX_STR))\r
+ || (null != moName && numberOfChilds == 1)) {\r
+ result.put(moName, moRNode.getTextContent());\r
+ }\r
+ }\r
+ }\r
+\r
+ return result;\r
+ }\r
+\r
+ private static String getMOName(Node moRNode) {\r
+ String result = removeNS(moRNode.getNodeName());\r
+ Node pNode = moRNode;\r
+ while (true) {\r
+ pNode = pNode.getParentNode();\r
+ if (pNode == null || pNode.getNodeType() != Node.ELEMENT_NODE\r
+ || pNode.getNodeName().equals("edit-config") || pNode.getNodeName().equals("config")\r
+ || pNode.getNodeName().equals("get-config") || pNode.getNodeName().equals("filter")\r
+ || pNode.getNodeName().equals("get")) {\r
+ return result;\r
+ } else {\r
+ String indexStr = getMOIndex(pNode);\r
+ StringBuilder bld = new StringBuilder(removeNS(pNode.getNodeName()));\r
+ bld.append(".");\r
+ if (indexStr == null) {\r
+ bld.append(result);\r
+ result = bld.toString();\r
+ } else {\r
+ bld.append(indexStr);\r
+ bld.append(".");\r
+ bld.append(result);\r
+ result = bld.toString();\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ private static Node getDeviceDataNode(Element el, String filter) {\r
+ try {\r
+ NodeList nodeList = el.getElementsByTagName(filter);\r
+ if (nodeList.getLength() > 0) {\r
+ nodeList = nodeList.item(0).getChildNodes();\r
+ for (int i = 0; i < nodeList.getLength(); i++) {\r
+ Node node = nodeList.item(i);\r
+ if (node.getNodeType() == Node.ELEMENT_NODE) {\r
+ return node;\r
+ }\r
+ }\r
+ }\r
+ } catch (Exception e) {\r
+ logger.error("exception occured while parsing the request xml {}", e.getMessage());\r
+ }\r
+ return null;\r
+ }\r
+\r
+ private static String removeNS(String nodeName) {\r
+ // remove name space\r
+ int li = nodeName.lastIndexOf(':');\r
+ nodeName = nodeName.substring(li + 1);\r
+ return nodeName;\r
+ }\r
+\r
+ private static String getMOIndex(Node pNode) {\r
+ if (null != pNode) {\r
+ NodeList nodeList = pNode.getChildNodes();\r
+ for (int i = 0; i < nodeList.getLength(); i++) {\r
+ Node childNode = nodeList.item(i);\r
+ String nodeName = removeNS(childNode.getNodeName());\r
+ if (nodeName.equalsIgnoreCase(INDEX_STR)) {\r
+ return childNode.getTextContent();\r
+ }\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+\r
+}\r
+\r
+\r
+class SortByParamterName implements Comparator<ParameterDTO>, Serializable {\r
+ private static final long serialVersionUID = 3010693349267067945L;\r
+\r
+ public int compare(ParameterDTO a, ParameterDTO b) {\r
+ return a.getParamName().compareTo(b.getParamName());\r
+ }\r
+}\r