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