Naming micro-service - upgrade to spring-boot-2.
[ccsdk/apps.git] / ms / neng / src / main / java / org / onap / ccsdk / apps / ms / neng / core / gen / NameGenerator.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP : CCSDK.apps
4  * ================================================================================
5  * Copyright (C) 2018 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  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.ccsdk.apps.ms.neng.core.gen;
22
23 import static org.onap.ccsdk.apps.ms.neng.core.policy.PolicyReader.namingModel;
24 import static org.onap.ccsdk.apps.ms.neng.core.policy.PolicyReader.namingModelRelaxed;
25 import static org.onap.ccsdk.apps.ms.neng.core.policy.PolicyReader.namingModels;
26 import static org.onap.ccsdk.apps.ms.neng.core.policy.PolicyReader.namingOperation;
27 import static org.onap.ccsdk.apps.ms.neng.core.policy.PolicyReader.namingProperty;
28 import static org.onap.ccsdk.apps.ms.neng.core.policy.PolicyReader.namingRecipe;
29 import static org.onap.ccsdk.apps.ms.neng.core.policy.PolicyReader.namingType;
30 import static org.onap.ccsdk.apps.ms.neng.core.policy.PolicyReader.propertyValue;
31 import static org.onap.ccsdk.apps.ms.neng.core.policy.PolicyReader.relaxedNamingType;
32 import static org.onap.ccsdk.apps.ms.neng.core.policy.PolicyReader.seq;
33 import static org.onap.ccsdk.apps.ms.neng.core.policy.PolicyReader.value;
34
35 import java.sql.Timestamp;
36 import java.util.ArrayList;
37 import java.util.HashMap;
38 import java.util.LinkedHashMap;
39 import java.util.List;
40 import java.util.Map;
41 import org.onap.ccsdk.apps.ms.neng.core.exceptions.NengException;
42 import org.onap.ccsdk.apps.ms.neng.core.persistence.NamePersister;
43 import org.onap.ccsdk.apps.ms.neng.core.policy.PolicyFinder;
44 import org.onap.ccsdk.apps.ms.neng.core.policy.PolicyParameters;
45 import org.onap.ccsdk.apps.ms.neng.core.policy.PolicySequence;
46 import org.onap.ccsdk.apps.ms.neng.core.policy.PropertyOperator;
47 import org.onap.ccsdk.apps.ms.neng.core.policy.RecipeParser;
48 import org.onap.ccsdk.apps.ms.neng.core.seq.SequenceGenerator;
49 import org.onap.ccsdk.apps.ms.neng.core.validator.AaiNameValidator;
50 import org.onap.ccsdk.apps.ms.neng.core.validator.DbNameValidator;
51 import org.onap.ccsdk.apps.ms.neng.persistence.entity.GeneratedName;
52
53 /**
54  * Generates names of network elements based on policy data.
55  */
56 public class NameGenerator {
57     private static final String RESOURCE_NAME_ELEMENT_ITEM = "resource-name";
58     private static final String RESOURCE_VALUE_ELEMENT_ITEM = "resource-value";
59     private static final String EXTERNAL_KEY_ELEMENT_ITEM = "external-key";
60     private static final String NAMING_TYPE_ELEMENT_ITEM = "naming-type";
61
62     private final PolicyFinder policyFinder;
63     private final PolicyParameters policyParams;
64     private final SequenceGenerator seqGenerator;
65     private final DbNameValidator dbValidator;
66     private final AaiNameValidator aaiValidator;
67     private final NamePersister namePersister;
68     private final Map<String, String> requestElement;
69     private final List<Map<String, String>> allElements;
70     private final Map<String, Map<String, String>> earlierNames;
71     private final Map<String, Map<String, ?>> policyCache;
72     private final List<String> earlierNamingTypes;
73
74     /**
75      * Constructor.
76      * 
77      * @param policyFinder a way to find policies
78      * @param policyParams parameters related to policy
79      * @param seqGenerator a way to generate sequences
80      * @param dbValidator a way to validate generated names against DB
81      * @param aaiValidator a way to validate generated names against A&AI
82      * @param namePersister a way to persist names
83      * @param requestElement the request element for which the name is generated, containing data such 
84      *        as policy name, naming-type, external-key and resource-name
85      * @param allElements all the elements in the request (including the current request element for 
86      *        which name is generated), as this is needed to re-use names generated from other request elements 
87      *        within the same transaction
88      * @param earlierNames names generated earlier in the same transaction, as a map from naming-type 
89      *        to names (which is a map with keys "resource-name", "resource-value" and "external-key")
90      * @param policyCache cache containing policies retrieved in this transaction, to avoid repeated 
91      *        calls to policy manager within the same transaction
92      * @param earlierNamingTypes naming-types used earlier in the same transaction
93      */
94     public NameGenerator(PolicyFinder policyFinder, PolicyParameters policyParams, SequenceGenerator seqGenerator,
95                     DbNameValidator dbValidator, AaiNameValidator aaiValidator, NamePersister namePersister,
96                     Map<String, String> requestElement, List<Map<String, String>> allElements,
97                     Map<String, Map<String, String>> earlierNames, Map<String, Map<String, ?>> policyCache,
98                     List<String> earlierNamingTypes) {
99         this.policyFinder = policyFinder;
100         this.policyParams = policyParams;
101         this.seqGenerator = seqGenerator;
102         this.dbValidator = dbValidator;
103         this.aaiValidator = aaiValidator;
104         this.namePersister = namePersister;
105         this.requestElement = requestElement;
106         this.allElements = allElements;
107         this.earlierNames = earlierNames;
108         this.policyCache = policyCache;
109         this.earlierNamingTypes = earlierNamingTypes;
110     }
111
112     /**
113      * Generates the name.
114      * 
115      * @return the map (with keys "resource-name", "resource-value" and "external-key") containing the name.
116      */
117     public Map<String, String> generate() throws Exception {
118         String policyName = findElementPolicyName();
119         if (policyName == null) {
120             throw new NengException("Could not find policy name in the request");
121         }
122         String namingType = findElementNamingType();
123         String relaxedNamingType = relaxedNamingType(namingType);
124         Map<String,String> generated = null;
125         if (namingType != null) {
126             if (!earlierNamingTypes.contains(namingType)) {
127                 generated = this.earlierNames.get(namingType);
128                 if (generated == null) {
129                     generated = this.earlierNames.get(relaxedNamingType);
130                 }
131             }
132             if (generated != null) {
133                 return generated;
134             }
135             earlierNamingTypes.add(namingType);
136             return generateNew(policyName, namingType);
137
138         } else {
139             throw new NengException("Could not find naming type in the request for policy " + policyName);
140         }
141     }
142
143     /**
144      * Updates a generated name.
145      * 
146      * @return the map (with keys "resource-name", "resource-value" and "external-key") containing the name.
147      */
148     public Map<String, String> updateGenerateName() throws Exception {
149         String externalKey = findElementExternalKey();
150         String resourceValue = value(this.requestElement, RESOURCE_VALUE_ELEMENT_ITEM);
151         String reqNamingType = findElementNamingType();
152         String reqResourceName = findElementResourceName();
153         String namingType = (reqNamingType == null) ? reqResourceName : reqNamingType;
154         String relaxedNamingType = relaxedNamingType(namingType);
155         
156         //if (!aaiValidator.validate(namingType, resourceValue)) {
157             //throw new NengException("Name already exists in AAI");
158         //}
159         GeneratedName generatedName  = null;
160         if (relaxedNamingType != null) {
161             generatedName = namePersister.findByExternalIdAndElementType(externalKey, relaxedNamingType);
162         } else {
163             throw new NengException("Resource Name or naming type must be provided");
164         }
165         if (generatedName == null) {
166             generatedName = new GeneratedName();
167         } 
168         generatedName.setName(resourceValue);
169         generatedName.setExternalId(externalKey);
170         generatedName.setElementType(namingType);
171         generatedName.setSequenceNumber(null);
172         generatedName.setSequenceNumberEnc(null);
173         generatedName.setPrefix(null);
174         generatedName.setSuffix(null);
175         generatedName.setIsReleased(null);
176         namePersister.persist(generatedName);
177         Map<String, String> respMap = buildResponse(externalKey, reqResourceName, resourceValue);
178         respMap.put(externalKey, "Resource value updated successfully");
179
180         return respMap;
181     }
182
183     String applyNameOperation(Map<String, ?> namingModel, String name) throws Exception {
184         String nameOperation = namingOperation(namingModel);
185         if (nameOperation != null && !"".equals(nameOperation)) {
186             name = new PropertyOperator().apply(name, nameOperation, this.policyParams);
187         }
188         return name;
189     }
190
191     String applyPropertyOperation(String value, Map<String, ?> propertyMap, String recipeItem) throws Exception {
192         return new PropertyOperator().apply(value, propertyMap, this.policyParams, recipeItem);
193     }
194
195     static Map<String, String> buildResponse(String key, String name, String value) {
196         Map<String, String> response = new HashMap<String, String>();
197         response.put(EXTERNAL_KEY_ELEMENT_ITEM, key);
198         response.put(RESOURCE_NAME_ELEMENT_ITEM, name);
199         response.put(RESOURCE_VALUE_ELEMENT_ITEM, value);
200         return response;
201     }
202
203     String buildSequenceSuffix(Map<String, Object> recipeValues, String recipeName, List<String> recipe)
204                     throws Exception {
205         StringBuffer buf = new StringBuffer();
206         boolean postItem = false;
207         for (String key : recipe) {
208             if (postItem) {
209                 buf.append(recipeValues.get(key).toString());
210             } else if (key.equals(recipeName)) {
211                 postItem = true;
212             }
213         }
214         String value = buf.toString();
215         if (value.length() == 0) {
216             value = null;
217         }
218         return value;
219     }
220
221     String buildSequencePrefix(Map<String, Object> recipeValues, String recipeName, List<String> recipe)
222                     throws Exception {
223         StringBuffer buf = new StringBuffer();
224         for (String key : recipe) {
225             if (key.equals(recipeName)) {
226                 break;
227             }
228             buf.append(recipeValues.get(key).toString());
229         }
230         return buf.toString();
231     }
232
233     Map<String, String> generateNew(String policyName, String namingType) throws Exception {
234         Map<String, ?> policy = findPolicy(policyName);
235         if (policy != null) {
236             List<Map<String, ?>> namingModels = namingModels(policy);
237             Map<String, ?> namingModel = namingModel(namingModels, namingType);
238             if (namingModel == null) {
239                 throw new NengException(
240                                 "Could not find the policy data for " + policyName + " and naming-type " + namingType);
241             }
242             return generateNew(policyName, namingType, namingModels, namingModel);
243         } else {
244             throw new NengException("Could not find the policy data for " + policyName);
245         }
246     }
247
248     Map<String, String> generateNew(String policyName, String namingType, 
249                     List<Map<String, ?>> namingModels, Map<String, ?> namingModel) throws Exception {
250         String recipe = namingRecipe(namingModel);
251         if (recipe == null) {
252             throw new NengException("Could not find the recipe for " 
253                                       + policyName + " and naming-type " + namingType);
254         }
255         List<String> recipeItems = RecipeParser.parseRecipe(this.policyParams, recipe);
256         return generateNew(namingModels, policyName, namingType, namingModel, recipeItems);
257     }
258
259     Map<String, String> generateNew(List<Map<String, ?>> namingModels, String policyName, 
260                     String namingType, Map<String, ?> namingModel, List<String> recipe) throws Exception {
261         Map<String, Object> recipeValues = new LinkedHashMap<>();
262         for (String recipeItem : recipe) {
263             Map<String, ?> propMap = namingProperty(namingModel, recipeItem);
264             if ("SEQUENCE".equals(recipeItem)) {
265                 String propValue = value(this.requestElement,recipeItem);
266                 if (propValue != null) {
267                     recipeValues.put(recipeItem, propValue);
268                 } else {
269                     PolicySequence seq = seq(propMap);
270                     recipeValues.put(recipeItem, seq);
271                 }
272             } else {
273                 String val = generateNonSequenceValue(namingModels, policyName, namingType, namingModel, propMap,
274                                 recipeItem);
275                 if (val != null) {
276                     recipeValues.put(recipeItem, val);
277                 }
278             }
279         }
280         validateAllItemsPresent(policyName, namingType, recipe, recipeValues);
281         SeqGenData seqData = generateNameWithSequences(policyName, namingType, recipe, recipeValues, namingModel);
282         String name = seqData.getName();
283         storeGeneratedName(findElementExternalKey(), name, namingType, seqData);
284         Map<String, String> response = buildResponse(findElementExternalKey(), findElementResourceName(), name);
285         String relaxedNamingType = relaxedNamingType(namingType);
286         this.earlierNames.put(relaxedNamingType, response);
287         return response;
288     }
289
290     SeqGenData generateNameWithSequences(String policyName, String namingType, List<String> recipe,
291                     Map<String, Object> recipeValues, Map<String, ?> namingModel) throws Exception {
292         int attemptCount = 0;
293         int maxGenAttempt = this.policyParams.getMaxGenAttempt();
294         if (maxGenAttempt <= 0) {
295             maxGenAttempt = 1;
296         }
297         String name = null;
298         SeqGenData lastSeq = null;
299         boolean valid = false;
300         String additionalErrorMsg = "";
301         while (attemptCount <= maxGenAttempt && !valid) {
302             ++attemptCount;
303             lastSeq = generateSequenceValues(policyName, namingType, recipe, recipeValues, lastSeq, attemptCount);
304             name = generateNameFromSegments(recipeValues, recipe);
305             boolean sequenceLess = false;
306             if (lastSeq == null) {
307                 lastSeq = new SeqGenData();
308                 sequenceLess = true;
309             }
310             name = applyNameOperation(namingModel, name);
311             lastSeq.setName(name);
312             valid = this.dbValidator.validate(namingType, name);
313             if (valid) {
314                 //valid = this.aaiValidator.validate(namingType, name);
315                 //if (!valid) {
316                     //storeGeneratedName("AAI-BACKPOPULATE", name, namingType, lastSeq);
317                     //additionalErrorMsg = "AAI Name validation failed";
318                 //}
319             } else {
320                 additionalErrorMsg = "DB Name validation failed";
321             }
322             if (sequenceLess) {
323                 break; // handle names with no sequence in them
324             }
325         }
326         if (attemptCount > maxGenAttempt) {
327             throw new NengException("Could not generate a name successfully for policy " + policyName
328                             + " and naming-type " + namingType + " even after " + maxGenAttempt + " attempts.");
329         }
330         if (!valid) {
331             throw new NengException("Could not generate a valid name successfully for policy " + policyName
332                             + " and naming-type " + namingType + ". " + additionalErrorMsg);
333         }
334         return lastSeq;
335     }
336
337     String generateNameFromSegments(Map<String, Object> recipeValues, List<String> recipe) throws Exception {
338         StringBuffer buf = new StringBuffer();
339         for (String recName : recipe) {
340             Object val = recipeValues.get(recName);
341             if (val instanceof PolicySequence) {
342                 PolicySequence poly = (PolicySequence) val;
343                 buf.append(poly.getValue());
344             } else {
345                 buf.append(val.toString());
346             }
347         }
348         String value = buf.toString();
349         return value;
350     }
351
352     SeqGenData generateSequenceValues(String policyName, String namingType, List<String> recipe,
353                     Map<String, Object> recipeValues, SeqGenData lastSeq, int attemptCount) throws Exception {
354         SeqGenData precedSeq = generateSequenceValuesOfScope(
355                         policyName, namingType, recipe, recipeValues, "PRECEEDING", lastSeq, attemptCount);
356         SeqGenData entireSeq = generateSequenceValuesOfScope(
357                         policyName, namingType, recipe, recipeValues, "ENTIRETY", lastSeq, attemptCount);
358         if (entireSeq != null) {
359             return entireSeq;
360         }
361         return precedSeq;
362     }
363
364     SeqGenData generateSequenceValuesOfScope(String policyName, String namingType, List<String> recipe,
365                     Map<String, Object> recipeValues, String scope, SeqGenData lastSeq, int attemptCount)
366                     throws Exception {
367         for (String item : recipe) {
368             Object val = recipeValues.get(item);
369             if (val instanceof PolicySequence) {
370                 PolicySequence seq = (PolicySequence) val;
371                 if (scope.equals(seq.getScope())) {
372                     SeqGenData seqVal = generateSequenceValue(seq, policyName, namingType, recipeValues, item, 
373                                     lastSeq, attemptCount, recipe);
374                     String seqStr = SequenceFormatter.formatSequence(seqVal.getSeq(), seq);
375                     seqVal.setSeqEncoded(seqStr);
376                     seq.setKey(item);
377                     seq.setValue(seqStr);
378                     return seqVal;
379                 }
380             }
381         }
382         return null;
383     }
384
385     SeqGenData generateSequenceValue(PolicySequence seq, String policyName, String namingType,
386                     Map<String, Object> recipeValues, String recipeName, SeqGenData lastSeq, int attemptCount,
387                     List<String> recipe) throws Exception {
388         String prefix = buildSequencePrefix(recipeValues, recipeName, recipe);
389         String suffix = buildSequenceSuffix(recipeValues, recipeName, recipe);
390         SeqGenData seqData = new SeqGenData();
391         Long lastSeqValue = null;
392         if (lastSeq != null) {
393             lastSeqValue = lastSeq.getSeq();
394         }
395         long seqValue = this.seqGenerator.generate(prefix, suffix, seq, lastSeqValue, attemptCount);
396         seqData.setSeq(seqValue);
397         seqData.setPrefix(prefix);
398         seqData.setSuffix(suffix);
399         return seqData;
400     }
401
402     String generateNonSequenceValue(List<Map<String, ?>> namingModels, String policyName, String namingType,
403                     Map<String, ?> namingModel, Map<String, ?> propMap, String recipeItem) throws Exception {
404         String val = propertyValue(propMap);
405         if (val == null) {
406             val = value(this.requestElement, recipeItem);
407         }
408         if (val == null) {
409             val = value(this.requestElement, recipeItem, true);
410         }
411         if (val == null) {
412             val = generateValueRecursively(namingModels, policyName, recipeItem);
413         }
414         if (val != null) {
415             val = applyPropertyOperation(val, propMap, null);
416         }
417         if (val == null) { 
418             val = applyPropertyOperation(val, propMap, recipeItem);
419         }
420         if (val == null) {
421             val = value(namingModel, recipeItem);
422         }
423         return val;
424     }
425
426     String generateValueRecursively(List<Map<String, ?>> namingModels, String policyName, String recipeItem)
427                     throws Exception {
428         String val = null;
429         String relaxedVal = relaxedNamingType(recipeItem);
430         Map<String, String> generated = this.earlierNames.get(relaxedVal);
431         if (generated != null) {
432             return generated.get("resource-value");
433         }
434         Map<String, ?> relaxedModel = namingModelRelaxed(namingModels, recipeItem);
435         if (relaxedModel != null) {
436             String relaxedNamingType = namingType(relaxedModel);
437             Map<String, String> relaxedElement = findElement(relaxedNamingType);
438             if (relaxedElement != null) {
439                 relaxedElement = new HashMap<>(relaxedElement);
440                 relaxedElement.put(NAMING_TYPE_ELEMENT_ITEM, relaxedNamingType);
441             } else {
442                 relaxedElement = new HashMap<>(this.requestElement);
443                 relaxedElement.put(NAMING_TYPE_ELEMENT_ITEM, relaxedNamingType);
444                 relaxedElement.remove(EXTERNAL_KEY_ELEMENT_ITEM);
445                 relaxedElement.remove(RESOURCE_NAME_ELEMENT_ITEM);
446             }
447             if (relaxedElement != null) {
448                 NameGenerator recursive = new NameGenerator(policyFinder, policyParams, seqGenerator, dbValidator,
449                                 aaiValidator, namePersister, relaxedElement, allElements, earlierNames, policyCache,
450                                 earlierNamingTypes);
451                 Map<String, String> gen =
452                                 recursive.generateNew(policyName, relaxedNamingType, namingModels, relaxedModel);
453                 if (gen != null) {
454                     val = value(gen, RESOURCE_VALUE_ELEMENT_ITEM);
455                 }
456             }
457         }
458         return val;
459     }
460
461     Map<String, String> findElement(String namingType) throws Exception {
462         Map<String, String> theElement = null;
463         for (Map<String, String> anElement : this.allElements) {
464             String oneNamingType = namingType(anElement);
465             if (namingType.equals(oneNamingType)) {
466                 theElement = anElement;
467                 break;
468             }
469         }
470         return theElement;
471     }
472
473     String findElementPolicyName() throws Exception {
474         return value(this.requestElement, "policy-instance-name");
475     }
476
477     String findElementNamingType() throws Exception {
478         return value(this.requestElement, NAMING_TYPE_ELEMENT_ITEM);
479     }
480
481     String findElementResourceName() throws Exception {
482         return value(this.requestElement, RESOURCE_NAME_ELEMENT_ITEM);
483     }
484
485     String findElementExternalKey() throws Exception {
486         return value(this.requestElement, EXTERNAL_KEY_ELEMENT_ITEM);
487     }
488
489     Map<String, ?> findPolicy(String name) throws Exception {
490         Map<String, ?> policy = null;
491         if (name != null) {
492             policy = this.policyCache.get(name);
493             if (policy == null) {
494                 policy = this.policyFinder.findPolicy(name);
495                 if (policy != null) {
496                     this.policyCache.put(name, policy);
497                 }
498             }
499         }
500         return policy;
501     }
502
503     void storeGeneratedName(String key, String name, String namingType, 
504                     SeqGenData seqData) throws Exception {
505         String prefix = null;
506         String suffix = null;
507         Long seqNum = null;
508         String seqEncoded = null;
509         if (seqData != null) {
510             prefix = seqData.getPrefix();
511             suffix = seqData.getSuffix();
512             seqNum = seqData.getSeq();
513             seqEncoded = seqData.getSeqEncoded();
514         }
515         GeneratedName record = new GeneratedName();
516         GeneratedName releasedName = namePersister.findByElementTypeAndNameAndReleased(namingType, name, "Y");
517         if (releasedName != null) {
518             record = releasedName;
519             record.setLastUpdatedTime(new Timestamp(System.currentTimeMillis()));
520             record.setIsReleased(null);
521         }
522         record.setName(name);
523         record.setExternalId(key);
524         record.setElementType(namingType);
525         record.setPrefix(prefix);
526         record.setSuffix(suffix);
527         if (seqNum != null) {
528             record.setSequenceNumber(seqNum);
529         }
530         record.setSequenceNumberEnc(seqEncoded);
531         this.namePersister.persist(record);
532     }
533
534     void validateAllItemsPresent(String policyName, String namingType, List<String> recipe,
535                     Map<String, Object> recipeValues) throws Exception {
536         List<String> missing = new ArrayList<>();
537         for (String item : recipe) {
538             Object val = recipeValues.get(item);
539             if (val == null) {
540                 missing.add(item);
541             }
542         }
543         if (missing.size() > 0) {
544             StringBuffer msg = new StringBuffer();
545             for (int i = 0; i < missing.size(); ++i) {
546                 String item = missing.get(i);
547                 if (i > 0) {
548                     String separator = ", ";
549                     if (i == missing.size() - 1) {
550                         separator = " and ";
551                     }
552                     msg.append(separator);
553                 }
554                 msg.append(item);
555             }
556             String itemString = "items";
557             if (missing.size() == 1) {
558                 itemString = "item";
559             }
560             throw new NengException("Could not find data for recipe " + itemString + " " + msg.toString()
561                             + " in policy " + policyName + " and naming-type " + namingType);
562         }
563     }
564 }