37f44ba494d47e9b29ce8fcbaee4cdb58d4892c0
[policy/apex-pdp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
4  *  Modifications Copyright (C) 2019-2021 Nordix Foundation.
5  *  Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  * SPDX-License-Identifier: Apache-2.0
20  * ============LICENSE_END=========================================================
21  */
22
23 package org.onap.policy.apex.model.contextmodel.concepts;
24
25 import java.util.List;
26 import javax.persistence.Column;
27 import javax.persistence.Convert;
28 import javax.persistence.EmbeddedId;
29 import javax.persistence.Entity;
30 import javax.persistence.Table;
31 import javax.xml.bind.annotation.XmlAccessType;
32 import javax.xml.bind.annotation.XmlAccessorType;
33 import javax.xml.bind.annotation.XmlElement;
34 import javax.xml.bind.annotation.XmlRootElement;
35 import javax.xml.bind.annotation.XmlType;
36 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
37 import lombok.AccessLevel;
38 import lombok.Getter;
39 import lombok.ToString;
40 import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
41 import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
42 import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
43 import org.onap.policy.apex.model.basicmodel.concepts.AxValidationMessage;
44 import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
45 import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
46 import org.onap.policy.apex.model.basicmodel.dao.converters.CDataConditioner;
47 import org.onap.policy.common.utils.validation.Assertions;
48
49 /**
50  * This class holds a data schema definition in Apex. A data schema describes the structure of a single atom of data
51  * handled by Apex. This atom of data can be a primitive type such as an integer or a string, or it can be a more
52  * complex data type such as a Java object or an object described using a data definition language such as Avro. The
53  * schema flavour defines the type of schema being defined and the schema itself defines the schema. The schema flavour
54  * is used by Apex to look up and load a plugin class that understands and interprets the schema definition and can
55  * create instances of classes for the schema.
56  *
57  * <p>An {@link AxContextSchema} is used to define each parameter in Apex events, the messages that enter, exit, and are
58  * passed internally in Apex. In addition, an Apex {@link AxContextAlbum} instances hold a map of
59  * {@link AxContextSchema} instances to represent the context being managed as an {@link AxContextAlbum}. For example,
60  * the state of all cells in a mobile network might be represented as an {@link AxContextAlbum} with its
61  * {@link AxContextSchema} being defined as @code cell} objects.
62  *
63  * <p>Validation checks that the schema key is not null. It also checks that the schema flavour is defined and matches
64  * the regular expression SCHEMA_FLAVOUR_REGEXP. Finally, validation checks that the defined schema is not a blank or
65  * empty string.
66  */
67 @Entity
68 @Table(name = "AxContextSchema")
69
70 @Getter
71 @ToString
72
73 @XmlAccessorType(XmlAccessType.FIELD)
74 @XmlRootElement(name = "apexContextSchema", namespace = "http://www.onap.org/policy/apex-pdp")
75 @XmlType(name = "AxContextSchema", namespace = "http://www.onap.org/policy/apex-pdp", propOrder =
76     { "key", "schemaFlavour", "schemaDefinition" })
77
78 public class AxContextSchema extends AxConcept {
79     private static final String SCHEMA_FLAVOUR = "schemaFlavour";
80     private static final String WHITESPACE_REGEXP = "\\s+$";
81
82     private static final long serialVersionUID = -6443016863162692288L;
83
84     /** Regular expression that constrains what values a schema flavour can have. */
85     public static final String SCHEMA_FLAVOUR_REGEXP = "[A-Za-z0-9\\-_]+";
86
87     /** An undefined schema flavour has this value. */
88     public static final String SCHEMA_FLAVOUR_UNDEFINED = "UNDEFINED";
89
90     /** The maximum permissible size of a schema definition. */
91     public static final int MAX_SCHEMA_SIZE = 32672; // The maximum size supported by Apache Derby
92
93     @EmbeddedId
94     @XmlElement(name = "key", required = true)
95     private AxArtifactKey key;
96
97     @Column(name = SCHEMA_FLAVOUR)
98     @XmlElement(required = true)
99     private String schemaFlavour;
100
101     @Column(name = "schemaDefinition", length = MAX_SCHEMA_SIZE)
102     @Convert(converter = CDataConditioner.class)
103     @XmlJavaTypeAdapter(value = CDataConditioner.class)
104     @XmlElement(name = "schemaDefinition", required = true)
105     @Getter(AccessLevel.NONE)
106     private String schemaDefinition;
107
108     /**
109      * The default constructor creates a context schema with a null artifact key. The flavour of the context album is
110      * set as SCHEMA_FLAVOUR_UNDEFINED and the schema itself is defined as an empty string.
111      */
112     public AxContextSchema() {
113         this(new AxArtifactKey());
114         schemaFlavour = SCHEMA_FLAVOUR_UNDEFINED;
115     }
116
117     /**
118      * Copy constructor.
119      *
120      * @param copyConcept the concept to copy from
121      */
122     public AxContextSchema(final AxContextSchema copyConcept) {
123         super(copyConcept);
124     }
125
126     /**
127      * The key constructor creates a context schema with the given artifact key. The flavour of the context album is set
128      * as SCHEMA_FLAVOUR_UNDEFINED and the schema itself is defined as an empty string.
129      *
130      * @param key the key
131      */
132     public AxContextSchema(final AxArtifactKey key) {
133         this(key, SCHEMA_FLAVOUR_UNDEFINED, "");
134     }
135
136     /**
137      * This Constructor creates a context schema with all of its fields defined.
138      *
139      * @param key the key
140      * @param schemaFlavour the schema flavour
141      * @param schemaDefinition the schema definition
142      */
143     public AxContextSchema(final AxArtifactKey key, final String schemaFlavour, final String schemaDefinition) {
144         super();
145         Assertions.argumentNotNull(key, "key may not be null");
146         Assertions.argumentNotNull(schemaFlavour, "schemaFlavour may not be null");
147         Assertions.argumentNotNull(schemaDefinition, "schemaDefinition may not be null");
148
149         this.key = key;
150         this.schemaFlavour = Assertions.validateStringParameter(SCHEMA_FLAVOUR, schemaFlavour, SCHEMA_FLAVOUR_REGEXP);
151         this.schemaDefinition = schemaDefinition.replaceAll(WHITESPACE_REGEXP, "");
152     }
153
154     /**
155      * {@inheritDoc}.
156      */
157     @Override
158     public List<AxKey> getKeys() {
159         return key.getKeys();
160     }
161
162     /**
163      * Sets the key of the context schema.
164      *
165      * @param key the key of the context schema
166      */
167     public void setKey(final AxArtifactKey key) {
168         Assertions.argumentNotNull(key, "key may not be null");
169         this.key = key;
170     }
171
172     /**
173      * Sets the schema flavour, which defines the type of schema definition being used.
174      *
175      * @param schemaFlavour the schema flavour
176      */
177     public void setSchemaFlavour(final String schemaFlavour) {
178         this.schemaFlavour = Assertions.validateStringParameter(SCHEMA_FLAVOUR, schemaFlavour, SCHEMA_FLAVOUR_REGEXP);
179     }
180
181     /**
182      * Gets the schema, which defines the structure of this data schema atom.
183      *
184      * @return the schema definition
185      */
186     public String getSchema() {
187         return schemaDefinition;
188     }
189
190     /**
191      * Sets the schema, which defines the structure of this data schema atom.
192      *
193      * @param schema the schema definition
194      */
195     public void setSchema(final String schema) {
196         Assertions.argumentNotNull(schema, "schema may not be null");
197         this.schemaDefinition = schema.replaceAll(WHITESPACE_REGEXP, "");
198     }
199
200     /**
201      * {@inheritDoc}.
202      */
203     @Override
204     public AxValidationResult validate(final AxValidationResult resultIn) {
205         AxValidationResult result = resultIn;
206
207         if (key.equals(AxArtifactKey.getNullKey())) {
208             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
209                             "key is a null key"));
210         }
211
212         result = key.validate(result);
213
214         if (schemaFlavour.replaceAll(WHITESPACE_REGEXP, "").length() == 0
215                         || schemaFlavour.equals(SCHEMA_FLAVOUR_UNDEFINED)) {
216             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
217                             "schema flavour is not defined"));
218         }
219
220         var flavourValidationResult = Assertions.getStringParameterValidationMessage(SCHEMA_FLAVOUR, schemaFlavour,
221                         SCHEMA_FLAVOUR_REGEXP);
222         if (flavourValidationResult != null) {
223             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
224                             "schema flavour invalid-" + flavourValidationResult));
225         }
226
227         if (schemaDefinition.replaceAll(WHITESPACE_REGEXP, "").length() == 0) {
228             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
229                             "no schemaDefinition specified, schemaDefinition may not be blank"));
230         }
231
232         return result;
233     }
234
235     /**
236      * {@inheritDoc}.
237      */
238     @Override
239     public void clean() {
240         key.clean();
241         schemaFlavour = Assertions.validateStringParameter(SCHEMA_FLAVOUR, schemaFlavour, SCHEMA_FLAVOUR_REGEXP);
242         schemaDefinition = schemaDefinition.replaceAll(WHITESPACE_REGEXP, "");
243     }
244
245     /**
246      * {@inheritDoc}.
247      */
248     @Override
249     public AxConcept copyTo(final AxConcept target) {
250         Assertions.argumentNotNull(target, "target may not be null");
251
252         final Object copyObject = target;
253         Assertions.instanceOf(copyObject, AxContextSchema.class);
254
255         final AxContextSchema copy = ((AxContextSchema) copyObject);
256         copy.setKey(new AxArtifactKey(key));
257         copy.setSchemaFlavour(schemaFlavour);
258         copy.setSchema(schemaDefinition);
259
260         return copy;
261     }
262
263     /**
264      * {@inheritDoc}.
265      */
266     @Override
267     public int hashCode() {
268         final var prime = 31;
269         var result = 1;
270         result = prime * result + key.hashCode();
271         result = prime * result + schemaFlavour.hashCode();
272
273         final String thisSchema = CDataConditioner.clean(schemaDefinition).replace("\n", "");
274         result = prime * result + thisSchema.hashCode();
275         return result;
276     }
277
278     /**
279      * {@inheritDoc}.
280      */
281     @Override
282     public boolean equals(final Object obj) {
283         if (obj == null) {
284             return false;
285         }
286         if (this == obj) {
287             return true;
288         }
289
290         if (getClass() != obj.getClass()) {
291             return false;
292         }
293
294         final AxContextSchema other = (AxContextSchema) obj;
295
296         if (!key.equals(other.key)) {
297             return false;
298         }
299         if (!schemaFlavour.equals(other.schemaFlavour)) {
300             return false;
301         }
302         final String thisSchema = CDataConditioner.clean(schemaDefinition).replace("\n", "");
303         final String otherSchema = CDataConditioner.clean(other.schemaDefinition).replace("\n", "");
304         return thisSchema.equals(otherSchema);
305     }
306
307     /**
308      * {@inheritDoc}.
309      */
310     @Override
311     public int compareTo(final AxConcept otherObj) {
312         if (otherObj == null) {
313             return -1;
314         }
315         if (this == otherObj) {
316             return 0;
317         }
318         if (getClass() != otherObj.getClass()) {
319             return this.hashCode() - otherObj.hashCode();
320         }
321
322         final AxContextSchema other = (AxContextSchema) otherObj;
323         if (!key.equals(other.key)) {
324             return key.compareTo(other.key);
325         }
326         if (!schemaFlavour.equals(other.schemaFlavour)) {
327             return schemaFlavour.compareTo(other.schemaFlavour);
328         }
329         final String thisSchema = CDataConditioner.clean(schemaDefinition).replace("\n", "");
330         final String otherSchema = CDataConditioner.clean(other.schemaDefinition).replace("\n", "");
331         return thisSchema.compareTo(otherSchema);
332     }
333 }