2c329857c52238d236970655f7c509364ba66930
[aai/schema-service.git] / aai-schema-service / src / main / java / org / onap / aai / schemaservice / nodeschema / NodeIngestor.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *    http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20 package org.onap.aai.schemaservice.nodeschema;
21
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24 import com.google.common.base.CaseFormat;
25 import com.google.common.collect.ArrayListMultimap;
26 import com.google.common.collect.Multimap;
27 import org.eclipse.persistence.jaxb.JAXBContextProperties;
28 import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext;
29 import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContextFactory;
30 import org.onap.aai.schemaservice.config.ConfigTranslator;
31 import org.springframework.beans.factory.annotation.Autowired;
32 import org.springframework.stereotype.Component;
33 import org.w3c.dom.Document;
34 import org.w3c.dom.Element;
35 import org.w3c.dom.Node;
36 import org.w3c.dom.NodeList;
37 import org.xml.sax.SAXException;
38
39 import javax.xml.XMLConstants;
40 import jakarta.xml.bind.JAXBException;
41 import javax.xml.parsers.DocumentBuilder;
42 import javax.xml.parsers.DocumentBuilderFactory;
43 import javax.xml.parsers.ParserConfigurationException;
44 import java.io.*;
45 import java.nio.charset.StandardCharsets;
46 import java.util.*;
47 import java.util.Map.Entry;
48 import java.util.regex.Matcher;
49 import java.util.regex.Pattern;
50
51 /**
52  * NodeIngestor - ingests A&AI OXM files per given config, serves DynamicJAXBContext per version
53  */
54 @Component
55 public class NodeIngestor {
56
57     private static final Logger LOGGER = LoggerFactory.getLogger(NodeIngestor.class);
58
59     private static final Pattern classNamePattern = Pattern.compile("\\.(v\\d+)\\.");
60     private Map<SchemaVersion, DynamicJAXBContext> versionContextMap = new TreeMap<>();
61     private Map<SchemaVersion, Set<String>> typesPerVersion = new TreeMap<>();
62     private Map<SchemaVersion, Document> schemaPerVersion = new TreeMap<>();
63     private ConfigTranslator translator;
64
65
66     @Autowired
67     /**
68      * Instantiates the NodeIngestor bean.
69      *
70      * @param translator - ConfigTranslator autowired in by Spring framework which
71      * contains the configuration information needed to ingest the desired files.
72      */
73     public NodeIngestor(ConfigTranslator translator) {
74         this.translator = translator;
75         Map<SchemaVersion, List<String>> filesToIngest = translator.getNodeFiles();
76
77         try {
78             for (Entry<SchemaVersion, List<String>> verFiles : filesToIngest.entrySet()) {
79                 SchemaVersion v = verFiles.getKey();
80                 List<String> files = verFiles.getValue();
81                 final DynamicJAXBContext ctx = ingest(files);
82                 versionContextMap.put(v, ctx);
83                 typesPerVersion.put(v, getAllNodeTypes(files));
84                 schemaPerVersion.put(v, createCombinedSchema(files, v));
85             }
86         } catch (JAXBException | ParserConfigurationException | SAXException | IOException e) {
87             throw new ExceptionInInitializerError(e);
88         }
89     }
90
91     /**
92      * Ingests the given OXM files into DynamicJAXBContext
93      *
94      * @param files - List<String> of full filenames (ie including the path) to be ingested
95      * @return DynamicJAXBContext including schema information from all given files
96      * @throws FileNotFoundException if an OXM file can't be found
97      * @throws JAXBException         if there's an error creating the DynamicJAXBContext
98      */
99     private DynamicJAXBContext ingest(List<String> files) throws FileNotFoundException, JAXBException {
100         List<InputStream> streams = new ArrayList<>();
101
102         for (String name : files) {
103             streams.add(new FileInputStream(new File(name)));
104         }
105
106         Map<String, Object> properties = new HashMap<>();
107         properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, streams);
108         return DynamicJAXBContextFactory.createContextFromOXM(this.getClass().getClassLoader(), properties);
109     }
110
111
112     private Set<String> getAllNodeTypes(List<String> files) throws ParserConfigurationException, SAXException, IOException {
113         Set<String> types = new HashSet<>();
114         final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
115         docFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
116         docFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
117         docFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
118         docFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
119         docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
120         docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
121         final DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
122
123         ArrayList<Node> javaTypes = new ArrayList<>();
124         for (String file : files) {
125             InputStream inputStream = new FileInputStream(file);
126
127             final Document doc = docBuilder.parse(inputStream);
128             final NodeList list = doc.getElementsByTagName("java-type");
129
130
131             for (int i = 0; i < list.getLength(); i++) {
132                 String type = list.item(i).getAttributes().getNamedItem("name").getNodeValue();
133                 javaTypes.add(list.item(i));
134                 types.add(CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, type));
135             }
136         }
137
138         return types;
139     }
140
141     private Document createCombinedSchema(List<String> files, SchemaVersion v) throws ParserConfigurationException, SAXException, IOException {
142         final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
143         docFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
144         docFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
145         docFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
146         docFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
147         docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
148         docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
149         final DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
150         DocumentBuilder masterDocBuilder = docFactory.newDocumentBuilder();
151         Document combinedDoc = masterDocBuilder.parse(getShell(v));
152         NodeList masterList = combinedDoc.getElementsByTagName("java-types");
153         Node javaTypesContainer = masterList.getLength() == 0 ? combinedDoc.getDocumentElement() : masterList.item(0);
154
155         Multimap<String, Node> nodeMultimap = ArrayListMultimap.create();
156         LOGGER.debug("Started combining the schema from list of files {} for version {}", files, v);
157
158         for (String file : files) {
159             InputStream inputStream = new FileInputStream(file);
160
161             final Document doc = docBuilder.parse(inputStream);
162             final NodeList list = doc.getElementsByTagName("java-type");
163
164             for(int i = 0; i < list.getLength(); i++){
165                 Node curNode = list.item(i);
166                 String name = curNode.getAttributes().getNamedItem("name").getNodeValue();
167                 nodeMultimap.put(name, curNode);
168             }
169         }
170
171         Map<String, Collection<Node>> map = nodeMultimap.asMap();
172         createNode(combinedDoc, javaTypesContainer, map);
173
174         LOGGER.debug("Successfully merged all schema files for version {}", v);
175
176         return combinedDoc;
177     }
178
179     private void createNode(Document combinedDoc, Node javaTypesContainer, Map<String, Collection<Node>> map){
180
181         for (Entry<String, Collection<Node>> entry : map.entrySet()) {
182
183             List<Node> listOfNodes = (List<Node>)entry.getValue();
184             LOGGER.trace("NodeType {} Occurrences {}", entry.getKey(), listOfNodes.size());
185             Node copyOfFirstElement = null;
186             Node javaAttributeElement = null;
187
188             if(listOfNodes.size() > 1){
189                 for(int index = 0; index < listOfNodes.size(); index++){
190                     if(index == 0){
191                         Node currentNode = listOfNodes.get(index);
192                         copyOfFirstElement = combinedDoc.importNode(currentNode, true);
193                         if(copyOfFirstElement.getNodeType() == Node.ELEMENT_NODE){
194                             Element element = (Element) copyOfFirstElement;
195                             NodeList javaAttributesList = element.getElementsByTagName("java-attributes");
196                             for(int javaAttributeIndex = 0; javaAttributeIndex < javaAttributesList.getLength(); javaAttributeIndex++){
197                                 javaAttributeElement = javaAttributesList.item(javaAttributeIndex);
198                             }
199                         }
200                     } else {
201                         Node currentNode = listOfNodes.get(index);
202                         Node copyOfCurrentElement = combinedDoc.importNode(currentNode, true);
203                         if(copyOfCurrentElement.getNodeType() == Node.ELEMENT_NODE){
204                             Element element = (Element) copyOfCurrentElement;
205                             NodeList javaAttributesList = element.getElementsByTagName("java-attributes");
206                             for(int javaAttributeIndex = 0; javaAttributeIndex < javaAttributesList.getLength(); javaAttributeIndex++){
207                                 Node jaElement = javaAttributesList.item(javaAttributeIndex);
208                                 NodeList xmlElementList = jaElement.getChildNodes();
209                                 for(int xmlElementIndex = 0; xmlElementIndex < xmlElementList.getLength(); xmlElementIndex++){
210                                     if(javaAttributeElement != null){
211                                         Node curElem = xmlElementList.item(xmlElementIndex);
212                                         if(curElem != null){
213                                             javaAttributeElement.appendChild(curElem.cloneNode(true));
214                                         }
215                                     }
216                                 }
217                             }
218                         }
219
220                     }
221                 }
222                 javaTypesContainer.appendChild(copyOfFirstElement);
223             } else if(listOfNodes.size() == 1){
224                 javaTypesContainer.appendChild(combinedDoc.importNode(listOfNodes.get(0), true));
225             }
226         }
227     }
228
229     /**
230      * Gets the DynamicJAXBContext for the given version
231      *
232      * @param v
233      * @return DynamicJAXBContext
234      */
235     public DynamicJAXBContext getContextForVersion(SchemaVersion v) {
236         return versionContextMap.get(v);
237     }
238
239     /**
240      * Determines if the given version contains the given node type
241      *
242      * @param nodeType - node type to check, must be in lower hyphen form (ie "type-name")
243      * @param v        - schema version to check against
244      * @return
245      */
246     public boolean hasNodeType(String nodeType, SchemaVersion v) {
247         return typesPerVersion.get(v).contains(nodeType);
248     }
249
250     public Set<String> getObjectsInVersion(SchemaVersion v) {
251         return typesPerVersion.get(v);
252     }
253
254     /**
255      * Determines if the given version contains the given node type
256      *
257      * @param nodeType - node type to check, must be in lower hyphen form (ie "type-name")
258      * @param v
259      * @return
260      */
261     public Document getSchema(SchemaVersion v) {
262         return schemaPerVersion.get(v);
263     }
264
265     private InputStream getShell(SchemaVersion v) {
266         String source = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
267             "<xml-bindings xmlns=\"http://www.eclipse.org/eclipselink/xsds/persistence/oxm\" package-name=\"inventory.aai.onap.org." + v.toString().toLowerCase() + "\" xml-mapping-metadata-complete=\"true\">\n" +
268             "   <xml-schema element-form-default=\"QUALIFIED\">\n" +
269             "           <xml-ns namespace-uri=\"http://org.onap.aai.inventory/" + v.toString().toLowerCase() + "\" />\n" +
270             "   </xml-schema>\n" +
271             "   <java-types>\n" +
272             "   </java-types>\n" +
273             "</xml-bindings>";
274         return new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8));
275     }
276
277
278     public SchemaVersion getVersionFromClassName(String classname) {
279         Matcher m = classNamePattern.matcher(classname);
280         String version = null;
281         if (m.find()) {
282             version = m.group(1);
283             return new SchemaVersion(version);
284         } else {
285             return translator.getSchemaVersions().getDefaultVersion();
286         }
287     }
288 }
289