2 * ============LICENSE_START=======================================================
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
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 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
23 package org.onap.aai.sparky.viewandinspect.services;
25 import static java.util.concurrent.CompletableFuture.supplyAsync;
27 import java.io.IOException;
28 import java.util.List;
30 import java.util.concurrent.ConcurrentHashMap;
31 import java.util.concurrent.ExecutorService;
32 import java.util.concurrent.atomic.AtomicInteger;
34 import org.onap.aai.cl.api.Logger;
35 import org.onap.aai.cl.eelf.LoggerFactory;
36 import org.onap.aai.restclient.client.OperationResult;
37 import org.onap.aai.sparky.config.oxm.OxmEntityDescriptor;
38 import org.onap.aai.sparky.config.oxm.OxmEntityLookup;
39 import org.onap.aai.sparky.dal.GizmoAdapter;
40 import org.onap.aai.sparky.logging.AaiUiMsgs;
41 import org.onap.aai.sparky.sync.entity.SearchableEntity;
42 import org.onap.aai.sparky.util.NodeUtils;
43 import org.onap.aai.sparky.viewandinspect.config.SparkyConstants;
44 import org.onap.aai.sparky.viewandinspect.config.VisualizationConfigs;
45 import org.onap.aai.sparky.viewandinspect.entity.ActiveInventoryNode;
46 import org.onap.aai.sparky.viewandinspect.entity.GizmoEntity;
47 import org.onap.aai.sparky.viewandinspect.entity.GizmoRelationshipEntity;
48 import org.onap.aai.sparky.viewandinspect.entity.GizmoRelationshipHint;
49 import org.onap.aai.sparky.viewandinspect.entity.InlineMessage;
50 import org.onap.aai.sparky.viewandinspect.entity.NodeProcessingTransaction;
51 import org.onap.aai.sparky.viewandinspect.entity.QueryParams;
52 import org.onap.aai.sparky.viewandinspect.enumeration.NodeProcessingAction;
53 import org.onap.aai.sparky.viewandinspect.enumeration.NodeProcessingState;
54 import org.onap.aai.sparky.viewandinspect.task.PerformGizmoNodeSelfLinkProcessingTask;
56 import com.fasterxml.jackson.annotation.JsonInclude.Include;
57 import com.fasterxml.jackson.databind.ObjectMapper;
58 import com.fasterxml.jackson.databind.PropertyNamingStrategy;
61 * The Class SelfLinkNodeCollector.
63 public class BaseGizmoVisualizationContext implements VisualizationContext {
65 private static final int MAX_DEPTH_EVALUATION_ATTEMPTS = 100;
67 private static final Logger LOG =
68 LoggerFactory.getInstance().getLogger(BaseGizmoVisualizationContext.class);
70 private final GizmoAdapter gizmoAdapter;
72 private AtomicInteger numLinksDiscovered;
73 private AtomicInteger numSuccessfulLinkResolveFromCache;
74 private AtomicInteger numSuccessfulLinkResolveFromFromServer;
75 private AtomicInteger numFailedLinkResolve;
76 private AtomicInteger aaiWorkOnHand;
78 private VisualizationConfigs visualizationConfigs;
80 private AtomicInteger totalLinksRetrieved;
82 private final long contextId;
83 private final String contextIdStr;
84 private long lastProcessStatesSummaryLogInMs = -1;
87 private ObjectMapper mapper;
88 private InlineMessage inlineMessage = null;
90 private ExecutorService graphExecutorService;
91 private OxmEntityLookup oxmEntityLookup;
92 private boolean rootNodeFound;
95 * The node cache is intended to be a flat structure indexed by a primary key to avoid needlessly
96 * re-requesting the same self-links over-and-over again, to speed up the overall render time and
97 * more importantly to reduce the network cost of determining information we already have.
99 private ConcurrentHashMap<String, ActiveInventoryNode> nodeCache;
102 * Instantiates a new self link node collector.
104 * @param loader the loader
105 * @throws Exception the exception
107 public BaseGizmoVisualizationContext(long contextId, GizmoAdapter gizmoAdapter,
108 ExecutorService graphExecutorService, VisualizationConfigs visualizationConfigs,
109 OxmEntityLookup oxmEntityLookup) throws Exception {
111 this.contextId = contextId;
112 this.contextIdStr = "[Context-Id=" + contextId + "]";
113 this.gizmoAdapter = gizmoAdapter;
114 this.graphExecutorService = graphExecutorService;
115 this.visualizationConfigs = visualizationConfigs;
116 this.oxmEntityLookup = oxmEntityLookup;
118 this.nodeCache = new ConcurrentHashMap<String, ActiveInventoryNode>();
119 this.numLinksDiscovered = new AtomicInteger(0);
120 this.totalLinksRetrieved = new AtomicInteger(0);
121 this.numSuccessfulLinkResolveFromCache = new AtomicInteger(0);
122 this.numSuccessfulLinkResolveFromFromServer = new AtomicInteger(0);
123 this.numFailedLinkResolve = new AtomicInteger(0);
124 this.aaiWorkOnHand = new AtomicInteger(0);
126 this.mapper = new ObjectMapper();
127 mapper.setSerializationInclusion(Include.NON_EMPTY);
128 mapper.setPropertyNamingStrategy(new PropertyNamingStrategy.KebabCaseStrategy());
129 this.rootNodeFound = false;
132 protected boolean isRootNodeFound() {
133 return rootNodeFound;
136 protected void setRootNodeFound(boolean rootNodeFound) {
137 this.rootNodeFound = rootNodeFound;
140 public long getContextId() {
144 public GizmoAdapter getGizmoAdapter() {
149 * Process self link response.
151 * @param nodeId the node id
153 private void processSelfLinkResponse(String nodeId) {
155 if (nodeId == null) {
156 LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR,
157 "Cannot process self link" + " response because nodeId is null");
161 ActiveInventoryNode ain = nodeCache.get(nodeId);
164 LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR,
165 "Cannot process self link response" + " because can't find node for id = " + nodeId);
169 GizmoEntity gizmoEntity = null;
172 gizmoEntity = mapper.readValue(ain.getOpResult().getResult(), GizmoEntity.class);
173 } catch (Exception exc) {
174 exc.printStackTrace();
175 LOG.error(AaiUiMsgs.SELF_LINK_JSON_PARSE_ERROR, "Failed to marshal json"
176 + " response str into JsonNode with error, " + exc.getLocalizedMessage());
177 ain.changeState(NodeProcessingState.ERROR,
178 NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_ERROR);
182 if (gizmoEntity == null) {
184 LOG.error(AaiUiMsgs.SELF_LINK_JSON_PARSE_ERROR,
185 "Failed to parse json node str." + " Parse resulted a null value.");
186 ain.changeState(NodeProcessingState.ERROR,
187 NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_ERROR);
192 * Now that we have the gizmo entity we can populate the AIN node with it, as well as the
196 ain.setEntityType(gizmoEntity.getType());
198 ain.setPrimaryKeyName(getEntityTypePrimaryKeyName(gizmoEntity.getType()));
200 OxmEntityDescriptor descriptor = oxmEntityLookup.getEntityDescriptors().get(gizmoEntity);
202 if (descriptor != null) {
203 ain.setPrimaryKeyValue(getPrimaryKeyValues(gizmoEntity.getProperties(),
204 descriptor.getPrimaryKeyAttributeNames()));
206 LOG.error(AaiUiMsgs.ERROR_GENERIC, "Could not determine oxm descriptor for entity type = " + gizmoEntity.getType());
209 gizmoEntity.getProperties().forEach((key, value) -> {
210 ain.getProperties().put(key, value);
213 // add edit attributes link
214 if (ain.getSelfLink() != null) {
215 ain.addProperty(SparkyConstants.URI_ATTR_NAME, ain.getSelfLink());
221 * Only discover neighbors if our depth is less than the Max-Traversal-Depth
224 if (ain.getNodeDepth() < this.visualizationConfigs.getMaxSelfLinkTraversalDepth()) {
227 * I think the next thing to do is:
229 * 1. Calculate the source / target node id 2. Add the nodeId to the incoming / outgoing links
230 * collection 3. Add the node to the node cache for processing
233 String resourceLink = null;
234 String relationshipNodeId = null;
235 ActiveInventoryNode relationshipNode = null;
237 for (GizmoRelationshipHint inRelationship : gizmoEntity.getIn()) {
239 if (inRelationship.getSource() != null) {
241 resourceLink = NodeUtils.extractRawGizmoPathWithoutVersion(inRelationship.getSource());
242 relationshipNodeId = NodeUtils.generateUniqueShaDigest(resourceLink);
244 if (!nodeCache.containsKey(relationshipNodeId)) {
246 relationshipNode = new ActiveInventoryNode(visualizationConfigs, oxmEntityLookup);
247 relationshipNode.setNodeId(relationshipNodeId);
248 relationshipNode.setSelfLink(resourceLink);
249 relationshipNode.changeState(NodeProcessingState.SELF_LINK_UNRESOLVED,
250 NodeProcessingAction.NEW_NODE_PROCESSED);
252 ain.addInboundNeighbor(relationshipNodeId);
254 addNode(relationshipNode);
261 for (GizmoRelationshipHint outRelationship : gizmoEntity.getOut()) {
263 if (outRelationship.getTarget() != null) {
265 resourceLink = NodeUtils.extractRawGizmoPathWithoutVersion(outRelationship.getTarget());
266 relationshipNodeId = NodeUtils.generateUniqueShaDigest(resourceLink);
268 if (!nodeCache.containsKey(relationshipNodeId)) {
270 relationshipNode = new ActiveInventoryNode(visualizationConfigs, oxmEntityLookup);
271 relationshipNode.setNodeId(relationshipNodeId);
272 relationshipNode.setSelfLink(resourceLink);
273 relationshipNode.changeState(NodeProcessingState.SELF_LINK_UNRESOLVED,
274 NodeProcessingAction.NEW_NODE_PROCESSED);
276 ain.addOutboundNeighbor(relationshipNodeId);
278 addNode(relationshipNode);
286 ain.changeState(NodeProcessingState.READY, NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_OK);
291 * Perform self link resolve.
293 * @param nodeId the node id
295 private void performSelfLinkResolve(String nodeId) {
297 if (nodeId == null) {
298 LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR,
299 "Resolve of self-link" + " has been skipped because provided nodeId is null");
303 ActiveInventoryNode ain = nodeCache.get(nodeId);
306 LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, "Failed to find node with id, " + nodeId
307 + ", from node cache. Resolve self-link method has been skipped.");
311 if (!ain.isSelfLinkPendingResolve()) {
313 ain.setSelfLinkPendingResolve(true);
315 // kick off async self-link resolution
317 if (LOG.isDebugEnabled()) {
318 LOG.debug(AaiUiMsgs.DEBUG_GENERIC,
319 "About to process node in SELF_LINK_UNPROCESSED State, link = " + ain.getSelfLink());
322 numLinksDiscovered.incrementAndGet();
325 * If the current node is the search target, we want to see everything the node has to offer
326 * from the self-link and not filter it to a single node.
329 NodeProcessingTransaction txn = new NodeProcessingTransaction();
330 txn.setProcessingNode(ain);
331 txn.setRequestParameters(null);
332 aaiWorkOnHand.incrementAndGet();
333 supplyAsync(new PerformGizmoNodeSelfLinkProcessingTask(txn, null, gizmoAdapter),
334 graphExecutorService).whenComplete((nodeTxn, error) -> {
339 * an error processing the self link should probably result in the node processing
340 * state shifting to ERROR
343 nodeTxn.getProcessingNode().setSelflinkRetrievalFailure(true);
345 nodeTxn.getProcessingNode().changeState(NodeProcessingState.ERROR,
346 NodeProcessingAction.SELF_LINK_RESOLVE_ERROR);
348 nodeTxn.getProcessingNode().setSelfLinkPendingResolve(false);
352 totalLinksRetrieved.incrementAndGet();
354 OperationResult opResult = nodeTxn.getOpResult();
356 if (opResult != null && opResult.wasSuccessful()) {
358 if (!opResult.wasSuccessful()) {
359 numFailedLinkResolve.incrementAndGet();
362 if (opResult.isFromCache()) {
363 numSuccessfulLinkResolveFromCache.incrementAndGet();
365 numSuccessfulLinkResolveFromFromServer.incrementAndGet();
369 nodeTxn.getProcessingNode().setOpResult(opResult);
370 nodeTxn.getProcessingNode().changeState(
371 NodeProcessingState.SELF_LINK_RESPONSE_UNPROCESSED,
372 NodeProcessingAction.SELF_LINK_RESOLVE_OK);
374 nodeTxn.getProcessingNode().setSelfLinkProcessed(true);
375 nodeTxn.getProcessingNode().setSelfLinkPendingResolve(false);
378 LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR,
379 "Self Link retrieval for link," + txn.getSelfLinkWithModifiers()
380 + ", failed with error code," + nodeTxn.getOpResult().getResultCode()
381 + ", and message," + nodeTxn.getOpResult().getResult());
383 nodeTxn.getProcessingNode().setSelflinkRetrievalFailure(true);
384 nodeTxn.getProcessingNode().setSelfLinkProcessed(true);
386 nodeTxn.getProcessingNode().changeState(NodeProcessingState.ERROR,
387 NodeProcessingAction.SELF_LINK_RESOLVE_ERROR);
389 nodeTxn.getProcessingNode().setSelfLinkPendingResolve(false);
394 aaiWorkOnHand.decrementAndGet();
402 public GizmoRelationshipEntity getGizmoRelationshipEntity(String gizmoJsonResponse) {
404 GizmoRelationshipEntity gizmoRelationship = null;
406 gizmoRelationship = mapper.readValue(gizmoJsonResponse, GizmoRelationshipEntity.class);
407 } catch (IOException exc) {
408 LOG.error(AaiUiMsgs.ERROR_GENERIC, "Failed to map json to GizmoRelationshipEntity. Error: " + exc.getMessage());
411 return gizmoRelationship;
415 public String getPrimaryKeyValues(Map<String, String> properties, List<String> pkeyNames) {
417 StringBuilder sb = new StringBuilder(64);
419 if (pkeyNames.size() > 0) {
420 String primaryKey = properties.get(pkeyNames.get(0));
421 if (primaryKey != null) {
422 sb.append(primaryKey);
424 // this should be a fatal error because unless we can
425 // successfully retrieve all the expected keys we'll end up
426 // with a garbage node
427 LOG.error(AaiUiMsgs.EXTRACTION_ERROR, "ERROR: Failed to extract" + " keyName, "
428 + pkeyNames.get(0) + ", from properties , " + properties);
432 for (int i = 1; i < pkeyNames.size(); i++) {
434 String kv = properties.get(pkeyNames.get(i));
436 sb.append("/").append(kv);
438 // this should be a fatal error because unless we can
439 // successfully retrieve all the expected keys we'll end up
440 // with a garbage node
441 LOG.error(AaiUiMsgs.EXTRACTION_ERROR, "ERROR: failed to extract keyName, "
442 + pkeyNames.get(i) + ", from properties, " + properties);
447 return sb.toString();
458 * Find and mark root node.
460 * @param queryParams the query params
461 * @return true, if successful
463 private void findAndMarkRootNode(QueryParams queryParams) {
465 if (isRootNodeFound()) {
469 for (ActiveInventoryNode cacheNode : nodeCache.values()) {
471 if (queryParams.getSearchTargetNodeId().equals(cacheNode.getNodeId())) {
472 cacheNode.setNodeDepth(0);
473 cacheNode.setRootNode(true);
474 LOG.info(AaiUiMsgs.ROOT_NODE_DISCOVERED, queryParams.getSearchTargetNodeId());
475 setRootNodeFound(true);
481 public void addNode(ActiveInventoryNode node) {
487 nodeCache.putIfAbsent(node.getNodeId(), node);
490 public VisualizationConfigs getVisualizationConfigs() {
491 return visualizationConfigs;
494 public void setVisualizationConfigs(VisualizationConfigs visualizationConfigs) {
495 this.visualizationConfigs = visualizationConfigs;
498 public OxmEntityLookup getOxmEntityLookup() {
499 return oxmEntityLookup;
502 public void setOxmEntityLookup(OxmEntityLookup oxmEntityLookup) {
503 this.oxmEntityLookup = oxmEntityLookup;
506 public ObjectMapper getMapper() {
510 public void setMapper(ObjectMapper mapper) {
511 this.mapper = mapper;
514 private void dumpThrottledWorkOnHandLog() {
515 dumpThrottledWorkOnHandLog(false);
518 private void dumpThrottledWorkOnHandLog(boolean override) {
520 if ((lastProcessStatesSummaryLogInMs < 0)
521 || ((System.currentTimeMillis() > (lastProcessStatesSummaryLogInMs + 5000))) || override) {
523 lastProcessStatesSummaryLogInMs = System.currentTimeMillis();
528 int numSelfLinkUnresolved = 0;
529 int numSelfLinkResponseUnprocessed = 0;
531 for (ActiveInventoryNode cacheNode : nodeCache.values()) {
533 switch (cacheNode.getState()) {
549 case SELF_LINK_UNRESOLVED: {
550 numSelfLinkUnresolved++;
554 case SELF_LINK_RESPONSE_UNPROCESSED: {
555 numSelfLinkResponseUnprocessed++;
565 LOG.info(AaiUiMsgs.INFO_GENERIC,
567 "ProcessCurrentStates for ContextId=%s, [PendingTxns=%d, numInit=%d, numSelfLinkUnresolved=%d, numSelfLinkResponseUnProcessed=%d, numReady=%d, numError=%d]",
568 contextIdStr, aaiWorkOnHand.get(), numInit, numSelfLinkUnresolved, numSelfLinkResponseUnprocessed,
569 numReady, numError));
575 * Process current node states.
577 * @param rootNodeDiscovered the root node discovered
579 private void processCurrentNodeStates(QueryParams queryParams) {
581 * Force an evaluation of node depths before determining if we should limit state-based
582 * traversal or processing.
585 findAndMarkRootNode(queryParams);
587 verifyOutboundNeighbors();
589 for (ActiveInventoryNode cacheNode : nodeCache.values()) {
591 if (LOG.isDebugEnabled()) {
592 LOG.debug(AaiUiMsgs.DEBUG_GENERIC, "processCurrentNodeState(), nid = "
593 + cacheNode.getNodeId() + " , nodeDepth = " + cacheNode.getNodeDepth());
596 switch (cacheNode.getState()) {
599 processInitialState(cacheNode.getNodeId());
608 case SELF_LINK_UNRESOLVED: {
609 performSelfLinkResolve(cacheNode.getNodeId());
613 case SELF_LINK_RESPONSE_UNPROCESSED: {
614 processSelfLinkResponse(cacheNode.getNodeId());
624 dumpThrottledWorkOnHandLog();
630 public int getNumSuccessfulLinkResolveFromCache() {
631 return numSuccessfulLinkResolveFromCache.get();
634 public int getNumSuccessfulLinkResolveFromFromServer() {
635 return numSuccessfulLinkResolveFromFromServer.get();
638 public int getNumFailedLinkResolve() {
639 return numFailedLinkResolve.get();
642 public InlineMessage getInlineMessage() {
643 return inlineMessage;
646 public void setInlineMessage(InlineMessage inlineMessage) {
647 this.inlineMessage = inlineMessage;
650 public ConcurrentHashMap<String, ActiveInventoryNode> getNodeCache() {
657 * Process initial state.
659 * @param nodeId the node id
661 private void processInitialState(String nodeId) {
663 if (nodeId == null) {
664 LOG.error(AaiUiMsgs.FAILED_TO_PROCESS_INITIAL_STATE, "Node id is null");
668 ActiveInventoryNode cachedNode = nodeCache.get(nodeId);
670 if (cachedNode == null) {
671 LOG.error(AaiUiMsgs.FAILED_TO_PROCESS_INITIAL_STATE,
672 "Node cannot be" + " found for nodeId, " + nodeId);
676 if (cachedNode.getSelfLink() == null) {
678 if (cachedNode.getNodeId() == null) {
681 * if the self link is null at the INIT state, which could be valid if this node is a
682 * complex attribute group which didn't originate from a self-link, but in that situation
683 * both the node id and node key should already be set.
686 cachedNode.changeState(NodeProcessingState.ERROR, NodeProcessingAction.NODE_IDENTITY_ERROR);
690 if (cachedNode.getNodeId() != null) {
693 * This should be the success path branch if the self-link is not set
696 cachedNode.changeState(NodeProcessingState.SELF_LINK_RESPONSE_UNPROCESSED,
697 NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_OK);
703 if (cachedNode.hasResolvedSelfLink()) {
704 LOG.error(AaiUiMsgs.INVALID_RESOLVE_STATE_DURING_INIT);
705 cachedNode.changeState(NodeProcessingState.ERROR,
706 NodeProcessingAction.UNEXPECTED_STATE_TRANSITION);
708 cachedNode.changeState(NodeProcessingState.SELF_LINK_UNRESOLVED,
709 NodeProcessingAction.SELF_LINK_SET);
715 * Process skeleton node.
717 * @param skeletonNode the skeleton node
718 * @param queryParams the query params
720 private void processSearchableEntity(SearchableEntity searchTargetEntity,
721 QueryParams queryParams) {
723 if (searchTargetEntity == null) {
727 if (searchTargetEntity.getId() == null) {
728 LOG.error(AaiUiMsgs.FAILED_TO_PROCESS_SKELETON_NODE, "Failed to process skeleton"
729 + " node because nodeId is null for node, " + searchTargetEntity.getLink());
733 ActiveInventoryNode newNode =
734 new ActiveInventoryNode(this.visualizationConfigs, oxmEntityLookup);
736 newNode.setNodeId(searchTargetEntity.getId());
738 newNode.setNodeDepth(0);
739 newNode.setRootNode(true);
740 LOG.info(AaiUiMsgs.ROOT_NODE_DISCOVERED, queryParams.getSearchTargetNodeId());
741 setRootNodeFound(true);
743 newNode.setSelfLink(searchTargetEntity.getLink());
745 nodeCache.putIfAbsent(newNode.getNodeId(), newNode);
748 private int getTotalWorkOnHand() {
750 int numNodesWithPendingStates = 0;
752 if (isRootNodeFound()) {
753 evaluateNodeDepths();
756 for (ActiveInventoryNode n : nodeCache.values()) {
758 switch (n.getState()) {
762 // do nothing, these are our normal
770 * for all other states, there is work to be done
772 numNodesWithPendingStates++;
779 return (aaiWorkOnHand.get() + numNodesWithPendingStates);
784 * Checks for out standing work.
786 * @return true, if successful
788 private void processOutstandingWork(QueryParams queryParams) {
790 while (getTotalWorkOnHand() > 0) {
793 * Force an evaluation of node depths before determining if we should limit state-based
794 * traversal or processing.
797 processCurrentNodeStates(queryParams);
801 } catch (InterruptedException exc) {
802 LOG.error(AaiUiMsgs.PROCESSING_LOOP_INTERUPTED, exc.getMessage());
808 dumpThrottledWorkOnHandLog(true);
816 * org.onap.aai.sparky.viewandinspect.services.VisualizationContext#processSelfLinks(org.onap.aai.
817 * sparky.sync.entity.SearchableEntity, org.onap.aai.sparky.viewandinspect.entity.QueryParams)
820 public void processSelfLinks(SearchableEntity searchtargetEntity, QueryParams queryParams) {
825 if (searchtargetEntity == null) {
826 LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR,
827 contextIdStr + " - Failed to" + " processSelfLinks, searchtargetEntity is null");
831 long startTimeInMs = System.currentTimeMillis();
833 processSearchableEntity(searchtargetEntity, queryParams);
836 * This method is blocking until we decouple it with a CountDownLatch await condition, and
837 * make the internal graph processing more event-y.
840 processOutstandingWork(queryParams);
842 long totalResolveTime = (System.currentTimeMillis() - startTimeInMs);
844 long opTime = System.currentTimeMillis() - startTimeInMs;
846 LOG.info(AaiUiMsgs.ALL_TRANSACTIONS_RESOLVED, String.valueOf(totalResolveTime),
847 String.valueOf(totalLinksRetrieved.get()), String.valueOf(opTime));
849 } catch (Exception exc) {
850 LOG.error(AaiUiMsgs.VISUALIZATION_OUTPUT_ERROR, exc.getMessage());
856 * Verify outbound neighbors.
858 private void verifyOutboundNeighbors() {
860 for (ActiveInventoryNode srcNode : nodeCache.values()) {
862 for (String targetNodeId : srcNode.getOutboundNeighbors()) {
864 ActiveInventoryNode targetNode = nodeCache.get(targetNodeId);
866 if (targetNode != null && srcNode.getNodeId() != null) {
868 targetNode.addInboundNeighbor(srcNode.getNodeId());
870 if (this.visualizationConfigs.makeAllNeighborsBidirectional()) {
871 targetNode.addOutboundNeighbor(srcNode.getNodeId());
883 * Evaluate node depths.
885 private void evaluateNodeDepths() {
890 while (numChanged != 0) {
895 for (ActiveInventoryNode srcNode : nodeCache.values()) {
897 if (srcNode.getState() == NodeProcessingState.INIT) {
900 * this maybe the only state that we don't want to to process the node depth on, because
901 * typically it won't have any valid fields set, and it may remain in a partial state
902 * until we have processed the self-link.
909 for (String targetNodeId : srcNode.getOutboundNeighbors()) {
910 ActiveInventoryNode targetNode = nodeCache.get(targetNodeId);
912 if (targetNode != null) {
914 if (targetNode.changeDepth(srcNode.getNodeDepth() + 1)) {
920 for (String targetNodeId : srcNode.getInboundNeighbors()) {
921 ActiveInventoryNode targetNode = nodeCache.get(targetNodeId);
923 if (targetNode != null) {
925 if (targetNode.changeDepth(srcNode.getNodeDepth() + 1)) {
932 if (numAttempts >= MAX_DEPTH_EVALUATION_ATTEMPTS) {
933 LOG.info(AaiUiMsgs.MAX_EVALUATION_ATTEMPTS_EXCEEDED);
939 if (LOG.isDebugEnabled()) {
940 if (numAttempts > 0) {
941 LOG.debug(AaiUiMsgs.DEBUG_GENERIC,
942 "Evaluate node depths completed in " + numAttempts + " attempts");
944 LOG.debug(AaiUiMsgs.DEBUG_GENERIC,
945 "Evaluate node depths completed in 0 attempts because all nodes at correct depth");
953 * Gets the entity type primary key name.
955 * @param entityType the entity type
956 * @return the entity type primary key name
960 private String getEntityTypePrimaryKeyName(String entityType) {
962 if (entityType == null) {
963 LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE,
964 "node primary key" + " name because entity type is null");
968 OxmEntityDescriptor descriptor = oxmEntityLookup.getEntityDescriptors().get(entityType);
970 if (descriptor == null) {
971 LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE,
972 "oxm entity" + " descriptor for entityType = " + entityType);
976 List<String> pkeyNames = descriptor.getPrimaryKeyAttributeNames();
978 if (pkeyNames == null || pkeyNames.size() == 0) {
979 LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE,
980 "node primary" + " key because descriptor primary key names is empty");
984 return NodeUtils.concatArray(pkeyNames, "/");