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