b593b3e9f5db7807975d7936fd569dbac5ba1cb4
[policy/apex-pdp.git] / model / basic-model / src / main / java / org / onap / policy / apex / model / basicmodel / handling / ApexModelReader.java
1 /*
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
4  *  Modifications Copyright (C) 2019-2020 Nordix Foundation.
5  * ================================================================================
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * SPDX-License-Identifier: Apache-2.0
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.policy.apex.model.basicmodel.handling;
23
24 import java.io.BufferedReader;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.InputStreamReader;
28 import java.io.StringReader;
29 import java.net.URL;
30 import java.util.regex.Pattern;
31
32 import javax.xml.XMLConstants;
33 import javax.xml.bind.JAXBContext;
34 import javax.xml.bind.JAXBElement;
35 import javax.xml.bind.JAXBException;
36 import javax.xml.bind.Unmarshaller;
37 import javax.xml.transform.stream.StreamSource;
38 import javax.xml.validation.Schema;
39 import javax.xml.validation.SchemaFactory;
40
41 import org.eclipse.persistence.jaxb.JAXBContextFactory;
42 import org.eclipse.persistence.jaxb.MarshallerProperties;
43 import org.eclipse.persistence.oxm.MediaType;
44 import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
45 import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
46 import org.onap.policy.common.utils.resources.ResourceUtils;
47 import org.onap.policy.common.utils.resources.TextFileUtils;
48 import org.onap.policy.common.utils.validation.Assertions;
49 import org.slf4j.ext.XLogger;
50 import org.slf4j.ext.XLoggerFactory;
51
52 /**
53  * This class reads an Apex concept from an XML file into a Java Apex Concept {@link AxConcept}.
54  *
55  * @author Liam Fallon (liam.fallon@ericsson.com)
56  * @param <C> the type of Apex concept to read, must be a sub class of {@link AxConcept}
57  */
58 public class ApexModelReader<C extends AxConcept> {
59     // Get a reference to the logger
60     private static final XLogger LOGGER = XLoggerFactory.getXLogger(ApexModelReader.class);
61
62     // Regular expressions for checking input types
63     // (starts with <?xml...>
64     private static final String XML_INPUT_TYPE_REGEXP = "^\\s*<\\?xml.*>\\s*";
65     // starts with some kind of bracket [ or (
66     private static final String JSON_INPUT_TYPE_REGEXP = "^\\s*[\\(\\{\\[][\\s+\\S]*[\\)\\}\\]]";
67     // or {, then has something, then has
68     // and has a close bracket
69
70     //  The root class of the concept we are reading
71     private final Class<C> rootConceptClass;
72
73     // The unmarshaller for the Apex concepts
74     private Unmarshaller unmarshaller = null;
75
76     // All read concepts are validated after reading if this flag is set
77     private boolean validateFlag = true;
78
79     /**
80      * Constructor, initiates the reader with validation on.
81      *
82      * @param rootConceptClass the root concept class for concept reading
83      * @throws ApexModelException the apex concept reader exception
84      */
85     public ApexModelReader(final Class<C> rootConceptClass) throws ApexModelException {
86         // Save the root concept class
87         this.rootConceptClass = rootConceptClass;
88
89         try {
90             final JAXBContext jaxbContext = JAXBContextFactory.createContext(new Class[] {rootConceptClass}, null);
91
92             // Set up the unmarshaller to carry out validation
93             unmarshaller = jaxbContext.createUnmarshaller();
94             unmarshaller.setEventHandler(new javax.xml.bind.helpers.DefaultValidationEventHandler());
95         } catch (final JAXBException e) {
96             LOGGER.error("Unable to set JAXB context", e);
97             throw new ApexModelException("Unable to set JAXB context", e);
98         }
99     }
100
101     /**
102      * Constructor, initiates the reader.
103      *
104      * @param rootConceptClass the root concept class for concept reading
105      * @param validate whether to perform validation by default
106      * @throws ApexModelException the apex concept reader exception
107      */
108     public ApexModelReader(final Class<C> rootConceptClass, final boolean validate) throws ApexModelException {
109         this(rootConceptClass);
110         this.validateFlag = validate;
111     }
112
113     /**
114      * Set the schema to use for reading XML files.
115      *
116      * @param schemaFileName the schema file to use
117      * @throws ApexModelException if the schema cannot be set
118      */
119     public void setSchema(final String schemaFileName) throws ApexModelException {
120         // Has a schema been set
121         if (schemaFileName != null) {
122             try {
123                 // Set the concept schema
124                 final URL schemaUrl = ResourceUtils.getUrlResource(schemaFileName);
125                 final Schema apexConceptSchema =
126                         SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(schemaUrl);
127                 unmarshaller.setSchema(apexConceptSchema);
128             } catch (final Exception e) {
129                 LOGGER.error("Unable to load schema ", e);
130                 throw new ApexModelException("Unable to load schema", e);
131             }
132         } else {
133             // Clear the schema
134             unmarshaller.setSchema(null);
135         }
136     }
137
138     /**
139      * This method checks the specified Apex concept XML file and reads it into an Apex concept.
140      *
141      * @param apexConceptStream the apex concept stream
142      * @return the Apex concept
143      * @throws ApexModelException on reading exceptions
144      */
145     public C read(final InputStream apexConceptStream) throws ApexModelException {
146         Assertions.argumentNotNull(apexConceptStream, "concept stream may not be null");
147
148         return read(new BufferedReader(new InputStreamReader(apexConceptStream)));
149     }
150
151     /**
152      * This method reads the specified Apex reader into an Apex concept.
153      *
154      * @param apexConceptReader the apex concept reader
155      * @return the Apex concept
156      * @throws ApexModelException on reading exceptions
157      */
158     public C read(final BufferedReader apexConceptReader) throws ApexModelException {
159         Assertions.argumentNotNull(apexConceptReader, "concept reader may not be null");
160
161         LOGGER.entry("reading Apex concept into a String . . .");
162
163         // Get the Apex concept as a string
164         String apexConceptString = null;
165         try {
166             apexConceptString = TextFileUtils.getReaderAsString(apexConceptReader).trim();
167         } catch (final IOException e) {
168             throw new ApexModelException("Unable to read Apex concept ", e);
169         }
170
171         return read(apexConceptString);
172     }
173
174     /**
175      * This method reads the specified Apex string into an Apex concept.
176      *
177      * @param apexConceptString the apex concept as a string
178      * @return the Apex concept
179      * @throws ApexModelException on reading exceptions
180      */
181     public C read(final String apexConceptString) throws ApexModelException {
182         Assertions.argumentNotNull(apexConceptString, "concept string may not be null");
183
184         LOGGER.entry("reading Apex concept from string . . .");
185
186         final String apexString = apexConceptString.trim();
187
188         // Set the type of input for this stream
189         setInputType(apexString);
190
191         // The Apex Concept
192         C apexConcept = null;
193
194         // Use JAXB to read and verify the Apex concept XML file
195         try {
196             // Load the configuration file
197             final StreamSource source = new StreamSource(new StringReader(apexString));
198             final JAXBElement<C> rootElement = unmarshaller.unmarshal(source, rootConceptClass);
199             apexConcept = rootElement.getValue();
200         } catch (final JAXBException e) {
201             throw new ApexModelException("Unable to unmarshal Apex concept ", e);
202         }
203
204         LOGGER.debug("reading of Apex concept {} completed");
205
206         // Check if the concept should be validated
207         if (validateFlag) {
208             // Validate the configuration file
209             final AxValidationResult validationResult = apexConcept.validate(new AxValidationResult());
210             if (validationResult.isValid()) {
211                 return apexConcept;
212             } else {
213                 String message = "Apex concept validation failed" + validationResult.toString();
214                 LOGGER.error(message);
215                 throw new ApexModelException(message);
216             }
217         } else {
218             // No validation check
219             return apexConcept;
220         }
221     }
222
223     /**
224      * Gets the value of the validation flag.
225      *
226      * @return the validation flag value
227      */
228     public boolean getValidateFlag() {
229         return validateFlag;
230     }
231
232     /**
233      * Sets the validation flag.
234      *
235      * @param validateFlag the validation flag value
236      */
237     public void setValidateFlag(final boolean validateFlag) {
238         this.validateFlag = validateFlag;
239     }
240
241     /**
242      * Set the type of input for the concept reader.
243      *
244      * @param apexConceptString The stream with
245      * @throws ApexModelException on errors setting input type
246      */
247     private void setInputType(final String apexConceptString) throws ApexModelException {
248         // Check the input type
249         if (Pattern.compile(JSON_INPUT_TYPE_REGEXP).matcher(apexConceptString).find()) {
250             // is json
251             try {
252                 unmarshaller.setProperty(MarshallerProperties.MEDIA_TYPE, MediaType.APPLICATION_JSON);
253                 unmarshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, true);
254             } catch (final Exception e) {
255                 LOGGER.warn("JAXB error setting marshaller for JSON Input", e);
256                 throw new ApexModelException("JAXB error setting unmarshaller for JSON input", e);
257             }
258         } else if (Pattern.compile(XML_INPUT_TYPE_REGEXP).matcher(apexConceptString).find()) {
259             // is xml
260             try {
261                 unmarshaller.setProperty(MarshallerProperties.MEDIA_TYPE, MediaType.APPLICATION_XML);
262             } catch (final Exception e) {
263                 LOGGER.warn("JAXB error setting marshaller for XML Input", e);
264                 throw new ApexModelException("JAXB error setting unmarshaller for XML input", e);
265             }
266         } else {
267             LOGGER.warn("format of input for Apex concept is neither JSON nor XML");
268             throw new ApexModelException("format of input for Apex concept is neither JSON nor XML");
269         }
270     }
271
272 }