Changing the license and trademark
[aai/sparky-be.git] / src / main / java / org / openecomp / 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.openecomp.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.openecomp.cl.api.Logger;
40 import org.openecomp.cl.eelf.LoggerFactory;
41 import org.openecomp.sparky.config.oxm.OxmModelLoader;
42 import org.openecomp.sparky.dal.rest.OperationResult;
43 import org.openecomp.sparky.logging.AaiUiMsgs;
44 import org.openecomp.sparky.viewandinspect.config.VisualizationConfig;
45 import org.openecomp.sparky.viewandinspect.enumeration.NodeProcessingAction;
46 import org.openecomp.sparky.viewandinspect.enumeration.NodeProcessingState;
47
48 import com.fasterxml.jackson.databind.JsonNode;
49 import com.fasterxml.jackson.databind.ObjectMapper;
50
51 /**
52  * The Class ActiveInventoryNode.
53  */
54 public class ActiveInventoryNode {
55
56   private static final Logger LOG = LoggerFactory.getInstance().getLogger(
57       ActiveInventoryNode.class);
58   private static final String URIRegexPattern = "aai/v[\\d]/";
59
60   public static final int DEFAULT_INIT_NODE_DEPTH = 1000;
61
62   private String nodeId;
63   private String selfLink;
64
65   private boolean isRootNode;
66   private ConcurrentLinkedDeque<String> inboundNeighbors;
67   private ConcurrentLinkedDeque<String> outboundNeighbors;
68   private List<JsonNode> complexGroups;
69   private List<RelationshipList> relationshipLists;
70   private int nodeDepth;
71   private OperationResult opResult;
72
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
93   private OxmModelLoader oxmModelLoader;
94   private VisualizationConfig visualizationConfig;
95
96   private String entityType;
97   private String primaryKeyName;
98   private String primaryKeyValue;
99
100   private boolean nodeIssue;
101   private boolean ignoredByFilter;
102
103   private boolean resolvedSelfLink;
104
105   private Map<String, String> properties;
106   private ArrayList<String> queryParams;
107
108   private ObjectMapper mapper;
109
110   /**
111    * Instantiates a new active inventory node.
112    */
113   public ActiveInventoryNode() {
114     this(null);
115   }
116
117   /**
118    * Instantiates a new active inventory node.
119    *
120    * @param key the key
121    */
122   public ActiveInventoryNode(String key) {
123     this.nodeId = null;
124     this.entityType = null;
125     this.selfLink = null;
126     this.properties = new HashMap<String, String>();
127     this.processingErrorOccurred = false;
128     this.errorCauses = new ArrayList<String>();
129     this.selflinkRetrievalFailure = false;
130     this.nodeIssue = false;
131     this.state = NodeProcessingState.INIT;
132     this.selfLinkPendingResolve = false;
133     this.selfLinkDeterminationPending = false;
134
135     selfLinkProcessed = new AtomicBoolean(Boolean.FALSE);
136     oxmModelLoader = null;
137     visualizationConfig = null;
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 VisualizationConfig getvisualizationConfig() {
220     return visualizationConfig;
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(VisualizationConfig visualizationConfig) {
237     this.visualizationConfig = visualizationConfig;
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 isNodeIssue() {
257     return nodeIssue;
258   }
259
260   public boolean isIgnoredByFilter() {
261     return ignoredByFilter;
262   }
263
264   public void setIgnoredByFilter(boolean ignoredByFilter) {
265     this.ignoredByFilter = ignoredByFilter;
266   }
267
268   public void setNodeIssue(boolean nodeIssue) {
269     this.nodeIssue = nodeIssue;
270   }
271
272   /**
273    * Checks for processed neighbors.
274    *
275    * @return true, if successful
276    */
277   public boolean hasProcessedNeighbors() {
278     return processedNeighbors;
279   }
280
281   public void setProcessedNeighbors(boolean processedNeighbors) {
282     this.processedNeighbors = processedNeighbors;
283   }
284
285   /**
286    * Checks for resolved self link.
287    *
288    * @return true, if successful
289    */
290   public boolean hasResolvedSelfLink() {
291     return resolvedSelfLink;
292   }
293
294   public void setResolvedSelfLink(boolean resolvedSelfLink) {
295     this.resolvedSelfLink = resolvedSelfLink;
296   }
297
298   /**
299    * Checks for neighbors.
300    *
301    * @return true, if successful
302    */
303   public boolean hasNeighbors() {
304     return (inboundNeighbors.size() > 0 || outboundNeighbors.size() > 0);
305   }
306
307   /**
308    * Adds the inbound neighbor.
309    *
310    * @param nodeId the node id
311    */
312   public void addInboundNeighbor(String nodeId) {
313
314     if (nodeId == null) {
315       return;
316     }
317
318     if (!inboundNeighbors.contains(nodeId)) {
319       inboundNeighbors.add(nodeId);
320     }
321
322   }
323
324   /**
325    * Adds the outbound neighbor.
326    *
327    * @param nodeId the node id
328    */
329   public void addOutboundNeighbor(String nodeId) {
330
331     if (nodeId == null) {
332       return;
333     }
334
335     if (!outboundNeighbors.contains(nodeId)) {
336       outboundNeighbors.add(nodeId);
337     }
338
339   }
340
341   public boolean isAtMaxDepth() {
342     return (nodeDepth >= VisualizationConfig.getConfig().getMaxSelfLinkTraversalDepth());
343   }
344
345   public ConcurrentLinkedDeque<String> getInboundNeighbors() {
346     return inboundNeighbors;
347   }
348
349   public void setInboundNeighbors(ConcurrentLinkedDeque<String> inboundNeighbors) {
350     this.inboundNeighbors = inboundNeighbors;
351   }
352
353   public Collection<String> getOutboundNeighbors() {
354     List<String> result = new ArrayList<String>();
355
356     Iterator<String> neighborIterator = outboundNeighbors.iterator();
357
358     while (neighborIterator.hasNext()) {
359       result.add(neighborIterator.next());
360     }
361
362     return result;
363   }
364
365   /**
366    * Change depth.
367    *
368    * @param newDepth the new depth
369    * @return true, if successful
370    */
371   public boolean changeDepth(int newDepth) {
372
373     boolean nodeDepthWasChanged = false;
374
375     if (newDepth < nodeDepth) {
376       LOG.info(AaiUiMsgs.ACTIVE_INV_NODE_CHANGE_DEPTH, nodeId,
377           String.valueOf(this.nodeDepth), String.valueOf(newDepth));
378       this.nodeDepth = newDepth;
379       nodeDepthWasChanged = true;
380     }
381
382     return nodeDepthWasChanged;
383
384   }
385
386   public void setOutboundNeighbors(ConcurrentLinkedDeque<String> outboundNeighbors) {
387     this.outboundNeighbors = outboundNeighbors;
388   }
389
390   public boolean isRootNode() {
391     return isRootNode;
392   }
393
394   public void setRootNode(boolean isRootNode) {
395     this.isRootNode = isRootNode;
396   }
397
398   /**
399    * Change state.
400    *
401    * @param newState the new state
402    * @param action the action
403    */
404   public void changeState(NodeProcessingState newState, NodeProcessingAction action) {
405     /*
406      * NodeId may be null depending on the current node life-cycle state 
407      */
408     
409     if (getNodeId() != null) {
410       LOG.info(AaiUiMsgs.ACTIVE_INV_NODE_CHANGE_STATE, state.toString(), newState.toString(), action.toString());
411     } else {
412       LOG.info(AaiUiMsgs.ACTIVE_INV_NODE_CHANGE_STATE_NO_NODE_ID, state.toString(), newState.toString(), action.toString());
413     }
414     this.state = newState;
415   }
416
417   public boolean isSelfLinkPendingResolve() {
418     return selfLinkPendingResolve;
419   }
420
421   public void setSelfLinkPendingResolve(boolean selfLinkPendingResolve) {
422     this.selfLinkPendingResolve = selfLinkPendingResolve;
423   }
424
425   public boolean isSelflinkRetrievalFailure() {
426     return selflinkRetrievalFailure;
427   }
428
429   public void setSelflinkRetrievalFailure(boolean selflinkRetrievalFailure) {
430     this.selflinkRetrievalFailure = selflinkRetrievalFailure;
431   }
432
433   public void setOxmModelLoader(OxmModelLoader loader) {
434     this.oxmModelLoader = loader;
435   }
436
437   public boolean getSelfLinkProcessed() {
438     return selfLinkProcessed.get();
439   }
440
441   public void setSelfLinkProcessed(boolean selfLinkProcessed) {
442     this.selfLinkProcessed.set(selfLinkProcessed);
443   }
444
445   public boolean isDirectSelfLink() {
446     // https://aai-int1.test.att.com:8443/aai/v8/resources/id/2458124400
447     return isDirectSelfLink(this.selfLink);
448   }
449
450   /**
451    * Checks if is direct self link.
452    *
453    * @param link the link
454    * @return true, if is direct self link
455    */
456   public static boolean isDirectSelfLink(String link) {
457     // https://aai-int1.test.att.com:8443/aai/v8/resources/id/2458124400
458
459     if (link == null) {
460       return false;
461     }
462
463     return link.contains("/resources/id/");
464
465   }
466
467   public Map<String, String> getProperties() {
468     return properties;
469   }
470
471   /**
472    * Adds the error cause.
473    *
474    * @param error the error
475    */
476   public void addErrorCause(String error) {
477     if (!errorCauses.contains(error)) {
478       errorCauses.add(error);
479     }
480   }
481
482   /**
483    * Adds the property.
484    *
485    * @param key the key
486    * @param value the value
487    */
488   public void addProperty(String key, String value) {
489     properties.put(key, value);
490   }
491
492   public boolean isProcessingErrorOccurred() {
493     return processingErrorOccurred;
494   }
495
496   public void setProcessingErrorOccurred(boolean processingErrorOccurred) {
497     this.processingErrorOccurred = processingErrorOccurred;
498   }
499
500   public String getNodeId() {
501     return nodeId;
502   }
503
504   public void setNodeId(String nodeId) {
505     this.nodeId = nodeId;
506   }
507
508   public String getEntityType() {
509     return entityType;
510   }
511
512   public void setEntityType(String entityType) {
513     this.entityType = entityType;
514   }
515
516   public String getSelfLink() {
517     return selfLink;
518   }
519
520   /**
521    * Calculate edit attribute uri.
522    *
523    * @param link the link
524    * @return the string
525    */
526   public String calculateEditAttributeUri(String link) {
527     String uri = null;
528     Pattern pattern = Pattern.compile(URIRegexPattern);
529     Matcher matcher = pattern.matcher(link);
530     if (matcher.find()) {
531       uri = link.substring(matcher.end());
532     }
533     return uri;
534   }
535
536   /**
537    * Analyze self link relationship list.
538    *
539    * @param jsonResult the json result
540    * @return the relationship list
541    */
542   private RelationshipList analyzeSelfLinkRelationshipList(String jsonResult) {
543
544
545     RelationshipList relationshipList = null;
546
547     try {
548       relationshipList = mapper.readValue(jsonResult, RelationshipList.class);
549     } catch (Exception exc) {
550       LOG.error(AaiUiMsgs.SELF_LINK_RELATIONSHIP_LIST_ERROR, exc.toString());
551     }
552
553     return relationshipList;
554   }
555
556   /**
557    * Adds the relationship list.
558    *
559    * @param relationshipList the relationship list
560    */
561   public void addRelationshipList(RelationshipList relationshipList) {
562
563     if (!relationshipLists.contains(relationshipList)) {
564       relationshipLists.add(relationshipList);
565     }
566
567   }
568
569   /**
570    * Process pathed self link response.
571    *
572    * @param selfLinkJsonResponse the self link json response
573    * @param startNodeType the start node type
574    * @param startNodeResourceKey the start node resource key
575    */
576   public void processPathedSelfLinkResponse(String selfLinkJsonResponse, String startNodeType,
577       String startNodeResourceKey) {
578
579     if (selfLinkJsonResponse == null || selfLinkJsonResponse.length() == 0) {
580       LOG.error(AaiUiMsgs.SELF_LINK_NULL_EMPTY_RESPONSE);
581       return;
582     }
583
584     try {
585       JsonNode jsonNode = mapper.readValue(selfLinkJsonResponse, JsonNode.class);
586
587       Iterator<Entry<String, JsonNode>> fieldNames = jsonNode.fields();
588       Entry<String, JsonNode> field = null;
589
590       while (fieldNames.hasNext()) {
591
592         field = fieldNames.next();
593
594         /*
595          * Is there a way to tell if the field is an aggregate or an atomic value? This is where our
596          * flattening code needs to live
597          */
598
599         String fieldName = field.getKey();
600
601         if ("relationship-list".equals(fieldName)) {
602
603           /*
604            * Parse the relationship list like we were doing before, so we can determine whether or
605            * not to keep it or traverse it after we have performed the evaluative node depth logic.
606            */
607           RelationshipList relationshipList =
608               analyzeSelfLinkRelationshipList(field.getValue().toString());
609
610           if (relationshipList != null) {
611             this.relationshipLists.add(relationshipList);
612           } else {
613             LOG.info(AaiUiMsgs.NO_RELATIONSHIP_DISCOVERED, nodeId);
614           }
615         } else {
616           JsonNode nodeValue = field.getValue();
617
618           if (nodeValue != null && nodeValue.isValueNode()) {
619
620             /*
621              * before we blindly add the fieldName and value to our property set, let's do one more
622              * check to see if the field name is an entity type. If it is, then our complex
623              * attribute processing code will pick it up and process it instead, but this is
624              * probably more likely just for array node types, but we'll see.
625              */
626
627             if (oxmModelLoader.getEntityDescriptor(fieldName) == null) {
628               /*
629                * this is no an entity type as far as we can tell, so we can add it to our property
630                * set.
631                */
632
633               addProperty(fieldName, nodeValue.asText());
634
635             }
636
637           } else {
638
639             if (nodeValue.isArray()) {
640
641               /*
642                * make sure array entity-type collection is not an entityType before adding it to the
643                * property set. The expetation is that it will be added the visualization through a
644                * complex group or relationship.
645                */
646
647               if (oxmModelLoader.getEntityDescriptor(field.getKey()) == null) {
648                 /*
649                  * this is no an entity type as far as we can tell, so we can add it to our property
650                  * set.
651                  */
652
653                 addProperty(field.getKey(), nodeValue.toString());
654
655               }
656
657             } else {
658
659               complexGroups.add(nodeValue);
660
661             }
662
663           }
664
665         }
666
667       }
668
669     } catch (IOException exc) {
670       LOG.error(AaiUiMsgs.JSON_CONVERSION_ERROR, "POJO", exc.getLocalizedMessage());
671       this.setProcessingErrorOccurred(true);
672       this.addErrorCause(
673           "An error occurred while converting JSON into POJO = " + exc.getLocalizedMessage());
674     }
675
676   }
677
678   public void setSelfLink(String selfLink) {
679     this.selfLink = selfLink;
680   }
681
682   /**
683    * Adds the complex group.
684    *
685    * @param complexGroup the complex group
686    */
687   public void addComplexGroup(JsonNode complexGroup) {
688
689     if (!complexGroups.contains(complexGroup)) {
690       complexGroups.add(complexGroup);
691     }
692
693   }
694
695   /**
696    * Gets the padding.
697    *
698    * @param level the level
699    * @param paddingString the padding string
700    * @return the padding
701    */
702   private static String getPadding(int level, String paddingString) {
703     StringBuilder sb = new StringBuilder(32);
704     for (int x = 0; x < level; x++) {
705       sb.append(paddingString);
706     }
707     return sb.toString();
708   }
709
710   /**
711    * Dump node tree.
712    *
713    * @param showProperties the show properties
714    * @return the string
715    */
716   public String dumpNodeTree(boolean showProperties) {
717     return dumpNodeTree(0, showProperties);
718   }
719   
720   /**
721    * Dump node tree.
722    *
723    * @param level the level
724    * @param showProperties the show properties
725    * @return the string
726    */
727   private String dumpNodeTree(int level, boolean showProperties) {
728     StringBuilder sb = new StringBuilder(128);
729     String padding = getPadding(level, "   ");
730
731     sb.append(padding + " -> " + getNodeId() + "]").append("\n");
732     sb.append(padding + " -> primaryKeyName = " + primaryKeyName + "]").append("\n");
733     sb.append(padding + " -> primaryKeyValue = " + primaryKeyValue + "]").append("\n");
734     sb.append(padding + " -> entityType = " + entityType + "]").append("\n");
735
736     if (showProperties) {
737       Set<Entry<String, String>> entries = properties.entrySet();
738       for (Entry<String, String> entry : entries) {
739         sb.append(
740             padding + " ----> " + String.format("[ %s => %s ]", entry.getKey(), entry.getValue()))
741             .append("\n");
742       }
743     }
744
745     sb.append(padding + " ----> " + String.format("[ selfLink => %s ]", getSelfLink()))
746         .append("\n");
747
748     sb.append("\n").append(padding + " ----> Inbound Neighbors:").append("\n");
749
750     for (String inboundNeighbor : inboundNeighbors) {
751       sb.append("\n").append(inboundNeighbor.toString());
752     }
753
754     sb.append(padding + " ----> Outbound Neighbors:").append("\n");
755     sb.append("\n").append(padding + " ----> Outbound Neighbors:").append("\n");
756
757     for (String outboundNeighbor : outboundNeighbors) {
758       sb.append("\n").append(outboundNeighbor.toString());
759     }
760
761     return sb.toString();
762
763   }
764
765   public String getProcessingErrorCauses() {
766
767     StringBuilder sb = new StringBuilder(128);
768
769     for (String c : this.errorCauses) {
770       sb.append(c).append("\n");
771     }
772
773     return sb.toString();
774   }
775 }