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