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