Merge "Release 1.14.0 maven artifact"
[aai/aai-common.git] / aai-schema-ingest / src / main / java / org / onap / aai / nodes / NodeIngestor.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Modifications Copyright © 2018 IBM.
8  * ================================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *    http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ============LICENSE_END=========================================================
21  */
22
23 package org.onap.aai.nodes;
24
25 import com.google.common.base.CaseFormat;
26
27 import java.io.ByteArrayInputStream;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.nio.charset.StandardCharsets;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Set;
36 import java.util.regex.Matcher;
37 import java.util.regex.Pattern;
38
39 import javax.annotation.PostConstruct;
40 import javax.xml.XMLConstants;
41 import javax.xml.bind.JAXBException;
42 import javax.xml.parsers.DocumentBuilder;
43 import javax.xml.parsers.DocumentBuilderFactory;
44 import javax.xml.parsers.ParserConfigurationException;
45
46 import org.eclipse.persistence.jaxb.JAXBContextProperties;
47 import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext;
48 import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContextFactory;
49 import org.onap.aai.setup.ConfigTranslator;
50 import org.onap.aai.setup.SchemaVersion;
51 import org.onap.aai.setup.SchemaVersions;
52 import org.onap.aai.setup.Translator;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55 import org.springframework.beans.factory.annotation.Autowired;
56 import org.springframework.context.annotation.PropertySource;
57 import org.springframework.stereotype.Component;
58 import org.w3c.dom.Document;
59 import org.w3c.dom.Node;
60 import org.w3c.dom.NodeList;
61 import org.xml.sax.SAXException;
62
63 @Component
64 /*
65  * NodeIngestor - ingests A&AI OXM files per given config, serves DynamicJAXBContext per version
66  */
67 @PropertySource(value = "classpath:schema-ingest.properties", ignoreResourceNotFound = true)
68 @PropertySource(value = "file:${schema.ingest.file}", ignoreResourceNotFound = true)
69 public class NodeIngestor {
70
71     private static final Logger LOGGER = LoggerFactory.getLogger(NodeIngestor.class);
72     private static final Pattern classNamePattern = Pattern.compile("\\.(v\\d+)\\.");
73     private Map<SchemaVersion, DynamicJAXBContext> versionContextMap = new HashMap<>();
74     private Map<SchemaVersion, Set<String>> typesPerVersion = new HashMap<>();
75     private Map<SchemaVersion, Document> schemaPerVersion = new HashMap<>();
76     private String localSchema;
77     private SchemaVersions schemaVersions;
78     private Set<Translator> translators;
79
80     private CaseFormatStore caseFormatStore;
81     // TODO : See if you can get rid of InputStream resets
82
83     /**
84      * Instantiates the NodeIngestor bean.
85      *
86      * @param translatorSet
87      */
88
89     @Autowired
90     public NodeIngestor(Set<Translator> translatorSet) {
91         this.translators = translatorSet;
92         this.caseFormatStore = new CaseFormatStore();
93     }
94
95     @PostConstruct
96     public void initialize() {
97
98         for (Translator translator : translators) {
99             try {
100                 LOGGER.debug("Processing the translator");
101                 translateAll(translator);
102
103             } catch (Exception e) {
104                 LOGGER.error("Error while Processing the translator" + e.getMessage());
105                 throw new ExceptionInInitializerError("NodeIngestor could not ingest schema");
106             }
107         }
108         if (versionContextMap.isEmpty() || schemaPerVersion.isEmpty() || typesPerVersion.isEmpty()) {
109             throw new ExceptionInInitializerError("NodeIngestor could not ingest schema");
110         }
111     }
112
113     private void translateAll(Translator translator) throws ExceptionInInitializerError {
114         if (translator instanceof ConfigTranslator) {
115             this.localSchema = "true";
116         }
117
118         Boolean retrieveLocalSchema = Boolean.parseBoolean(this.localSchema);
119         /*
120          * Set this to default schemaVersion
121          */
122         this.schemaVersions = translator.getSchemaVersions();
123         List<SchemaVersion> schemaVersionList = translator.getSchemaVersions().getVersions();
124
125         try {
126             for (SchemaVersion version : schemaVersionList) {
127                 LOGGER.debug("Version being processed" + version);
128                 List<InputStream> inputStreams = retrieveOXM(version, translator);
129                 LOGGER.debug("Retrieved OXMs from SchemaService");
130                 /*
131                  * IOUtils.copy and copy the inputstream
132                  */
133                 if (inputStreams.isEmpty()) {
134                     continue;
135                 }
136
137                 final DynamicJAXBContext ctx = ingest(inputStreams);
138                 versionContextMap.put(version, ctx);
139                 setAllTypesAndProperties(version, inputStreams);
140                 schemaPerVersion.put(version, createCombinedSchema(inputStreams, version, retrieveLocalSchema));
141             }
142         } catch (JAXBException | ParserConfigurationException | SAXException | IOException e) {
143             LOGGER.error("Schema ingestion failed", e);
144             throw new ExceptionInInitializerError(e);
145         }
146     }
147
148     /**
149      * Ingests the given OXM files into DynamicJAXBContext
150      *
151      * @param inputStreams - inputStrean of oxms from SchemaService to be ingested
152      *
153      * @return DynamicJAXBContext including schema information from all given files
154      *
155      * @throws JAXBException if there's an error creating the DynamicJAXBContext
156      */
157     private DynamicJAXBContext ingest(List<InputStream> inputStreams) throws JAXBException {
158         Map<String, Object> properties = new HashMap<>();
159         properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, inputStreams);
160         LOGGER.debug("Ingested the InputStream");
161         return DynamicJAXBContextFactory.createContextFromOXM(this.getClass().getClassLoader(), properties);
162     }
163
164     private void setAllTypesAndProperties(SchemaVersion version, List<InputStream> inputStreams)
165             throws ParserConfigurationException, IOException, SAXException {
166         Set<String> types = new HashSet<>();
167         final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
168         docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
169         docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
170         docFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
171         final DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
172
173         for (InputStream inputStream : inputStreams) {
174             // TODO Change this
175             inputStream.reset();
176             final Document doc = docBuilder.parse(inputStream);
177             final NodeList list = doc.getElementsByTagName("java-type");
178             getAllNodeTypes(list, types);
179             caseFormatStore.parse(doc);
180         }
181
182         LOGGER.debug("Types size {}", types.size());
183         typesPerVersion.put(version, types);
184     }
185
186     private void getAllNodeTypes(NodeList list, Set<String> types) {
187
188         for (int i = 0; i < list.getLength(); i++) {
189             String type = list.item(i).getAttributes().getNamedItem("name").getNodeValue();
190             types.add(CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, type));
191         }
192     }
193
194     private Document createCombinedSchema(List<InputStream> inputStreams, SchemaVersion version, boolean localSchema)
195             throws ParserConfigurationException, SAXException, IOException {
196         if (localSchema) {
197             return createCombinedSchema(inputStreams, version);
198         }
199
200         InputStream inputStream = inputStreams.get(0);
201         inputStream.reset();
202         final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
203         docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
204         docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
205         docFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
206         DocumentBuilder masterDocBuilder = docFactory.newDocumentBuilder();
207         return masterDocBuilder.parse(inputStream);
208     }
209
210     private Document createCombinedSchema(List<InputStream> inputStreams, SchemaVersion version)
211             throws ParserConfigurationException, SAXException, IOException {
212         final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
213         docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
214         docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
215         docFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
216         final DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
217         DocumentBuilder masterDocBuilder = docFactory.newDocumentBuilder();
218         Document combinedDoc = masterDocBuilder.parse(getShell(version));
219         NodeList masterList = combinedDoc.getElementsByTagName("java-types");
220         Node javaTypesContainer = masterList.getLength() == 0 ? combinedDoc.getDocumentElement() : masterList.item(0);
221
222         for (InputStream inputStream : inputStreams) {
223             inputStream.reset();
224             final Document doc = docBuilder.parse(inputStream);
225             final NodeList list = doc.getElementsByTagName("java-type");
226             for (int i = 0; i < list.getLength(); i++) {
227                 Node copy = combinedDoc.importNode(list.item(i), true);
228                 javaTypesContainer.appendChild(copy);
229             }
230         }
231         return combinedDoc;
232     }
233
234     /**
235      * Gets the DynamicJAXBContext for the given version
236      *
237      * @param v - schema version to retrieve the context
238      * @return DynamicJAXBContext
239      */
240     public DynamicJAXBContext getContextForVersion(SchemaVersion v) {
241         return versionContextMap.get(v);
242     }
243
244     /**
245      * Determines if the given version contains the given node type
246      *
247      * @param nodeType - node type to check, must be in lower hyphen form (ie "type-name")
248      * @param v - schema version to check against
249      * @return boolean
250      */
251     public boolean hasNodeType(String nodeType, SchemaVersion v) {
252         return typesPerVersion.get(v).contains(nodeType);
253     }
254
255     public Set<String> getObjectsInVersion(SchemaVersion v) {
256         return typesPerVersion.get(v);
257     }
258
259     /**
260      * Determines if the given version contains the given node type
261      *
262      * @param v - Schemaversion to retrieve the schema
263      * @return Document
264      */
265     public Document getSchema(SchemaVersion v) {
266         return schemaPerVersion.get(v);
267     }
268
269     public SchemaVersion getVersionFromClassName(String classname) {
270         Matcher m = classNamePattern.matcher(classname);
271         if (m.find()) {
272             String version = m.group(1);
273             return new SchemaVersion(version);
274         } else {
275             return this.schemaVersions.getDefaultVersion();
276         }
277     }
278
279     private List<InputStream> retrieveOXM(SchemaVersion version, Translator translator) throws IOException {
280         /*
281          * Call Schema MS to get versions using RestTemplate or Local
282          */
283         return translator.getVersionNodeStream(version);
284
285     }
286
287     private InputStream getShell(SchemaVersion v) {
288         String source = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
289                 + "<xml-bindings xmlns=\"http://www.eclipse.org/eclipselink/xsds/persistence/oxm\" package-name=\"inventory.aai.onap.org."
290                 + v.toString().toLowerCase() + "\" xml-mapping-metadata-complete=\"true\">\n"
291                 + "     <xml-schema element-form-default=\"QUALIFIED\">\n"
292                 + "             <xml-ns namespace-uri=\"http://org.onap.aai.inventory/" + v.toString().toLowerCase() + "\" />\n"
293                 + "     </xml-schema>\n" + "    <java-types>\n" + "     </java-types>\n" + "</xml-bindings>";
294         return new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8));
295     }
296
297     public CaseFormatStore getCaseFormatStore() {
298         return caseFormatStore;
299     }
300 }