Update license and poms
[aai/sparky-be.git] / sparkybe-onap-service / src / main / java / org / onap / aai / sparky / viewandinspect / entity / ActiveInventoryNode.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * Copyright © 2017-2018 Amdocs
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *       http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  */
21 package org.onap.aai.sparky.viewandinspect.entity;
22
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.HashMap;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Map.Entry;
31 import java.util.Set;
32 import java.util.concurrent.ConcurrentLinkedDeque;
33 import java.util.concurrent.atomic.AtomicBoolean;
34 import java.util.regex.Matcher;
35 import java.util.regex.Pattern;
36
37 import org.onap.aai.cl.api.Logger;
38 import org.onap.aai.cl.eelf.LoggerFactory;
39 import org.onap.aai.restclient.client.OperationResult;
40 import org.onap.aai.sparky.config.oxm.OxmEntityLookup;
41 import org.onap.aai.sparky.config.oxm.OxmModelLoader;
42 import org.onap.aai.sparky.logging.AaiUiMsgs;
43 import org.onap.aai.sparky.viewandinspect.config.VisualizationConfigs;
44 import org.onap.aai.sparky.viewandinspect.enumeration.NodeProcessingAction;
45 import org.onap.aai.sparky.viewandinspect.enumeration.NodeProcessingState;
46
47 import com.fasterxml.jackson.databind.JsonNode;
48 import com.fasterxml.jackson.databind.ObjectMapper;
49
50 /**
51  * The Class ActiveInventoryNode.
52  */
53 public class ActiveInventoryNode {
54
55   private static final Logger LOG = LoggerFactory.getInstance().getLogger(
56       ActiveInventoryNode.class);
57   private static final String URIRegexPattern = "aai/v[\\d]/";
58
59   public static final int DEFAULT_INIT_NODE_DEPTH = 1000;
60
61   private String nodeId;
62   private String selfLink;
63
64   private boolean isRootNode;
65   private ConcurrentLinkedDeque<String> inboundNeighbors;
66   private ConcurrentLinkedDeque<String> outboundNeighbors;
67   
68   private ConcurrentLinkedDeque<String> inboundNeighborSelfLinks;
69   private ConcurrentLinkedDeque<String> outboundNeighborSelfLinks;
70   
71   private List<JsonNode> complexGroups;
72   private List<RelationshipList> relationshipLists;
73   private int nodeDepth;
74   private OperationResult opResult;
75
76   private boolean processingErrorOccurred;
77   private List<String> errorCauses;
78   private boolean selflinkRetrievalFailure;
79   private NodeProcessingState state;
80
81   private boolean processedNeighbors;
82
83   private boolean selfLinkPendingResolve;
84   
85   /*
86    * I think we shouldn't be using this crutch flags.  If these things are meant
87    * to represent the current state of the node, then they should be legitimate 
88    * state transitions.
89    */
90   
91   private boolean selfLinkDeterminationPending;
92
93   private AtomicBoolean selfLinkProcessed;
94   private AtomicBoolean nodeIntegrityProcessed;
95
96   private OxmModelLoader oxmModelLoader;
97   private VisualizationConfigs visualizationConfigs;
98
99   private String entityType;
100   private String primaryKeyName;
101   private String primaryKeyValue;
102
103   private boolean nodeValidated;
104   private boolean nodeIssue;
105   private boolean ignoredByFilter;
106
107   private boolean resolvedSelfLink;
108
109   private Map<String, String> properties;
110   private ArrayList<String> queryParams;
111
112   private ObjectMapper mapper;
113   
114   private OxmEntityLookup oxmEntityLookup;
115  
116   /**
117    * Instantiates a new active inventory node.
118    *
119    * @param key the key
120    */
121   public ActiveInventoryNode(VisualizationConfigs visualizationConfigs, OxmEntityLookup oxmEntityLookup) {
122     this.oxmEntityLookup = oxmEntityLookup;
123     this.nodeId = null;
124     this.entityType = null;
125     this.selfLink = null;
126     this.properties = new HashMap<String, String>();
127     this.processingErrorOccurred = false;
128     this.errorCauses = new ArrayList<String>();
129     this.selflinkRetrievalFailure = false;
130     this.nodeIssue = false;
131     this.nodeValidated = false;
132     this.state = NodeProcessingState.INIT;
133     this.selfLinkPendingResolve = false;
134     this.selfLinkDeterminationPending = false;
135
136     selfLinkProcessed = new AtomicBoolean(Boolean.FALSE);
137     nodeIntegrityProcessed = new AtomicBoolean(Boolean.FALSE);
138     oxmModelLoader = null;
139     this.visualizationConfigs = visualizationConfigs ;
140
141     isRootNode = false;
142     inboundNeighbors = new ConcurrentLinkedDeque<String>();
143     outboundNeighbors = new ConcurrentLinkedDeque<String>();
144     
145     inboundNeighborSelfLinks = new ConcurrentLinkedDeque<String>();
146     outboundNeighborSelfLinks = new ConcurrentLinkedDeque<String>();
147     
148     complexGroups = new ArrayList<JsonNode>();
149     relationshipLists = new ArrayList<RelationshipList>();
150     nodeDepth = DEFAULT_INIT_NODE_DEPTH;
151     queryParams = new ArrayList<String>();
152
153     mapper = new ObjectMapper();
154
155     processedNeighbors = false;
156     resolvedSelfLink = false;
157
158
159   }
160   
161   public void clearQueryParams() {
162     queryParams.clear();
163   }
164   
165   public void addQueryParam(String queryParam) {
166     if ( queryParam!= null) {
167       if( !queryParams.contains(queryParam)) {
168         queryParams.add(queryParam);
169       }
170     }
171   }
172   
173         public void addInboundSelfLink(String link) {
174
175                 if (link == null) {
176                         return;
177                 }
178
179                 if (!inboundNeighborSelfLinks.contains(link)) {
180                         inboundNeighborSelfLinks.add(link);
181                 }
182
183         }
184
185         public void addOutboundSelfLink(String link) {
186
187                 if (link == null) {
188                         return;
189                 }
190
191                 if (!outboundNeighborSelfLinks.contains(link)) {
192                         outboundNeighborSelfLinks.add(link);
193                 }
194
195         }
196
197         public Collection<String> getInboundNeighborSelfLinks() {
198                 return inboundNeighborSelfLinks;
199         }
200
201         public Collection<String> getOutboundNeighborSelfLinks() {
202                 return outboundNeighborSelfLinks;
203         }
204   
205   public void addQueryParams(Collection<String> params) {
206
207     if (params != null & params.size() > 0) {
208
209       for (String param : params) {
210         addQueryParam(param);
211       }
212     }
213   }
214
215   
216   public List<String> getQueryParams() {
217     return queryParams;
218   }
219
220   public void setSelfLinkDeterminationPending(boolean selfLinkDeterminationPending) {
221     this.selfLinkDeterminationPending = selfLinkDeterminationPending;
222   }
223
224   public boolean isSelfLinkDeterminationPending() {
225     return selfLinkDeterminationPending;
226   }
227
228   public NodeProcessingState getState() {
229     return state;
230   }
231
232   public List<JsonNode> getComplexGroups() {
233     return complexGroups;
234   }
235
236   public List<RelationshipList> getRelationshipLists() {
237     return relationshipLists;
238   }
239
240   public OperationResult getOpResult() {
241     return opResult;
242   }
243
244   public void setOpResult(OperationResult opResult) {
245     this.opResult = opResult;
246   }
247
248   public String getPrimaryKeyName() {
249     return primaryKeyName;
250   }
251
252   /**
253    * Gets the visualization config.
254    *
255    * @return the visualization config
256    */
257   public VisualizationConfigs getvisualizationConfigs() {
258     return visualizationConfigs;
259   }
260
261   public int getNodeDepth() {
262     return nodeDepth;
263   }
264
265   public void setNodeDepth(int nodeDepth) {
266     this.nodeDepth = nodeDepth;
267   }
268
269   /**
270    * Sets the visualization config.
271    *
272    * @param visualizationConfig the new visualization config
273    */
274   public void setvisualizationConfig(VisualizationConfigs visualizationConfigs) {
275     this.visualizationConfigs = visualizationConfigs;
276   }
277
278   public OxmModelLoader getOxmModelLoader() {
279     return oxmModelLoader;
280   }
281
282   public void setPrimaryKeyName(String primaryKeyName) {
283     this.primaryKeyName = primaryKeyName;
284   }
285
286   public String getPrimaryKeyValue() {
287     return primaryKeyValue;
288   }
289
290   public void setPrimaryKeyValue(String primaryKeyValue) {
291     this.primaryKeyValue = primaryKeyValue;
292   }
293
294   public boolean isNodeValidated() {
295     return nodeValidated;
296   }
297
298   public void setNodeValidated(boolean nodeValidated) {
299     this.nodeValidated = nodeValidated;
300   }
301
302   public boolean isNodeIssue() {
303     return nodeIssue;
304   }
305
306   public boolean isIgnoredByFilter() {
307     return ignoredByFilter;
308   }
309
310   public void setIgnoredByFilter(boolean ignoredByFilter) {
311     this.ignoredByFilter = ignoredByFilter;
312   }
313
314   public void setNodeIssue(boolean nodeIssue) {
315     this.nodeIssue = nodeIssue;
316   }
317
318   /**
319    * Checks for processed neighbors.
320    *
321    * @return true, if successful
322    */
323   public boolean hasProcessedNeighbors() {
324     return processedNeighbors;
325   }
326
327   public void setProcessedNeighbors(boolean processedNeighbors) {
328     this.processedNeighbors = processedNeighbors;
329   }
330
331   /**
332    * Checks for resolved self link.
333    *
334    * @return true, if successful
335    */
336   public boolean hasResolvedSelfLink() {
337     return resolvedSelfLink;
338   }
339
340   public void setResolvedSelfLink(boolean resolvedSelfLink) {
341     this.resolvedSelfLink = resolvedSelfLink;
342   }
343
344   /**
345    * Checks for neighbors.
346    *
347    * @return true, if successful
348    */
349   public boolean hasNeighbors() {
350     return (inboundNeighbors.size() > 0 || outboundNeighbors.size() > 0);
351   }
352
353   /**
354    * Adds the inbound neighbor.
355    *
356    * @param nodeId the node id
357    */
358   public void addInboundNeighbor(String nodeId) {
359
360     if (nodeId == null) {
361       return;
362     }
363
364     if (!inboundNeighbors.contains(nodeId)) {
365       inboundNeighbors.add(nodeId);
366     }
367
368   }
369
370   /**
371    * Adds the outbound neighbor.
372    *
373    * @param nodeId the node id
374    */
375   public void addOutboundNeighbor(String nodeId) {
376
377     if (nodeId == null) {
378       return;
379     }
380
381     if (!outboundNeighbors.contains(nodeId)) {
382       outboundNeighbors.add(nodeId);
383     }
384
385   }
386
387   public boolean isAtMaxDepth() {
388     return (nodeDepth >= this.visualizationConfigs.getMaxSelfLinkTraversalDepth());
389   }
390
391   public ConcurrentLinkedDeque<String> getInboundNeighbors() {
392     return inboundNeighbors;
393   }
394
395   public void setInboundNeighbors(ConcurrentLinkedDeque<String> inboundNeighbors) {
396     this.inboundNeighbors = inboundNeighbors;
397   }
398
399   public Collection<String> getOutboundNeighbors() {
400     List<String> result = new ArrayList<String>();
401
402     Iterator<String> neighborIterator = outboundNeighbors.iterator();
403
404     while (neighborIterator.hasNext()) {
405       result.add(neighborIterator.next());
406     }
407
408     return result;
409   }
410
411   /**
412    * Change depth.
413    *
414    * @param newDepth the new depth
415    * @return true, if successful
416    */
417   public boolean changeDepth(int newDepth) {
418
419     boolean nodeDepthWasChanged = false;
420
421     if (newDepth < nodeDepth) {
422       LOG.info(AaiUiMsgs.ACTIVE_INV_NODE_CHANGE_DEPTH, nodeId,
423           String.valueOf(this.nodeDepth), String.valueOf(newDepth));
424       this.nodeDepth = newDepth;
425       nodeDepthWasChanged = true;
426     }
427
428     return nodeDepthWasChanged;
429
430   }
431
432   public void setOutboundNeighbors(ConcurrentLinkedDeque<String> outboundNeighbors) {
433     this.outboundNeighbors = outboundNeighbors;
434   }
435
436   public boolean isRootNode() {
437     return isRootNode;
438   }
439
440   public void setRootNode(boolean isRootNode) {
441     this.isRootNode = isRootNode;
442   }
443
444   /**
445    * Change state.
446    *
447    * @param newState the new state
448    * @param action the action
449    */
450   public void changeState(NodeProcessingState newState, NodeProcessingAction action) {
451     /*
452      * NodeId may be null depending on the current node life-cycle state 
453      */
454     
455     if (getNodeId() != null) {
456       LOG.info(AaiUiMsgs.ACTIVE_INV_NODE_CHANGE_STATE, state.toString(), newState.toString(), action.toString());
457     } else {
458       LOG.info(AaiUiMsgs.ACTIVE_INV_NODE_CHANGE_STATE_NO_NODE_ID, state.toString(), newState.toString(), action.toString());
459     }
460     this.state = newState;
461   }
462
463   public boolean isSelfLinkPendingResolve() {
464     return selfLinkPendingResolve;
465   }
466
467   public void setSelfLinkPendingResolve(boolean selfLinkPendingResolve) {
468     this.selfLinkPendingResolve = selfLinkPendingResolve;
469   }
470
471   public boolean isSelflinkRetrievalFailure() {
472     return selflinkRetrievalFailure;
473   }
474
475   public void setSelflinkRetrievalFailure(boolean selflinkRetrievalFailure) {
476     this.selflinkRetrievalFailure = selflinkRetrievalFailure;
477   }
478
479   public void setOxmModelLoader(OxmModelLoader loader) {
480     this.oxmModelLoader = loader;
481   }
482
483   public boolean getSelfLinkProcessed() {
484     return selfLinkProcessed.get();
485   }
486
487   public void setSelfLinkProcessed(boolean selfLinkProcessed) {
488     this.selfLinkProcessed.set(selfLinkProcessed);
489   }
490
491   public boolean getNodeIntegrityProcessed() {
492     return nodeIntegrityProcessed.get();
493   }
494
495   public void setNodeIntegrityProcessed(boolean nodeIntegrityProcessed) {
496     this.nodeIntegrityProcessed.set(nodeIntegrityProcessed);
497   }
498
499   public boolean isDirectSelfLink() {
500     return isDirectSelfLink(this.selfLink);
501   }
502
503   /**
504    * Checks if is direct self link.
505    *
506    * @param link the link
507    * @return true, if is direct self link
508    */
509   public static boolean isDirectSelfLink(String link) {
510     
511     if (link == null) {
512       return false;
513     }
514
515     return link.contains("/resources/id/");
516
517   }
518
519   public Map<String, String> getProperties() {
520     return properties;
521   }
522
523   /**
524    * Adds the error cause.
525    *
526    * @param error the error
527    */
528   public void addErrorCause(String error) {
529     if (!errorCauses.contains(error)) {
530       errorCauses.add(error);
531     }
532   }
533
534   /**
535    * Adds the property.
536    *
537    * @param key the key
538    * @param value the value
539    */
540   public void addProperty(String key, String value) {
541     properties.put(key, value);
542   }
543
544   public boolean isProcessingErrorOccurred() {
545     return processingErrorOccurred;
546   }
547
548   public void setProcessingErrorOccurred(boolean processingErrorOccurred) {
549     this.processingErrorOccurred = processingErrorOccurred;
550   }
551
552   public String getNodeId() {
553     return nodeId;
554   }
555
556   public void setNodeId(String nodeId) {
557     this.nodeId = nodeId;
558   }
559
560   public String getEntityType() {
561     return entityType;
562   }
563
564   public void setEntityType(String entityType) {
565     this.entityType = entityType;
566   }
567
568   public String getSelfLink() {
569     return selfLink;
570   }
571
572   /**
573    * Calculate edit attribute uri.
574    *
575    * @param link the link
576    * @return the string
577    */
578   public String calculateEditAttributeUri(String link) {
579     String uri = null;
580     Pattern pattern = Pattern.compile(URIRegexPattern);
581     Matcher matcher = pattern.matcher(link);
582     if (matcher.find()) {
583       uri = link.substring(matcher.end());
584     }
585     return uri;
586   }
587
588   /**
589    * Analyze self link relationship list.
590    *
591    * @param jsonResult the json result
592    * @return the relationship list
593    */
594   private RelationshipList analyzeSelfLinkRelationshipList(String jsonResult) {
595
596
597     RelationshipList relationshipList = null;
598
599     try {
600       relationshipList = mapper.readValue(jsonResult, RelationshipList.class);
601     } catch (Exception exc) {
602       LOG.error(AaiUiMsgs.SELF_LINK_RELATIONSHIP_LIST_ERROR, exc.toString());
603     }
604
605     return relationshipList;
606   }
607
608   /**
609    * Adds the relationship list.
610    *
611    * @param relationshipList the relationship list
612    */
613   public void addRelationshipList(RelationshipList relationshipList) {
614
615     if (!relationshipLists.contains(relationshipList)) {
616       relationshipLists.add(relationshipList);
617     }
618
619   }
620
621   /**
622    * Process pathed self link response.
623    *
624    * @param selfLinkJsonResponse the self link json response
625    * @param startNodeType the start node type
626    * @param startNodeResourceKey the start node resource key
627    */
628   public void processPathedSelfLinkResponse(String selfLinkJsonResponse, String startNodeType,
629       String startNodeResourceKey) {
630
631     if (selfLinkJsonResponse == null || selfLinkJsonResponse.length() == 0) {
632       LOG.error(AaiUiMsgs.SELF_LINK_NULL_EMPTY_RESPONSE);
633       return;
634     }
635
636     try {
637       JsonNode jsonNode = mapper.readValue(selfLinkJsonResponse, JsonNode.class);
638
639       Iterator<Entry<String, JsonNode>> fieldNames = jsonNode.fields();
640       Entry<String, JsonNode> field = null;
641
642       while (fieldNames.hasNext()) {
643
644         field = fieldNames.next();
645
646         /*
647          * Is there a way to tell if the field is an aggregate or an atomic value? This is where our
648          * flattening code needs to live
649          */
650
651         String fieldName = field.getKey();
652
653         if ("relationship-list".equals(fieldName)) {
654
655           /*
656            * Parse the relationship list like we were doing before, so we can determine whether or
657            * not to keep it or traverse it after we have performed the evaluative node depth logic.
658            */
659           RelationshipList relationshipList =
660               analyzeSelfLinkRelationshipList(field.getValue().toString());
661
662           if (relationshipList != null) {
663             this.relationshipLists.add(relationshipList);
664           } else {
665             LOG.info(AaiUiMsgs.NO_RELATIONSHIP_DISCOVERED, nodeId);
666           }
667         } else {
668           JsonNode nodeValue = field.getValue();
669
670           if (nodeValue != null && nodeValue.isValueNode()) {
671
672             /*
673              * before we blindly add the fieldName and value to our property set, let's do one more
674              * check to see if the field name is an entity type. If it is, then our complex
675              * attribute processing code will pick it up and process it instead, but this is
676              * probably more likely just for array node types, but we'll see.
677              */
678
679             if (oxmEntityLookup.getEntityDescriptors().get(fieldName) == null) {
680               /*
681                * this is no an entity type as far as we can tell, so we can add it to our property
682                * set.
683                */
684
685               addProperty(fieldName, nodeValue.asText());
686
687             }
688
689           } else {
690
691             if (nodeValue.isArray()) {
692
693               /*
694                * make sure array entity-type collection is not an entityType before adding it to the
695                * property set. The expetation is that it will be added the visualization through a
696                * complex group or relationship.
697                */
698
699               if (oxmEntityLookup.getEntityDescriptors().get(field.getKey()) == null) {
700                 /*
701                  * this is no an entity type as far as we can tell, so we can add it to our property
702                  * set.
703                  */
704
705                 addProperty(field.getKey(), nodeValue.toString());
706
707               }
708
709             } else {
710
711               complexGroups.add(nodeValue);
712
713             }
714
715           }
716
717         }
718
719       }
720
721     } catch (IOException exc) {
722       LOG.error(AaiUiMsgs.JSON_CONVERSION_ERROR, "POJO", exc.getLocalizedMessage());
723       this.setProcessingErrorOccurred(true);
724       this.addErrorCause(
725           "An error occurred while converting JSON into POJO = " + exc.getLocalizedMessage());
726     }
727
728   }
729
730   public void setSelfLink(String selfLink) {
731     this.selfLink = selfLink;
732   }
733
734   /**
735    * Adds the complex group.
736    *
737    * @param complexGroup the complex group
738    */
739   public void addComplexGroup(JsonNode complexGroup) {
740
741     if (!complexGroups.contains(complexGroup)) {
742       complexGroups.add(complexGroup);
743     }
744
745   }
746
747   /**
748    * Gets the padding.
749    *
750    * @param level the level
751    * @param paddingString the padding string
752    * @return the padding
753    */
754   private static String getPadding(int level, String paddingString) {
755     StringBuilder sb = new StringBuilder(32);
756     for (int x = 0; x < level; x++) {
757       sb.append(paddingString);
758     }
759     return sb.toString();
760   }
761
762   /**
763    * Dump node tree.
764    *
765    * @param showProperties the show properties
766    * @return the string
767    */
768   public String dumpNodeTree(boolean showProperties) {
769     return dumpNodeTree(0, showProperties);
770   }
771   
772   /**
773    * Dump node tree.
774    *
775    * @param level the level
776    * @param showProperties the show properties
777    * @return the string
778    */
779   private String dumpNodeTree(int level, boolean showProperties) {
780     StringBuilder sb = new StringBuilder(128);
781     String padding = getPadding(level, "   ");
782
783     sb.append(padding + " -> " + getNodeId() + "]").append("\n");
784     sb.append(padding + " -> primaryKeyName = " + primaryKeyName + "]").append("\n");
785     sb.append(padding + " -> primaryKeyValue = " + primaryKeyValue + "]").append("\n");
786     sb.append(padding + " -> entityType = " + entityType + "]").append("\n");
787
788     if (showProperties) {
789       Set<Entry<String, String>> entries = properties.entrySet();
790       for (Entry<String, String> entry : entries) {
791         sb.append(
792             padding + " ----> " + String.format("[ %s => %s ]", entry.getKey(), entry.getValue()))
793             .append("\n");
794       }
795     }
796
797     sb.append(padding + " ----> " + String.format("[ selfLink => %s ]", getSelfLink()))
798         .append("\n");
799
800     sb.append("\n").append(padding + " ----> Inbound Neighbors:").append("\n");
801
802     for (String inboundNeighbor : inboundNeighbors) {
803       sb.append("\n").append(inboundNeighbor.toString());
804     }
805
806     sb.append(padding + " ----> Outbound Neighbors:").append("\n");
807     sb.append("\n").append(padding + " ----> Outbound Neighbors:").append("\n");
808
809     for (String outboundNeighbor : outboundNeighbors) {
810       sb.append("\n").append(outboundNeighbor.toString());
811     }
812
813     return sb.toString();
814
815   }
816
817   public String getProcessingErrorCauses() {
818
819     StringBuilder sb = new StringBuilder(128);
820
821     for (String c : this.errorCauses) {
822       sb.append(c).append("\n");
823     }
824
825     return sb.toString();
826   }
827 }