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