Fix metadataSet serialization for db persistence and retrieval.
[policy/models.git] / models-tosca / src / main / java / org / onap / policy / models / tosca / simple / concepts / JpaToscaEntityType.java
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2019-2020,2022 Nordix Foundation.
4  *  Modifications Copyright (C) 2019-2021 AT&T Intellectual Property. All rights reserved.
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.models.tosca.simple.concepts;
23
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Map.Entry;
27 import java.util.TreeMap;
28 import javax.persistence.AttributeOverride;
29 import javax.persistence.Column;
30 import javax.persistence.ElementCollection;
31 import javax.persistence.EmbeddedId;
32 import javax.persistence.Lob;
33 import javax.persistence.MappedSuperclass;
34 import javax.ws.rs.core.Response;
35 import lombok.Data;
36 import lombok.EqualsAndHashCode;
37 import lombok.NonNull;
38 import org.apache.commons.lang3.ObjectUtils;
39 import org.onap.policy.common.parameters.annotations.NotBlank;
40 import org.onap.policy.common.parameters.annotations.NotNull;
41 import org.onap.policy.common.utils.coder.CoderException;
42 import org.onap.policy.common.utils.coder.StandardCoder;
43 import org.onap.policy.models.base.PfAuthorative;
44 import org.onap.policy.models.base.PfConcept;
45 import org.onap.policy.models.base.PfConceptKey;
46 import org.onap.policy.models.base.PfKey;
47 import org.onap.policy.models.base.PfModelRuntimeException;
48 import org.onap.policy.models.base.PfUtils;
49 import org.onap.policy.models.base.validation.annotations.VerifyKey;
50 import org.onap.policy.models.tosca.authorative.concepts.ToscaEntity;
51
52 /**
53  * Class to represent the EntrySchema of list/map property in TOSCA definition.
54  */
55 @MappedSuperclass
56 @Data
57 @EqualsAndHashCode(callSuper = false)
58 public class JpaToscaEntityType<T extends ToscaEntity> extends PfConcept implements PfAuthorative<T> {
59     private static final long serialVersionUID = -1330661834220739393L;
60
61     private static final StandardCoder STANDARD_CODER = new StandardCoder();
62
63     @EmbeddedId
64     @VerifyKey
65     @NotNull
66     private PfConceptKey key;
67
68     // @formatter:off
69     @Column
70     @AttributeOverride(name = "name", column = @Column(name = "derived_from_name"))
71     @AttributeOverride(name = "version", column = @Column(name = "derived_from_version"))
72     @VerifyKey
73     private PfConceptKey derivedFrom;
74
75     @ElementCollection
76     @Lob
77     private Map<@NotNull @NotBlank String, @NotNull @NotBlank String> metadata;
78
79     @Column
80     @NotBlank
81     private String description;
82
83     private transient T toscaEntity;
84     // @formatter:on
85
86     /**
87      * The Default Constructor creates a {@link JpaToscaEntityType} object with a null key.
88      */
89     public JpaToscaEntityType() {
90         this(new PfConceptKey());
91     }
92
93     /**
94      * The Key Constructor creates a {@link JpaToscaEntityType} object with the given concept key.
95      *
96      * @param key the key
97      */
98     public JpaToscaEntityType(@NonNull final PfConceptKey key) {
99         this.key = key;
100     }
101
102     /**
103      * Copy constructor.
104      *
105      * @param copyConcept the concept to copy from
106      */
107     public JpaToscaEntityType(final JpaToscaEntityType<T> copyConcept) {
108         super(copyConcept);
109         this.key = new PfConceptKey(copyConcept.key);
110         this.derivedFrom = (copyConcept.derivedFrom != null ? new PfConceptKey(copyConcept.derivedFrom) : null);
111         this.metadata = (copyConcept.metadata != null ? new TreeMap<>(copyConcept.metadata) : null);
112         this.description = copyConcept.description;
113     }
114
115     /**
116      * Authorative constructor.
117      *
118      * @param authorativeConcept the authorative concept to copy from
119      */
120     public JpaToscaEntityType(final T authorativeConcept) {
121         this.fromAuthorative(authorativeConcept);
122     }
123
124     @Override
125     public T toAuthorative() {
126         toscaEntity.setName(getKey().getName());
127         toscaEntity.setVersion(getKey().getVersion());
128
129         if (derivedFrom != null) {
130             toscaEntity.setDerivedFrom(derivedFrom.getName());
131         }
132
133         if (description != null) {
134             toscaEntity.setDescription(description);
135         }
136
137         toscaEntity.setMetadata(PfUtils.mapMap(metadata, this::deserializeMetadataValue));
138
139         return toscaEntity;
140     }
141
142     @Override
143     public void fromAuthorative(T toscaEntity) {
144         key = new PfConceptKey();
145
146         if (toscaEntity.getName() != null) {
147             key.setName(toscaEntity.getName());
148         }
149
150         if (toscaEntity.getVersion() != null) {
151             key.setVersion(toscaEntity.getVersion());
152         }
153
154         if (toscaEntity.getDerivedFrom() != null) {
155             // Check if the derived from field contains a name-version ID
156             if (toscaEntity.getDerivedFrom().contains(":")) {
157                 derivedFrom = new PfConceptKey(toscaEntity.getDerivedFrom());
158             } else {
159                 derivedFrom = new PfConceptKey(toscaEntity.getDerivedFrom(), PfKey.NULL_KEY_VERSION);
160             }
161         }
162
163         if (toscaEntity.getDescription() != null) {
164             description = toscaEntity.getDescription();
165         }
166
167         metadata = PfUtils.mapMap(toscaEntity.getMetadata(), this::serializeMetadataValue);
168     }
169
170     @Override
171     public List<PfKey> getKeys() {
172         final List<PfKey> keyList = getKey().getKeys();
173         if (derivedFrom != null) {
174             keyList.addAll(derivedFrom.getKeys());
175         }
176         return keyList;
177     }
178
179     @Override
180     public void clean() {
181         key.clean();
182
183         if (derivedFrom != null) {
184             derivedFrom.clean();
185         }
186
187         if (metadata != null) {
188             for (Entry<String, String> metadataEntry : metadata.entrySet()) {
189                 metadataEntry.setValue(metadataEntry.getValue().trim());
190             }
191         }
192
193         description = (description != null ? description.trim() : null);
194     }
195
196     @Override
197     public int compareTo(final PfConcept otherConcept) {
198         if (otherConcept == null) {
199             return -1;
200         }
201         if (this == otherConcept) {
202             return 0;
203         }
204         if (getClass() != otherConcept.getClass()) {
205             return getClass().getName().compareTo(otherConcept.getClass().getName());
206         }
207
208         @SuppressWarnings("unchecked")
209         final JpaToscaEntityType<T> other = (JpaToscaEntityType<T>) otherConcept;
210
211         int result = key.compareTo(other.key);
212         if (result != 0) {
213             return result;
214         }
215
216         result = ObjectUtils.compare(derivedFrom, other.derivedFrom);
217         if (result != 0) {
218             return result;
219         }
220
221         result = PfUtils.compareMaps(metadata, other.metadata);
222         if (result != 0) {
223             return result;
224         }
225
226         return ObjectUtils.compare(description, other.description);
227     }
228
229     protected Object deserializeMetadataValue(String metadataValue) {
230         try {
231             return STANDARD_CODER.decode(metadataValue, Object.class);
232         } catch (CoderException ce) {
233             String errorMessage = "error decoding metadata JSON value read from database: " + metadataValue;
234             throw new PfModelRuntimeException(Response.Status.INTERNAL_SERVER_ERROR, errorMessage, ce);
235         }
236     }
237
238     protected String serializeMetadataValue(Object metadataValue) {
239         try {
240             return STANDARD_CODER.encode(metadataValue);
241         } catch (CoderException ce) {
242             String errorMessage = "error encoding metadata JSON value for database: " + metadataValue;
243             throw new PfModelRuntimeException(Response.Status.INTERNAL_SERVER_ERROR, errorMessage, ce);
244         }
245     }
246 }