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