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