2  * ============LICENSE_START=======================================================
 
   4  * ================================================================================
 
   5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
 
   6  * Copyright © 2017-2018 Amdocs
 
   7  * ================================================================================
 
   8  * Licensed under the Apache License, Version 2.0 (the "License");
 
   9  * you may not use this file except in compliance with the License.
 
  10  * You may obtain a copy of the License at
 
  12  *       http://www.apache.org/licenses/LICENSE-2.0
 
  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=========================================================
 
  21 package org.onap.aai.sparky.viewandinspect.entity;
 
  23 import java.io.IOException;
 
  24 import java.util.ArrayList;
 
  25 import java.util.Collection;
 
  26 import java.util.HashMap;
 
  27 import java.util.Iterator;
 
  28 import java.util.List;
 
  30 import java.util.Map.Entry;
 
  32 import java.util.concurrent.ConcurrentLinkedDeque;
 
  33 import java.util.concurrent.atomic.AtomicBoolean;
 
  35 import org.onap.aai.cl.api.Logger;
 
  36 import org.onap.aai.cl.eelf.LoggerFactory;
 
  37 import org.onap.aai.restclient.client.OperationResult;
 
  38 import org.onap.aai.sparky.config.oxm.OxmEntityLookup;
 
  39 import org.onap.aai.sparky.config.oxm.OxmModelLoader;
 
  40 import org.onap.aai.sparky.logging.AaiUiMsgs;
 
  41 import org.onap.aai.sparky.viewandinspect.config.VisualizationConfigs;
 
  42 import org.onap.aai.sparky.viewandinspect.enumeration.NodeProcessingAction;
 
  43 import org.onap.aai.sparky.viewandinspect.enumeration.NodeProcessingState;
 
  45 import com.fasterxml.jackson.databind.JsonNode;
 
  46 import com.fasterxml.jackson.databind.ObjectMapper;
 
  49  * The Class ActiveInventoryNode.
 
  51 public class ActiveInventoryNode {
 
  53   private static final Logger LOG = LoggerFactory.getInstance().getLogger(
 
  54       ActiveInventoryNode.class);
 
  56   public static final int DEFAULT_INIT_NODE_DEPTH = 1000;
 
  58   private String nodeId;
 
  59   private String selfLink;
 
  61   private boolean isRootNode;
 
  62   private ConcurrentLinkedDeque<String> inboundNeighbors;
 
  63   private ConcurrentLinkedDeque<String> outboundNeighbors;
 
  65   private ConcurrentLinkedDeque<String> inboundNeighborSelfLinks;
 
  66   private ConcurrentLinkedDeque<String> outboundNeighborSelfLinks;
 
  68   private List<JsonNode> complexGroups;
 
  69   private List<RelationshipList> relationshipLists;
 
  70   private int nodeDepth;
 
  71   private OperationResult opResult;
 
  73   private boolean processingErrorOccurred;
 
  74   private List<String> errorCauses;
 
  75   private boolean selflinkRetrievalFailure;
 
  76   private NodeProcessingState state;
 
  78   private boolean processedNeighbors;
 
  80   private boolean selfLinkPendingResolve;
 
  83    * I think we shouldn't be using this crutch flags.  If these things are meant
 
  84    * to represent the current state of the node, then they should be legitimate 
 
  88   private boolean selfLinkDeterminationPending;
 
  90   private AtomicBoolean selfLinkProcessed;
 
  91   private AtomicBoolean nodeIntegrityProcessed;
 
  93   private OxmModelLoader oxmModelLoader;
 
  94   private VisualizationConfigs visualizationConfigs;
 
  96   private String entityType;
 
  97   private String primaryKeyName;
 
  98   private String primaryKeyValue;
 
 100   private boolean nodeValidated;
 
 101   private boolean nodeIssue;
 
 102   private boolean ignoredByFilter;
 
 104   private boolean resolvedSelfLink;
 
 106   private Map<String, String> properties;
 
 107   private ArrayList<String> queryParams;
 
 109   private ObjectMapper mapper;
 
 111   private OxmEntityLookup oxmEntityLookup;
 
 114    * Instantiates a new active inventory node.
 
 118   public ActiveInventoryNode(VisualizationConfigs visualizationConfigs, OxmEntityLookup oxmEntityLookup) {
 
 119     this.oxmEntityLookup = oxmEntityLookup;
 
 121     this.entityType = null;
 
 122     this.selfLink = null;
 
 123     this.properties = new HashMap<String, String>();
 
 124     this.processingErrorOccurred = false;
 
 125     this.errorCauses = new ArrayList<String>();
 
 126     this.selflinkRetrievalFailure = false;
 
 127     this.nodeIssue = false;
 
 128     this.nodeValidated = false;
 
 129     this.state = NodeProcessingState.INIT;
 
 130     this.selfLinkPendingResolve = false;
 
 131     this.selfLinkDeterminationPending = false;
 
 133     selfLinkProcessed = new AtomicBoolean(Boolean.FALSE);
 
 134     nodeIntegrityProcessed = new AtomicBoolean(Boolean.FALSE);
 
 135     oxmModelLoader = null;
 
 136     this.visualizationConfigs = visualizationConfigs ;
 
 139     inboundNeighbors = new ConcurrentLinkedDeque<String>();
 
 140     outboundNeighbors = new ConcurrentLinkedDeque<String>();
 
 142     inboundNeighborSelfLinks = new ConcurrentLinkedDeque<String>();
 
 143     outboundNeighborSelfLinks = new ConcurrentLinkedDeque<String>();
 
 145     complexGroups = new ArrayList<JsonNode>();
 
 146     relationshipLists = new ArrayList<RelationshipList>();
 
 147     nodeDepth = DEFAULT_INIT_NODE_DEPTH;
 
 148     queryParams = new ArrayList<String>();
 
 150     mapper = new ObjectMapper();
 
 152     processedNeighbors = false;
 
 153     resolvedSelfLink = false;
 
 158   public void clearQueryParams() {
 
 162   public void addQueryParam(String queryParam) {
 
 163     if ( queryParam!= null) {
 
 164       if( !queryParams.contains(queryParam)) {
 
 165         queryParams.add(queryParam);
 
 170         public void addInboundSelfLink(String link) {
 
 176                 if (!inboundNeighborSelfLinks.contains(link)) {
 
 177                         inboundNeighborSelfLinks.add(link);
 
 182         public void addOutboundSelfLink(String link) {
 
 188                 if (!outboundNeighborSelfLinks.contains(link)) {
 
 189                         outboundNeighborSelfLinks.add(link);
 
 194         public Collection<String> getInboundNeighborSelfLinks() {
 
 195                 return inboundNeighborSelfLinks;
 
 198         public Collection<String> getOutboundNeighborSelfLinks() {
 
 199                 return outboundNeighborSelfLinks;
 
 202   public void addQueryParams(Collection<String> params) {
 
 204     if (params != null & params.size() > 0) {
 
 206       for (String param : params) {
 
 207         addQueryParam(param);
 
 213   public List<String> getQueryParams() {
 
 217   public void setSelfLinkDeterminationPending(boolean selfLinkDeterminationPending) {
 
 218     this.selfLinkDeterminationPending = selfLinkDeterminationPending;
 
 221   public boolean isSelfLinkDeterminationPending() {
 
 222     return selfLinkDeterminationPending;
 
 225   public NodeProcessingState getState() {
 
 229   public List<JsonNode> getComplexGroups() {
 
 230     return complexGroups;
 
 233   public List<RelationshipList> getRelationshipLists() {
 
 234     return relationshipLists;
 
 237   public OperationResult getOpResult() {
 
 241   public void setOpResult(OperationResult opResult) {
 
 242     this.opResult = opResult;
 
 245   public String getPrimaryKeyName() {
 
 246     return primaryKeyName;
 
 250    * Gets the visualization config.
 
 252    * @return the visualization config
 
 254   public VisualizationConfigs getvisualizationConfigs() {
 
 255     return visualizationConfigs;
 
 258   public int getNodeDepth() {
 
 262   public void setNodeDepth(int nodeDepth) {
 
 263     this.nodeDepth = nodeDepth;
 
 267    * Sets the visualization config.
 
 269    * @param visualizationConfig the new visualization config
 
 271   public void setvisualizationConfig(VisualizationConfigs visualizationConfigs) {
 
 272     this.visualizationConfigs = visualizationConfigs;
 
 275   public OxmModelLoader getOxmModelLoader() {
 
 276     return oxmModelLoader;
 
 279   public void setPrimaryKeyName(String primaryKeyName) {
 
 280     this.primaryKeyName = primaryKeyName;
 
 283   public String getPrimaryKeyValue() {
 
 284     return primaryKeyValue;
 
 287   public void setPrimaryKeyValue(String primaryKeyValue) {
 
 288     this.primaryKeyValue = primaryKeyValue;
 
 291   public boolean isNodeValidated() {
 
 292     return nodeValidated;
 
 295   public void setNodeValidated(boolean nodeValidated) {
 
 296     this.nodeValidated = nodeValidated;
 
 299   public boolean isNodeIssue() {
 
 303   public boolean isIgnoredByFilter() {
 
 304     return ignoredByFilter;
 
 307   public void setIgnoredByFilter(boolean ignoredByFilter) {
 
 308     this.ignoredByFilter = ignoredByFilter;
 
 311   public void setNodeIssue(boolean nodeIssue) {
 
 312     this.nodeIssue = nodeIssue;
 
 316    * Checks for processed neighbors.
 
 318    * @return true, if successful
 
 320   public boolean hasProcessedNeighbors() {
 
 321     return processedNeighbors;
 
 324   public void setProcessedNeighbors(boolean processedNeighbors) {
 
 325     this.processedNeighbors = processedNeighbors;
 
 329    * Checks for resolved self link.
 
 331    * @return true, if successful
 
 333   public boolean hasResolvedSelfLink() {
 
 334     return resolvedSelfLink;
 
 337   public void setResolvedSelfLink(boolean resolvedSelfLink) {
 
 338     this.resolvedSelfLink = resolvedSelfLink;
 
 342    * Checks for neighbors.
 
 344    * @return true, if successful
 
 346   public boolean hasNeighbors() {
 
 347     return (inboundNeighbors.size() > 0 || outboundNeighbors.size() > 0);
 
 351    * Adds the inbound neighbor.
 
 353    * @param nodeId the node id
 
 355   public void addInboundNeighbor(String nodeId) {
 
 357     if (nodeId == null) {
 
 361     if (!inboundNeighbors.contains(nodeId)) {
 
 362       inboundNeighbors.add(nodeId);
 
 368    * Adds the outbound neighbor.
 
 370    * @param nodeId the node id
 
 372   public void addOutboundNeighbor(String nodeId) {
 
 374     if (nodeId == null) {
 
 378     if (!outboundNeighbors.contains(nodeId)) {
 
 379       outboundNeighbors.add(nodeId);
 
 384   public boolean isAtMaxDepth() {
 
 385     return (nodeDepth >= this.visualizationConfigs.getMaxSelfLinkTraversalDepth());
 
 388   public ConcurrentLinkedDeque<String> getInboundNeighbors() {
 
 389     return inboundNeighbors;
 
 392   public void setInboundNeighbors(ConcurrentLinkedDeque<String> inboundNeighbors) {
 
 393     this.inboundNeighbors = inboundNeighbors;
 
 396   public Collection<String> getOutboundNeighbors() {
 
 397     List<String> result = new ArrayList<String>();
 
 399     Iterator<String> neighborIterator = outboundNeighbors.iterator();
 
 401     while (neighborIterator.hasNext()) {
 
 402       result.add(neighborIterator.next());
 
 411    * @param newDepth the new depth
 
 412    * @return true, if successful
 
 414   public boolean changeDepth(int newDepth) {
 
 416     boolean nodeDepthWasChanged = false;
 
 418     if (newDepth < nodeDepth) {
 
 419       LOG.info(AaiUiMsgs.ACTIVE_INV_NODE_CHANGE_DEPTH, nodeId,
 
 420           String.valueOf(this.nodeDepth), String.valueOf(newDepth));
 
 421       this.nodeDepth = newDepth;
 
 422       nodeDepthWasChanged = true;
 
 425     return nodeDepthWasChanged;
 
 429   public void setOutboundNeighbors(ConcurrentLinkedDeque<String> outboundNeighbors) {
 
 430     this.outboundNeighbors = outboundNeighbors;
 
 433   public boolean isRootNode() {
 
 437   public void setRootNode(boolean isRootNode) {
 
 438     this.isRootNode = isRootNode;
 
 444    * @param newState the new state
 
 445    * @param action the action
 
 447   public void changeState(NodeProcessingState newState, NodeProcessingAction action) {
 
 449      * NodeId may be null depending on the current node life-cycle state 
 
 452     if (getNodeId() != null) {
 
 453       LOG.info(AaiUiMsgs.ACTIVE_INV_NODE_CHANGE_STATE, state.toString(), newState.toString(), action.toString());
 
 455       LOG.info(AaiUiMsgs.ACTIVE_INV_NODE_CHANGE_STATE_NO_NODE_ID, state.toString(), newState.toString(), action.toString());
 
 457     this.state = newState;
 
 460   public boolean isSelfLinkPendingResolve() {
 
 461     return selfLinkPendingResolve;
 
 464   public void setSelfLinkPendingResolve(boolean selfLinkPendingResolve) {
 
 465     this.selfLinkPendingResolve = selfLinkPendingResolve;
 
 468   public boolean isSelflinkRetrievalFailure() {
 
 469     return selflinkRetrievalFailure;
 
 472   public void setSelflinkRetrievalFailure(boolean selflinkRetrievalFailure) {
 
 473     this.selflinkRetrievalFailure = selflinkRetrievalFailure;
 
 476   public void setOxmModelLoader(OxmModelLoader loader) {
 
 477     this.oxmModelLoader = loader;
 
 480   public boolean getSelfLinkProcessed() {
 
 481     return selfLinkProcessed.get();
 
 484   public void setSelfLinkProcessed(boolean selfLinkProcessed) {
 
 485     this.selfLinkProcessed.set(selfLinkProcessed);
 
 488   public boolean getNodeIntegrityProcessed() {
 
 489     return nodeIntegrityProcessed.get();
 
 492   public void setNodeIntegrityProcessed(boolean nodeIntegrityProcessed) {
 
 493     this.nodeIntegrityProcessed.set(nodeIntegrityProcessed);
 
 496   public boolean isDirectSelfLink() {
 
 497     return isDirectSelfLink(this.selfLink);
 
 501    * Checks if is direct self link.
 
 503    * @param link the link
 
 504    * @return true, if is direct self link
 
 506   public static boolean isDirectSelfLink(String link) {
 
 512     return link.contains("/resources/id/");
 
 516   public Map<String, String> getProperties() {
 
 521    * Adds the error cause.
 
 523    * @param error the error
 
 525   public void addErrorCause(String error) {
 
 526     if (!errorCauses.contains(error)) {
 
 527       errorCauses.add(error);
 
 535    * @param value the value
 
 537   public void addProperty(String key, String value) {
 
 538     properties.put(key, value);
 
 541   public boolean isProcessingErrorOccurred() {
 
 542     return processingErrorOccurred;
 
 545   public void setProcessingErrorOccurred(boolean processingErrorOccurred) {
 
 546     this.processingErrorOccurred = processingErrorOccurred;
 
 549   public String getNodeId() {
 
 553   public void setNodeId(String nodeId) {
 
 554     this.nodeId = nodeId;
 
 557   public String getEntityType() {
 
 561   public void setEntityType(String entityType) {
 
 562     this.entityType = entityType;
 
 565   public String getSelfLink() {
 
 570    * Analyze self link relationship list.
 
 572    * @param jsonResult the json result
 
 573    * @return the relationship list
 
 575   private RelationshipList analyzeSelfLinkRelationshipList(String jsonResult) {
 
 578     RelationshipList relationshipList = null;
 
 581       relationshipList = mapper.readValue(jsonResult, RelationshipList.class);
 
 582     } catch (Exception exc) {
 
 583       LOG.error(AaiUiMsgs.SELF_LINK_RELATIONSHIP_LIST_ERROR, exc.toString());
 
 586     return relationshipList;
 
 590    * Adds the relationship list.
 
 592    * @param relationshipList the relationship list
 
 594   public void addRelationshipList(RelationshipList relationshipList) {
 
 596     if (!relationshipLists.contains(relationshipList)) {
 
 597       relationshipLists.add(relationshipList);
 
 603    * Process pathed self link response.
 
 605    * @param selfLinkJsonResponse the self link json response
 
 606    * @param startNodeType the start node type
 
 607    * @param startNodeResourceKey the start node resource key
 
 609   public void processPathedSelfLinkResponse(String selfLinkJsonResponse, String startNodeType,
 
 610       String startNodeResourceKey) {
 
 612     if (selfLinkJsonResponse == null || selfLinkJsonResponse.length() == 0) {
 
 613       LOG.error(AaiUiMsgs.SELF_LINK_NULL_EMPTY_RESPONSE);
 
 618       JsonNode jsonNode = mapper.readValue(selfLinkJsonResponse, JsonNode.class);
 
 620       Iterator<Entry<String, JsonNode>> fieldNames = jsonNode.fields();
 
 621       Entry<String, JsonNode> field = null;
 
 623       while (fieldNames.hasNext()) {
 
 625         field = fieldNames.next();
 
 628          * Is there a way to tell if the field is an aggregate or an atomic value? This is where our
 
 629          * flattening code needs to live
 
 632         String fieldName = field.getKey();
 
 634         if ("relationship-list".equals(fieldName)) {
 
 637            * Parse the relationship list like we were doing before, so we can determine whether or
 
 638            * not to keep it or traverse it after we have performed the evaluative node depth logic.
 
 640           RelationshipList relationshipList =
 
 641               analyzeSelfLinkRelationshipList(field.getValue().toString());
 
 643           if (relationshipList != null) {
 
 644             this.relationshipLists.add(relationshipList);
 
 646             LOG.info(AaiUiMsgs.NO_RELATIONSHIP_DISCOVERED, nodeId);
 
 649           JsonNode nodeValue = field.getValue();
 
 651           if (nodeValue != null) {
 
 652             if (nodeValue.isValueNode()) {
 
 655                * before we blindly add the fieldName and value to our property set, let's do one more
 
 656                * check to see if the field name is an entity type. If it is, then our complex
 
 657                * attribute processing code will pick it up and process it instead, but this is
 
 658                * probably more likely just for array node types, but we'll see.
 
 661               handleNodeValue(fieldName, nodeValue.asText());
 
 663             } else if (nodeValue.isArray()) {
 
 666                * make sure array entity-type collection is not an entityType before adding it to the
 
 667                * property set. The expetation is that it will be added the visualization through a
 
 668                * complex group or relationship.
 
 671               handleNodeValue(field.getKey(), nodeValue.toString());
 
 673               complexGroups.add(nodeValue);
 
 680     } catch (IOException exc) {
 
 681       LOG.error(AaiUiMsgs.JSON_CONVERSION_ERROR, "POJO", exc.getLocalizedMessage());
 
 682       this.setProcessingErrorOccurred(true);
 
 684           "An error occurred while converting JSON into POJO = " + exc.getLocalizedMessage());
 
 689   private void handleNodeValue(String fieldName, String fieldValue) {
 
 690     if (oxmEntityLookup.getEntityDescriptors().get(fieldName) == null) {
 
 692        * this is no an entity type as far as we can tell, so we can add it to our property
 
 696       addProperty(fieldName, fieldValue);
 
 701   public void setSelfLink(String selfLink) {
 
 702     this.selfLink = selfLink;
 
 706    * Adds the complex group.
 
 708    * @param complexGroup the complex group
 
 710   public void addComplexGroup(JsonNode complexGroup) {
 
 712     if (!complexGroups.contains(complexGroup)) {
 
 713       complexGroups.add(complexGroup);
 
 721    * @param level the level
 
 722    * @param paddingString the padding string
 
 723    * @return the padding
 
 725   private static String getPadding(int level, String paddingString) {
 
 726     StringBuilder sb = new StringBuilder(32);
 
 727     for (int x = 0; x < level; x++) {
 
 728       sb.append(paddingString);
 
 730     return sb.toString();
 
 736    * @param showProperties the show properties
 
 739   public String dumpNodeTree(boolean showProperties) {
 
 740     return dumpNodeTree(0, showProperties);
 
 746    * @param level the level
 
 747    * @param showProperties the show properties
 
 750   private String dumpNodeTree(int level, boolean showProperties) {
 
 751     StringBuilder sb = new StringBuilder(128);
 
 752     String padding = getPadding(level, "   ");
 
 754     sb.append(padding + " -> " + getNodeId() + "]").append("\n");
 
 755     sb.append(padding + " -> primaryKeyName = " + primaryKeyName + "]").append("\n");
 
 756     sb.append(padding + " -> primaryKeyValue = " + primaryKeyValue + "]").append("\n");
 
 757     sb.append(padding + " -> entityType = " + entityType + "]").append("\n");
 
 759     if (showProperties) {
 
 760       Set<Entry<String, String>> entries = properties.entrySet();
 
 761       for (Entry<String, String> entry : entries) {
 
 763             padding + " ----> " + String.format("[ %s => %s ]", entry.getKey(), entry.getValue()))
 
 768     sb.append(padding + " ----> " + String.format("[ selfLink => %s ]", getSelfLink()))
 
 771     sb.append("\n").append(padding + " ----> Inbound Neighbors:").append("\n");
 
 773     for (String inboundNeighbor : inboundNeighbors) {
 
 774       sb.append("\n").append(inboundNeighbor.toString());
 
 777     sb.append(padding + " ----> Outbound Neighbors:").append("\n");
 
 778     sb.append("\n").append(padding + " ----> Outbound Neighbors:").append("\n");
 
 780     for (String outboundNeighbor : outboundNeighbors) {
 
 781       sb.append("\n").append(outboundNeighbor.toString());
 
 784     return sb.toString();
 
 788   public String getProcessingErrorCauses() {
 
 790     StringBuilder sb = new StringBuilder(128);
 
 792     for (String c : this.errorCauses) {
 
 793       sb.append(c).append("\n");
 
 796     return sb.toString();