2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.onap.aai.schemaservice.nodeschema;
23 import com.google.common.base.CaseFormat;
24 import com.google.common.collect.ArrayListMultimap;
25 import com.google.common.collect.Multimap;
27 import jakarta.xml.bind.JAXBException;
29 import java.io.ByteArrayInputStream;
31 import java.io.FileInputStream;
32 import java.io.FileNotFoundException;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.nio.charset.StandardCharsets;
36 import java.util.ArrayList;
37 import java.util.Collection;
38 import java.util.HashMap;
39 import java.util.HashSet;
40 import java.util.List;
42 import java.util.Map.Entry;
44 import java.util.TreeMap;
45 import java.util.regex.Matcher;
46 import java.util.regex.Pattern;
48 import javax.xml.XMLConstants;
49 import javax.xml.parsers.DocumentBuilder;
50 import javax.xml.parsers.DocumentBuilderFactory;
51 import javax.xml.parsers.ParserConfigurationException;
53 import org.eclipse.persistence.jaxb.JAXBContextProperties;
54 import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext;
55 import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContextFactory;
56 import org.onap.aai.schemaservice.config.ConfigTranslator;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59 import org.springframework.beans.factory.annotation.Autowired;
60 import org.springframework.stereotype.Component;
61 import org.w3c.dom.Document;
62 import org.w3c.dom.Element;
63 import org.w3c.dom.Node;
64 import org.w3c.dom.NodeList;
65 import org.xml.sax.SAXException;
68 * NodeIngestor - ingests A&AI OXM files per given config, serves DynamicJAXBContext per version
71 public class NodeIngestor {
73 private static final Logger LOGGER = LoggerFactory.getLogger(NodeIngestor.class);
75 private static final Pattern classNamePattern = Pattern.compile("\\.(v\\d+)\\.");
76 private Map<SchemaVersion, DynamicJAXBContext> versionContextMap = new TreeMap<>();
77 private Map<SchemaVersion, Set<String>> typesPerVersion = new TreeMap<>();
78 private Map<SchemaVersion, Document> schemaPerVersion = new TreeMap<>();
79 private ConfigTranslator translator;
83 * Instantiates the NodeIngestor bean.
85 * @param translator - ConfigTranslator autowired in by Spring framework which
86 * contains the configuration information needed to ingest the desired files.
88 public NodeIngestor(ConfigTranslator translator) {
89 this.translator = translator;
90 Map<SchemaVersion, List<String>> filesToIngest = translator.getNodeFiles();
93 for (Entry<SchemaVersion, List<String>> verFiles : filesToIngest.entrySet()) {
94 SchemaVersion v = verFiles.getKey();
95 List<String> files = verFiles.getValue();
96 final DynamicJAXBContext ctx = ingest(files);
97 versionContextMap.put(v, ctx);
98 typesPerVersion.put(v, getAllNodeTypes(files));
99 schemaPerVersion.put(v, createCombinedSchema(files, v));
101 } catch (JAXBException | ParserConfigurationException | SAXException | IOException e) {
102 throw new ExceptionInInitializerError(e);
107 * Ingests the given OXM files into DynamicJAXBContext
109 * @param files - List<String> of full filenames (ie including the path) to be ingested
110 * @return DynamicJAXBContext including schema information from all given files
111 * @throws FileNotFoundException if an OXM file can't be found
112 * @throws JAXBException if there's an error creating the DynamicJAXBContext
114 private DynamicJAXBContext ingest(List<String> files)
115 throws FileNotFoundException, JAXBException {
116 List<InputStream> streams = new ArrayList<>();
118 for (String name : files) {
119 streams.add(new FileInputStream(new File(name)));
122 Map<String, Object> properties = new HashMap<>();
123 properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, streams);
124 return DynamicJAXBContextFactory.createContextFromOXM(this.getClass().getClassLoader(),
128 private Set<String> getAllNodeTypes(List<String> files)
129 throws ParserConfigurationException, SAXException, IOException {
130 Set<String> types = new HashSet<>();
131 final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
132 docFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
133 docFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
134 docFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
135 docFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
136 docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
137 docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
138 final DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
140 ArrayList<Node> javaTypes = new ArrayList<>();
141 for (String file : files) {
142 InputStream inputStream = new FileInputStream(file);
144 final Document doc = docBuilder.parse(inputStream);
145 final NodeList list = doc.getElementsByTagName("java-type");
147 for (int i = 0; i < list.getLength(); i++) {
148 String type = list.item(i).getAttributes().getNamedItem("name").getNodeValue();
149 javaTypes.add(list.item(i));
150 types.add(CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, type));
157 private Document createCombinedSchema(List<String> files, SchemaVersion v)
158 throws ParserConfigurationException, SAXException, IOException {
159 final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
160 docFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
161 docFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
162 docFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
163 docFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
164 docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
165 docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
166 final DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
167 DocumentBuilder masterDocBuilder = docFactory.newDocumentBuilder();
168 Document combinedDoc = masterDocBuilder.parse(getShell(v));
169 NodeList masterList = combinedDoc.getElementsByTagName("java-types");
170 Node javaTypesContainer =
171 masterList.getLength() == 0 ? combinedDoc.getDocumentElement() : masterList.item(0);
173 Multimap<String, Node> nodeMultimap = ArrayListMultimap.create();
174 LOGGER.debug("Started combining the schema from list of files {} for version {}", files, v);
176 for (String file : files) {
177 InputStream inputStream = new FileInputStream(file);
179 final Document doc = docBuilder.parse(inputStream);
180 final NodeList list = doc.getElementsByTagName("java-type");
182 for (int i = 0; i < list.getLength(); i++) {
183 Node curNode = list.item(i);
184 String name = curNode.getAttributes().getNamedItem("name").getNodeValue();
185 nodeMultimap.put(name, curNode);
189 Map<String, Collection<Node>> map = nodeMultimap.asMap();
190 createNode(combinedDoc, javaTypesContainer, map);
192 LOGGER.debug("Successfully merged all schema files for version {}", v);
197 private void createNode(Document combinedDoc, Node javaTypesContainer,
198 Map<String, Collection<Node>> map) {
200 for (Entry<String, Collection<Node>> entry : map.entrySet()) {
202 List<Node> listOfNodes = (List<Node>) entry.getValue();
203 LOGGER.trace("NodeType {} Occurrences {}", entry.getKey(), listOfNodes.size());
204 Node copyOfFirstElement = null;
205 Node javaAttributeElement = null;
207 if (listOfNodes.size() > 1) {
208 for (int index = 0; index < listOfNodes.size(); index++) {
210 Node currentNode = listOfNodes.get(index);
211 copyOfFirstElement = combinedDoc.importNode(currentNode, true);
212 if (copyOfFirstElement.getNodeType() == Node.ELEMENT_NODE) {
213 Element element = (Element) copyOfFirstElement;
214 NodeList javaAttributesList =
215 element.getElementsByTagName("java-attributes");
216 for (int javaAttributeIndex = 0; javaAttributeIndex < javaAttributesList
217 .getLength(); javaAttributeIndex++) {
218 javaAttributeElement = javaAttributesList.item(javaAttributeIndex);
222 Node currentNode = listOfNodes.get(index);
223 Node copyOfCurrentElement = combinedDoc.importNode(currentNode, true);
224 if (copyOfCurrentElement.getNodeType() == Node.ELEMENT_NODE) {
225 Element element = (Element) copyOfCurrentElement;
226 NodeList javaAttributesList =
227 element.getElementsByTagName("java-attributes");
228 for (int javaAttributeIndex = 0; javaAttributeIndex < javaAttributesList
229 .getLength(); javaAttributeIndex++) {
230 Node jaElement = javaAttributesList.item(javaAttributeIndex);
231 NodeList xmlElementList = jaElement.getChildNodes();
232 for (int xmlElementIndex = 0; xmlElementIndex < xmlElementList
233 .getLength(); xmlElementIndex++) {
234 if (javaAttributeElement != null) {
235 Node curElem = xmlElementList.item(xmlElementIndex);
236 if (curElem != null) {
238 .appendChild(curElem.cloneNode(true));
247 javaTypesContainer.appendChild(copyOfFirstElement);
248 } else if (listOfNodes.size() == 1) {
249 javaTypesContainer.appendChild(combinedDoc.importNode(listOfNodes.get(0), true));
255 * Gets the DynamicJAXBContext for the given version
258 * @return DynamicJAXBContext
260 public DynamicJAXBContext getContextForVersion(SchemaVersion v) {
261 return versionContextMap.get(v);
265 * Determines if the given version contains the given node type
267 * @param nodeType - node type to check, must be in lower hyphen form (ie "type-name")
268 * @param v - schema version to check against
271 public boolean hasNodeType(String nodeType, SchemaVersion v) {
272 return typesPerVersion.get(v).contains(nodeType);
275 public Set<String> getObjectsInVersion(SchemaVersion v) {
276 return typesPerVersion.get(v);
279 public Document getSchema(SchemaVersion v) {
280 return schemaPerVersion.get(v);
283 private InputStream getShell(SchemaVersion v) {
284 String source = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
285 + "<xml-bindings xmlns=\"http://www.eclipse.org/eclipselink/xsds/persistence/oxm\" package-name=\"inventory.aai.onap.org."
286 + v.toString().toLowerCase() + "\" xml-mapping-metadata-complete=\"true\">\n"
287 + " <xml-schema element-form-default=\"QUALIFIED\">\n"
288 + " <xml-ns namespace-uri=\"http://org.onap.aai.inventory/"
289 + v.toString().toLowerCase() + "\" />\n" + " </xml-schema>\n" + " <java-types>\n"
290 + " </java-types>\n" + "</xml-bindings>";
291 return new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8));
294 public SchemaVersion getVersionFromClassName(String classname) {
295 Matcher m = classNamePattern.matcher(classname);
296 String version = null;
298 version = m.group(1);
299 return new SchemaVersion(version);
301 return translator.getSchemaVersions().getDefaultVersion();