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;
26 import jakarta.xml.bind.JAXBException;
27 import java.io.ByteArrayInputStream;
29 import java.io.FileInputStream;
30 import java.io.FileNotFoundException;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.nio.charset.StandardCharsets;
34 import java.util.ArrayList;
35 import java.util.Collection;
36 import java.util.HashMap;
37 import java.util.HashSet;
38 import java.util.List;
40 import java.util.Map.Entry;
42 import java.util.TreeMap;
43 import java.util.regex.Matcher;
44 import java.util.regex.Pattern;
45 import javax.xml.XMLConstants;
46 import javax.xml.parsers.DocumentBuilder;
47 import javax.xml.parsers.DocumentBuilderFactory;
48 import javax.xml.parsers.ParserConfigurationException;
49 import org.eclipse.persistence.jaxb.JAXBContextProperties;
50 import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext;
51 import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContextFactory;
52 import org.onap.aai.schemaservice.config.ConfigTranslator;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55 import org.springframework.beans.factory.annotation.Autowired;
56 import org.springframework.stereotype.Component;
57 import org.w3c.dom.Document;
58 import org.w3c.dom.Element;
59 import org.w3c.dom.Node;
60 import org.w3c.dom.NodeList;
61 import org.xml.sax.SAXException;
64 * NodeIngestor - ingests A&AI OXM files per given config, serves DynamicJAXBContext per version
67 public class NodeIngestor {
69 private static final Logger LOGGER = LoggerFactory.getLogger(NodeIngestor.class);
71 private static final Pattern classNamePattern = Pattern.compile("\\.(v\\d+)\\.");
72 private Map<SchemaVersion, DynamicJAXBContext> versionContextMap = new TreeMap<>();
73 private Map<SchemaVersion, Set<String>> typesPerVersion = new TreeMap<>();
74 private Map<SchemaVersion, Document> schemaPerVersion = new TreeMap<>();
75 private ConfigTranslator translator;
80 * Instantiates the NodeIngestor bean.
82 * @param translator - ConfigTranslator autowired in by Spring framework which
83 * contains the configuration information needed to ingest the desired files.
85 public NodeIngestor(ConfigTranslator translator) {
86 this.translator = translator;
87 Map<SchemaVersion, List<String>> filesToIngest = translator.getNodeFiles();
90 for (Entry<SchemaVersion, List<String>> verFiles : filesToIngest.entrySet()) {
91 SchemaVersion v = verFiles.getKey();
92 List<String> files = verFiles.getValue();
93 final DynamicJAXBContext ctx = ingest(files);
94 versionContextMap.put(v, ctx);
95 typesPerVersion.put(v, getAllNodeTypes(files));
96 schemaPerVersion.put(v, createCombinedSchema(files, v));
98 } catch (JAXBException | ParserConfigurationException | SAXException | IOException e) {
99 throw new ExceptionInInitializerError(e);
104 * Ingests the given OXM files into DynamicJAXBContext
106 * @param files - List<String> of full filenames (ie including the path) to be ingested
107 * @return DynamicJAXBContext including schema information from all given files
108 * @throws FileNotFoundException if an OXM file can't be found
109 * @throws JAXBException if there's an error creating the DynamicJAXBContext
111 private DynamicJAXBContext ingest(List<String> files)
112 throws FileNotFoundException, JAXBException {
113 List<InputStream> streams = new ArrayList<>();
115 for (String name : files) {
116 streams.add(new FileInputStream(new File(name)));
119 Map<String, Object> properties = new HashMap<>();
120 properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, streams);
121 return DynamicJAXBContextFactory
122 .createContextFromOXM(this.getClass().getClassLoader(), properties);
126 private Set<String> getAllNodeTypes(List<String> files)
127 throws ParserConfigurationException, SAXException, IOException {
128 Set<String> types = new HashSet<>();
129 final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
130 docFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
131 docFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
132 docFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
133 docFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
134 docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
135 docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
136 final DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
138 ArrayList<Node> javaTypes = new ArrayList<>();
139 for (String file : files) {
140 InputStream inputStream = new FileInputStream(file);
142 final Document doc = docBuilder.parse(inputStream);
143 final NodeList list = doc.getElementsByTagName("java-type");
146 for (int i = 0; i < list.getLength(); i++) {
147 String type = list.item(i).getAttributes().getNamedItem("name").getNodeValue();
148 javaTypes.add(list.item(i));
149 types.add(CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, type));
156 private Document createCombinedSchema(List<String> files, SchemaVersion v)
157 throws ParserConfigurationException, SAXException, IOException {
158 final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
159 docFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
160 docFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
161 docFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
162 docFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
163 docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
164 docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
165 final DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
166 DocumentBuilder masterDocBuilder = docFactory.newDocumentBuilder();
167 Document combinedDoc = masterDocBuilder.parse(getShell(v));
168 NodeList masterList = combinedDoc.getElementsByTagName("java-types");
169 Node javaTypesContainer =
170 masterList.getLength() == 0 ? combinedDoc.getDocumentElement() : masterList.item(0);
172 Multimap<String, Node> nodeMultimap = ArrayListMultimap.create();
173 LOGGER.debug("Started combining the schema from list of files {} for version {}", files, v);
175 for (String file : files) {
176 InputStream inputStream = new FileInputStream(file);
178 final Document doc = docBuilder.parse(inputStream);
179 final NodeList list = doc.getElementsByTagName("java-type");
181 for (int i = 0; i < list.getLength(); i++) {
182 Node curNode = list.item(i);
183 String name = curNode.getAttributes().getNamedItem("name").getNodeValue();
184 nodeMultimap.put(name, curNode);
188 Map<String, Collection<Node>> map = nodeMultimap.asMap();
189 createNode(combinedDoc, javaTypesContainer, map);
191 LOGGER.debug("Successfully merged all schema files for version {}", v);
196 private void createNode(Document combinedDoc, Node javaTypesContainer,
197 Map<String, Collection<Node>> map) {
199 for (Entry<String, Collection<Node>> entry : map.entrySet()) {
201 List<Node> listOfNodes = (List<Node>) entry.getValue();
202 LOGGER.trace("NodeType {} Occurrences {}", entry.getKey(), listOfNodes.size());
203 Node copyOfFirstElement = null;
204 Node javaAttributeElement = null;
206 if (listOfNodes.size() > 1) {
207 for (int index = 0; index < listOfNodes.size(); index++) {
209 Node currentNode = listOfNodes.get(index);
210 copyOfFirstElement = combinedDoc.importNode(currentNode, true);
211 if (copyOfFirstElement.getNodeType() == Node.ELEMENT_NODE) {
212 Element element = (Element) copyOfFirstElement;
213 NodeList javaAttributesList =
214 element.getElementsByTagName("java-attributes");
215 for (int javaAttributeIndex = 0;
216 javaAttributeIndex < javaAttributesList.getLength();
217 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;
229 javaAttributeIndex < javaAttributesList.getLength();
230 javaAttributeIndex++) {
231 Node jaElement = javaAttributesList.item(javaAttributeIndex);
232 NodeList xmlElementList = jaElement.getChildNodes();
233 for (int xmlElementIndex = 0;
234 xmlElementIndex < xmlElementList.getLength();
236 if (javaAttributeElement != null) {
237 Node curElem = xmlElementList.item(xmlElementIndex);
238 if (curElem != null) {
240 .appendChild(curElem.cloneNode(true));
249 javaTypesContainer.appendChild(copyOfFirstElement);
250 } else if (listOfNodes.size() == 1) {
251 javaTypesContainer.appendChild(combinedDoc.importNode(listOfNodes.get(0), true));
257 * Gets the DynamicJAXBContext for the given version
260 * @return DynamicJAXBContext
262 public DynamicJAXBContext getContextForVersion(SchemaVersion v) {
263 return versionContextMap.get(v);
267 * Determines if the given version contains the given node type
269 * @param nodeType - node type to check, must be in lower hyphen form (ie "type-name")
270 * @param v - schema version to check against
273 public boolean hasNodeType(String nodeType, SchemaVersion v) {
274 return typesPerVersion.get(v).contains(nodeType);
277 public Set<String> getObjectsInVersion(SchemaVersion v) {
278 return typesPerVersion.get(v);
281 public Document getSchema(SchemaVersion v) {
282 return schemaPerVersion.get(v);
285 private InputStream getShell(SchemaVersion v) {
286 String source = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
287 "<xml-bindings xmlns=\"http://www.eclipse.org/eclipselink/xsds/persistence/oxm\" package-name=\"inventory.aai.onap.org." +
288 v.toString().toLowerCase() + "\" xml-mapping-metadata-complete=\"true\">\n" +
289 " <xml-schema element-form-default=\"QUALIFIED\">\n" +
290 " <xml-ns namespace-uri=\"http://org.onap.aai.inventory/" +
291 v.toString().toLowerCase() + "\" />\n" +
296 return new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8));
300 public SchemaVersion getVersionFromClassName(String classname) {
301 Matcher m = classNamePattern.matcher(classname);
302 String version = null;
304 version = m.group(1);
305 return new SchemaVersion(version);
307 return translator.getSchemaVersions().getDefaultVersion();