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.services;
23 import static java.util.concurrent.CompletableFuture.supplyAsync;
25 import java.io.IOException;
26 import java.util.List;
28 import java.util.concurrent.ConcurrentHashMap;
29 import java.util.concurrent.ExecutorService;
30 import java.util.concurrent.atomic.AtomicInteger;
32 import org.onap.aai.cl.api.Logger;
33 import org.onap.aai.cl.eelf.LoggerFactory;
34 import org.onap.aai.restclient.client.OperationResult;
35 import org.onap.aai.sparky.config.oxm.OxmEntityDescriptor;
36 import org.onap.aai.sparky.config.oxm.OxmEntityLookup;
37 import org.onap.aai.sparky.dal.GizmoAdapter;
38 import org.onap.aai.sparky.logging.AaiUiMsgs;
39 import org.onap.aai.sparky.sync.entity.SearchableEntity;
40 import org.onap.aai.sparky.util.NodeUtils;
41 import org.onap.aai.sparky.viewandinspect.config.SparkyConstants;
42 import org.onap.aai.sparky.viewandinspect.config.VisualizationConfigs;
43 import org.onap.aai.sparky.viewandinspect.entity.ActiveInventoryNode;
44 import org.onap.aai.sparky.viewandinspect.entity.GizmoEntity;
45 import org.onap.aai.sparky.viewandinspect.entity.GizmoRelationshipEntity;
46 import org.onap.aai.sparky.viewandinspect.entity.GizmoRelationshipHint;
47 import org.onap.aai.sparky.viewandinspect.entity.InlineMessage;
48 import org.onap.aai.sparky.viewandinspect.entity.NodeProcessingTransaction;
49 import org.onap.aai.sparky.viewandinspect.entity.QueryParams;
50 import org.onap.aai.sparky.viewandinspect.enumeration.NodeProcessingAction;
51 import org.onap.aai.sparky.viewandinspect.enumeration.NodeProcessingState;
52 import org.onap.aai.sparky.viewandinspect.task.PerformGizmoNodeSelfLinkProcessingTask;
54 import com.fasterxml.jackson.annotation.JsonInclude.Include;
55 import com.fasterxml.jackson.databind.ObjectMapper;
56 import com.fasterxml.jackson.databind.PropertyNamingStrategy;
59 * The Class SelfLinkNodeCollector.
61 public class BaseGizmoVisualizationContext implements VisualizationContext {
63 private static final int MAX_DEPTH_EVALUATION_ATTEMPTS = 100;
65 private static final Logger LOG =
66 LoggerFactory.getInstance().getLogger(BaseGizmoVisualizationContext.class);
68 private final GizmoAdapter gizmoAdapter;
70 private AtomicInteger numLinksDiscovered;
71 private AtomicInteger numSuccessfulLinkResolveFromCache;
72 private AtomicInteger numSuccessfulLinkResolveFromFromServer;
73 private AtomicInteger numFailedLinkResolve;
74 private AtomicInteger aaiWorkOnHand;
76 private VisualizationConfigs visualizationConfigs;
78 private AtomicInteger totalLinksRetrieved;
80 private final long contextId;
81 private final String contextIdStr;
82 private long lastProcessStatesSummaryLogInMs = -1;
85 private ObjectMapper mapper;
86 private InlineMessage inlineMessage = null;
88 private ExecutorService graphExecutorService;
89 private OxmEntityLookup oxmEntityLookup;
90 private boolean rootNodeFound;
93 * The node cache is intended to be a flat structure indexed by a primary key to avoid needlessly
94 * re-requesting the same self-links over-and-over again, to speed up the overall render time and
95 * more importantly to reduce the network cost of determining information we already have.
97 private ConcurrentHashMap<String, ActiveInventoryNode> nodeCache;
100 * Instantiates a new self link node collector.
102 * @param loader the loader
103 * @throws Exception the exception
105 public BaseGizmoVisualizationContext(long contextId, GizmoAdapter gizmoAdapter,
106 ExecutorService graphExecutorService, VisualizationConfigs visualizationConfigs,
107 OxmEntityLookup oxmEntityLookup) throws Exception {
109 this.contextId = contextId;
110 this.contextIdStr = "[Context-Id=" + contextId + "]";
111 this.gizmoAdapter = gizmoAdapter;
112 this.graphExecutorService = graphExecutorService;
113 this.visualizationConfigs = visualizationConfigs;
114 this.oxmEntityLookup = oxmEntityLookup;
116 this.nodeCache = new ConcurrentHashMap<String, ActiveInventoryNode>();
117 this.numLinksDiscovered = new AtomicInteger(0);
118 this.totalLinksRetrieved = new AtomicInteger(0);
119 this.numSuccessfulLinkResolveFromCache = new AtomicInteger(0);
120 this.numSuccessfulLinkResolveFromFromServer = new AtomicInteger(0);
121 this.numFailedLinkResolve = new AtomicInteger(0);
122 this.aaiWorkOnHand = new AtomicInteger(0);
124 this.mapper = new ObjectMapper();
125 mapper.setSerializationInclusion(Include.NON_EMPTY);
126 mapper.setPropertyNamingStrategy(new PropertyNamingStrategy.KebabCaseStrategy());
127 this.rootNodeFound = false;
130 protected boolean isRootNodeFound() {
131 return rootNodeFound;
134 protected void setRootNodeFound(boolean rootNodeFound) {
135 this.rootNodeFound = rootNodeFound;
138 public long getContextId() {
142 public GizmoAdapter getGizmoAdapter() {
147 * Process self link response.
149 * @param nodeId the node id
151 private void processSelfLinkResponse(String nodeId) {
153 if (nodeId == null) {
154 LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR,
155 "Cannot process self link" + " response because nodeId is null");
159 ActiveInventoryNode ain = nodeCache.get(nodeId);
162 LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR,
163 "Cannot process self link response" + " because can't find node for id = " + nodeId);
167 GizmoEntity gizmoEntity = null;
170 gizmoEntity = mapper.readValue(ain.getOpResult().getResult(), GizmoEntity.class);
171 } catch (Exception exc) {
172 exc.printStackTrace();
173 LOG.error(AaiUiMsgs.SELF_LINK_JSON_PARSE_ERROR, "Failed to marshal json"
174 + " response str into JsonNode with error, " + exc.getLocalizedMessage());
175 ain.changeState(NodeProcessingState.ERROR,
176 NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_ERROR);
180 if (gizmoEntity == null) {
182 LOG.error(AaiUiMsgs.SELF_LINK_JSON_PARSE_ERROR,
183 "Failed to parse json node str." + " Parse resulted a null value.");
184 ain.changeState(NodeProcessingState.ERROR,
185 NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_ERROR);
190 * Now that we have the gizmo entity we can populate the AIN node with it, as well as the
194 ain.setEntityType(gizmoEntity.getType());
196 ain.setPrimaryKeyName(getEntityTypePrimaryKeyName(gizmoEntity.getType()));
198 OxmEntityDescriptor descriptor = oxmEntityLookup.getEntityDescriptors().get(gizmoEntity);
200 if (descriptor != null) {
201 ain.setPrimaryKeyValue(getPrimaryKeyValues(gizmoEntity.getProperties(),
202 descriptor.getPrimaryKeyAttributeNames()));
204 LOG.error(AaiUiMsgs.ERROR_GENERIC, "Could not determine oxm descriptor for entity type = " + gizmoEntity.getType());
207 gizmoEntity.getProperties().forEach((key, value) -> {
208 ain.getProperties().put(key, value);
211 // add edit attributes link
212 if (ain.getSelfLink() != null) {
213 ain.addProperty(SparkyConstants.URI_ATTR_NAME, ain.getSelfLink());
219 * Only discover neighbors if our depth is less than the Max-Traversal-Depth
222 if (ain.getNodeDepth() < this.visualizationConfigs.getMaxSelfLinkTraversalDepth()) {
225 * I think the next thing to do is:
227 * 1. Calculate the source / target node id 2. Add the nodeId to the incoming / outgoing links
228 * collection 3. Add the node to the node cache for processing
231 String resourceLink = null;
232 String relationshipNodeId = null;
233 ActiveInventoryNode relationshipNode = null;
235 for (GizmoRelationshipHint inRelationship : gizmoEntity.getIn()) {
237 if (inRelationship.getSource() != null) {
239 resourceLink = NodeUtils.extractRawGizmoPathWithoutVersion(inRelationship.getSource());
240 relationshipNodeId = NodeUtils.generateUniqueShaDigest(resourceLink);
242 if (!nodeCache.containsKey(relationshipNodeId)) {
244 relationshipNode = new ActiveInventoryNode(visualizationConfigs, oxmEntityLookup);
245 relationshipNode.setNodeId(relationshipNodeId);
246 relationshipNode.setSelfLink(resourceLink);
247 relationshipNode.changeState(NodeProcessingState.SELF_LINK_UNRESOLVED,
248 NodeProcessingAction.NEW_NODE_PROCESSED);
250 ain.addInboundNeighbor(relationshipNodeId);
252 addNode(relationshipNode);
259 for (GizmoRelationshipHint outRelationship : gizmoEntity.getOut()) {
261 if (outRelationship.getTarget() != null) {
263 resourceLink = NodeUtils.extractRawGizmoPathWithoutVersion(outRelationship.getTarget());
264 relationshipNodeId = NodeUtils.generateUniqueShaDigest(resourceLink);
266 if (!nodeCache.containsKey(relationshipNodeId)) {
268 relationshipNode = new ActiveInventoryNode(visualizationConfigs, oxmEntityLookup);
269 relationshipNode.setNodeId(relationshipNodeId);
270 relationshipNode.setSelfLink(resourceLink);
271 relationshipNode.changeState(NodeProcessingState.SELF_LINK_UNRESOLVED,
272 NodeProcessingAction.NEW_NODE_PROCESSED);
274 ain.addOutboundNeighbor(relationshipNodeId);
276 addNode(relationshipNode);
284 ain.changeState(NodeProcessingState.READY, NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_OK);
289 * Perform self link resolve.
291 * @param nodeId the node id
293 private void performSelfLinkResolve(String nodeId) {
295 if (nodeId == null) {
296 LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR,
297 "Resolve of self-link" + " has been skipped because provided nodeId is null");
301 ActiveInventoryNode ain = nodeCache.get(nodeId);
304 LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, "Failed to find node with id, " + nodeId
305 + ", from node cache. Resolve self-link method has been skipped.");
309 if (!ain.isSelfLinkPendingResolve()) {
311 ain.setSelfLinkPendingResolve(true);
313 // kick off async self-link resolution
315 if (LOG.isDebugEnabled()) {
316 LOG.debug(AaiUiMsgs.DEBUG_GENERIC,
317 "About to process node in SELF_LINK_UNPROCESSED State, link = " + ain.getSelfLink());
320 numLinksDiscovered.incrementAndGet();
323 * If the current node is the search target, we want to see everything the node has to offer
324 * from the self-link and not filter it to a single node.
327 NodeProcessingTransaction txn = new NodeProcessingTransaction();
328 txn.setProcessingNode(ain);
329 txn.setRequestParameters(null);
330 aaiWorkOnHand.incrementAndGet();
331 supplyAsync(new PerformGizmoNodeSelfLinkProcessingTask(txn, null, gizmoAdapter),
332 graphExecutorService).whenComplete((nodeTxn, error) -> {
337 * an error processing the self link should probably result in the node processing
338 * state shifting to ERROR
341 nodeTxn.getProcessingNode().setSelflinkRetrievalFailure(true);
343 nodeTxn.getProcessingNode().changeState(NodeProcessingState.ERROR,
344 NodeProcessingAction.SELF_LINK_RESOLVE_ERROR);
346 nodeTxn.getProcessingNode().setSelfLinkPendingResolve(false);
350 totalLinksRetrieved.incrementAndGet();
352 OperationResult opResult = nodeTxn.getOpResult();
354 if (opResult != null && opResult.wasSuccessful()) {
356 if (!opResult.wasSuccessful()) {
357 numFailedLinkResolve.incrementAndGet();
360 if (opResult.isFromCache()) {
361 numSuccessfulLinkResolveFromCache.incrementAndGet();
363 numSuccessfulLinkResolveFromFromServer.incrementAndGet();
367 nodeTxn.getProcessingNode().setOpResult(opResult);
368 nodeTxn.getProcessingNode().changeState(
369 NodeProcessingState.SELF_LINK_RESPONSE_UNPROCESSED,
370 NodeProcessingAction.SELF_LINK_RESOLVE_OK);
372 nodeTxn.getProcessingNode().setSelfLinkProcessed(true);
373 nodeTxn.getProcessingNode().setSelfLinkPendingResolve(false);
376 LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR,
377 "Self Link retrieval for link," + txn.getSelfLinkWithModifiers()
378 + ", failed with error code," + nodeTxn.getOpResult().getResultCode()
379 + ", and message," + nodeTxn.getOpResult().getResult());
381 nodeTxn.getProcessingNode().setSelflinkRetrievalFailure(true);
382 nodeTxn.getProcessingNode().setSelfLinkProcessed(true);
384 nodeTxn.getProcessingNode().changeState(NodeProcessingState.ERROR,
385 NodeProcessingAction.SELF_LINK_RESOLVE_ERROR);
387 nodeTxn.getProcessingNode().setSelfLinkPendingResolve(false);
392 aaiWorkOnHand.decrementAndGet();
400 public GizmoRelationshipEntity getGizmoRelationshipEntity(String gizmoJsonResponse) {
402 GizmoRelationshipEntity gizmoRelationship = null;
404 gizmoRelationship = mapper.readValue(gizmoJsonResponse, GizmoRelationshipEntity.class);
405 } catch (IOException exc) {
406 LOG.error(AaiUiMsgs.ERROR_GENERIC, "Failed to map json to GizmoRelationshipEntity. Error: " + exc.getMessage());
409 return gizmoRelationship;
413 public String getPrimaryKeyValues(Map<String, String> properties, List<String> pkeyNames) {
415 StringBuilder sb = new StringBuilder(64);
417 if (pkeyNames.size() > 0) {
418 String primaryKey = properties.get(pkeyNames.get(0));
419 if (primaryKey != null) {
420 sb.append(primaryKey);
422 // this should be a fatal error because unless we can
423 // successfully retrieve all the expected keys we'll end up
424 // with a garbage node
425 LOG.error(AaiUiMsgs.EXTRACTION_ERROR, "ERROR: Failed to extract" + " keyName, "
426 + pkeyNames.get(0) + ", from properties , " + properties);
430 for (int i = 1; i < pkeyNames.size(); i++) {
432 String kv = properties.get(pkeyNames.get(i));
434 sb.append("/").append(kv);
436 // this should be a fatal error because unless we can
437 // successfully retrieve all the expected keys we'll end up
438 // with a garbage node
439 LOG.error(AaiUiMsgs.EXTRACTION_ERROR, "ERROR: failed to extract keyName, "
440 + pkeyNames.get(i) + ", from properties, " + properties);
445 return sb.toString();
456 * Find and mark root node.
458 * @param queryParams the query params
459 * @return true, if successful
461 private void findAndMarkRootNode(QueryParams queryParams) {
463 if (isRootNodeFound()) {
467 for (ActiveInventoryNode cacheNode : nodeCache.values()) {
469 if (queryParams.getSearchTargetNodeId().equals(cacheNode.getNodeId())) {
470 cacheNode.setNodeDepth(0);
471 cacheNode.setRootNode(true);
472 LOG.info(AaiUiMsgs.ROOT_NODE_DISCOVERED, queryParams.getSearchTargetNodeId());
473 setRootNodeFound(true);
479 public void addNode(ActiveInventoryNode node) {
485 nodeCache.putIfAbsent(node.getNodeId(), node);
488 public VisualizationConfigs getVisualizationConfigs() {
489 return visualizationConfigs;
492 public void setVisualizationConfigs(VisualizationConfigs visualizationConfigs) {
493 this.visualizationConfigs = visualizationConfigs;
496 public OxmEntityLookup getOxmEntityLookup() {
497 return oxmEntityLookup;
500 public void setOxmEntityLookup(OxmEntityLookup oxmEntityLookup) {
501 this.oxmEntityLookup = oxmEntityLookup;
504 public ObjectMapper getMapper() {
508 public void setMapper(ObjectMapper mapper) {
509 this.mapper = mapper;
512 private void dumpThrottledWorkOnHandLog() {
513 dumpThrottledWorkOnHandLog(false);
516 private void dumpThrottledWorkOnHandLog(boolean override) {
518 if ((lastProcessStatesSummaryLogInMs < 0)
519 || ((System.currentTimeMillis() > (lastProcessStatesSummaryLogInMs + 5000))) || override) {
521 lastProcessStatesSummaryLogInMs = System.currentTimeMillis();
526 int numSelfLinkUnresolved = 0;
527 int numSelfLinkResponseUnprocessed = 0;
529 for (ActiveInventoryNode cacheNode : nodeCache.values()) {
531 switch (cacheNode.getState()) {
547 case SELF_LINK_UNRESOLVED: {
548 numSelfLinkUnresolved++;
552 case SELF_LINK_RESPONSE_UNPROCESSED: {
553 numSelfLinkResponseUnprocessed++;
563 LOG.info(AaiUiMsgs.INFO_GENERIC,
565 "ProcessCurrentStates for ContextId=%s, [PendingTxns=%d, numInit=%d, numSelfLinkUnresolved=%d, numSelfLinkResponseUnProcessed=%d, numReady=%d, numError=%d]",
566 contextIdStr, aaiWorkOnHand.get(), numInit, numSelfLinkUnresolved, numSelfLinkResponseUnprocessed,
567 numReady, numError));
573 * Process current node states.
575 * @param rootNodeDiscovered the root node discovered
577 private void processCurrentNodeStates(QueryParams queryParams) {
579 * Force an evaluation of node depths before determining if we should limit state-based
580 * traversal or processing.
583 findAndMarkRootNode(queryParams);
585 verifyOutboundNeighbors();
587 for (ActiveInventoryNode cacheNode : nodeCache.values()) {
589 if (LOG.isDebugEnabled()) {
590 LOG.debug(AaiUiMsgs.DEBUG_GENERIC, "processCurrentNodeState(), nid = "
591 + cacheNode.getNodeId() + " , nodeDepth = " + cacheNode.getNodeDepth());
594 switch (cacheNode.getState()) {
597 processInitialState(cacheNode.getNodeId());
606 case SELF_LINK_UNRESOLVED: {
607 performSelfLinkResolve(cacheNode.getNodeId());
611 case SELF_LINK_RESPONSE_UNPROCESSED: {
612 processSelfLinkResponse(cacheNode.getNodeId());
622 dumpThrottledWorkOnHandLog();
628 public int getNumSuccessfulLinkResolveFromCache() {
629 return numSuccessfulLinkResolveFromCache.get();
632 public int getNumSuccessfulLinkResolveFromFromServer() {
633 return numSuccessfulLinkResolveFromFromServer.get();
636 public int getNumFailedLinkResolve() {
637 return numFailedLinkResolve.get();
640 public InlineMessage getInlineMessage() {
641 return inlineMessage;
644 public void setInlineMessage(InlineMessage inlineMessage) {
645 this.inlineMessage = inlineMessage;
648 public ConcurrentHashMap<String, ActiveInventoryNode> getNodeCache() {
655 * Process initial state.
657 * @param nodeId the node id
659 private void processInitialState(String nodeId) {
661 if (nodeId == null) {
662 LOG.error(AaiUiMsgs.FAILED_TO_PROCESS_INITIAL_STATE, "Node id is null");
666 ActiveInventoryNode cachedNode = nodeCache.get(nodeId);
668 if (cachedNode == null) {
669 LOG.error(AaiUiMsgs.FAILED_TO_PROCESS_INITIAL_STATE,
670 "Node cannot be" + " found for nodeId, " + nodeId);
674 if (cachedNode.getSelfLink() == null) {
676 if (cachedNode.getNodeId() == null) {
679 * if the self link is null at the INIT state, which could be valid if this node is a
680 * complex attribute group which didn't originate from a self-link, but in that situation
681 * both the node id and node key should already be set.
684 cachedNode.changeState(NodeProcessingState.ERROR, NodeProcessingAction.NODE_IDENTITY_ERROR);
688 if (cachedNode.getNodeId() != null) {
691 * This should be the success path branch if the self-link is not set
694 cachedNode.changeState(NodeProcessingState.SELF_LINK_RESPONSE_UNPROCESSED,
695 NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_OK);
701 if (cachedNode.hasResolvedSelfLink()) {
702 LOG.error(AaiUiMsgs.INVALID_RESOLVE_STATE_DURING_INIT);
703 cachedNode.changeState(NodeProcessingState.ERROR,
704 NodeProcessingAction.UNEXPECTED_STATE_TRANSITION);
706 cachedNode.changeState(NodeProcessingState.SELF_LINK_UNRESOLVED,
707 NodeProcessingAction.SELF_LINK_SET);
713 * Process skeleton node.
715 * @param skeletonNode the skeleton node
716 * @param queryParams the query params
718 private void processSearchableEntity(SearchableEntity searchTargetEntity,
719 QueryParams queryParams) {
721 if (searchTargetEntity == null) {
725 if (searchTargetEntity.getId() == null) {
726 LOG.error(AaiUiMsgs.FAILED_TO_PROCESS_SKELETON_NODE, "Failed to process skeleton"
727 + " node because nodeId is null for node, " + searchTargetEntity.getLink());
731 ActiveInventoryNode newNode =
732 new ActiveInventoryNode(this.visualizationConfigs, oxmEntityLookup);
734 newNode.setNodeId(searchTargetEntity.getId());
736 newNode.setNodeDepth(0);
737 newNode.setRootNode(true);
738 LOG.info(AaiUiMsgs.ROOT_NODE_DISCOVERED, queryParams.getSearchTargetNodeId());
739 setRootNodeFound(true);
741 newNode.setSelfLink(searchTargetEntity.getLink());
743 nodeCache.putIfAbsent(newNode.getNodeId(), newNode);
746 private int getTotalWorkOnHand() {
748 int numNodesWithPendingStates = 0;
750 if (isRootNodeFound()) {
751 evaluateNodeDepths();
754 for (ActiveInventoryNode n : nodeCache.values()) {
756 switch (n.getState()) {
760 // do nothing, these are our normal
768 * for all other states, there is work to be done
770 numNodesWithPendingStates++;
777 return (aaiWorkOnHand.get() + numNodesWithPendingStates);
782 * Checks for out standing work.
784 * @return true, if successful
786 private void processOutstandingWork(QueryParams queryParams) {
788 while (getTotalWorkOnHand() > 0) {
791 * Force an evaluation of node depths before determining if we should limit state-based
792 * traversal or processing.
795 processCurrentNodeStates(queryParams);
799 } catch (InterruptedException exc) {
800 LOG.error(AaiUiMsgs.PROCESSING_LOOP_INTERUPTED, exc.getMessage());
806 dumpThrottledWorkOnHandLog(true);
814 * org.onap.aai.sparky.viewandinspect.services.VisualizationContext#processSelfLinks(org.onap.aai.
815 * sparky.sync.entity.SearchableEntity, org.onap.aai.sparky.viewandinspect.entity.QueryParams)
818 public void processSelfLinks(SearchableEntity searchtargetEntity, QueryParams queryParams) {
823 if (searchtargetEntity == null) {
824 LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR,
825 contextIdStr + " - Failed to" + " processSelfLinks, searchtargetEntity is null");
829 long startTimeInMs = System.currentTimeMillis();
831 processSearchableEntity(searchtargetEntity, queryParams);
834 * This method is blocking until we decouple it with a CountDownLatch await condition, and
835 * make the internal graph processing more event-y.
838 processOutstandingWork(queryParams);
840 long totalResolveTime = (System.currentTimeMillis() - startTimeInMs);
842 long opTime = System.currentTimeMillis() - startTimeInMs;
844 LOG.info(AaiUiMsgs.ALL_TRANSACTIONS_RESOLVED, String.valueOf(totalResolveTime),
845 String.valueOf(totalLinksRetrieved.get()), String.valueOf(opTime));
847 } catch (Exception exc) {
848 LOG.error(AaiUiMsgs.VISUALIZATION_OUTPUT_ERROR, exc.getMessage());
854 * Verify outbound neighbors.
856 private void verifyOutboundNeighbors() {
858 for (ActiveInventoryNode srcNode : nodeCache.values()) {
860 for (String targetNodeId : srcNode.getOutboundNeighbors()) {
862 ActiveInventoryNode targetNode = nodeCache.get(targetNodeId);
864 if (targetNode != null && srcNode.getNodeId() != null) {
866 targetNode.addInboundNeighbor(srcNode.getNodeId());
868 if (this.visualizationConfigs.makeAllNeighborsBidirectional()) {
869 targetNode.addOutboundNeighbor(srcNode.getNodeId());
881 * Evaluate node depths.
883 private void evaluateNodeDepths() {
888 while (numChanged != 0) {
893 for (ActiveInventoryNode srcNode : nodeCache.values()) {
895 if (srcNode.getState() == NodeProcessingState.INIT) {
898 * this maybe the only state that we don't want to to process the node depth on, because
899 * typically it won't have any valid fields set, and it may remain in a partial state
900 * until we have processed the self-link.
907 for (String targetNodeId : srcNode.getOutboundNeighbors()) {
908 ActiveInventoryNode targetNode = nodeCache.get(targetNodeId);
910 if (targetNode != null) {
912 if (targetNode.changeDepth(srcNode.getNodeDepth() + 1)) {
918 for (String targetNodeId : srcNode.getInboundNeighbors()) {
919 ActiveInventoryNode targetNode = nodeCache.get(targetNodeId);
921 if (targetNode != null) {
923 if (targetNode.changeDepth(srcNode.getNodeDepth() + 1)) {
930 if (numAttempts >= MAX_DEPTH_EVALUATION_ATTEMPTS) {
931 LOG.info(AaiUiMsgs.MAX_EVALUATION_ATTEMPTS_EXCEEDED);
937 if (LOG.isDebugEnabled()) {
938 if (numAttempts > 0) {
939 LOG.debug(AaiUiMsgs.DEBUG_GENERIC,
940 "Evaluate node depths completed in " + numAttempts + " attempts");
942 LOG.debug(AaiUiMsgs.DEBUG_GENERIC,
943 "Evaluate node depths completed in 0 attempts because all nodes at correct depth");
951 * Gets the entity type primary key name.
953 * @param entityType the entity type
954 * @return the entity type primary key name
958 private String getEntityTypePrimaryKeyName(String entityType) {
960 if (entityType == null) {
961 LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE,
962 "node primary key" + " name because entity type is null");
966 OxmEntityDescriptor descriptor = oxmEntityLookup.getEntityDescriptors().get(entityType);
968 if (descriptor == null) {
969 LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE,
970 "oxm entity" + " descriptor for entityType = " + entityType);
974 List<String> pkeyNames = descriptor.getPrimaryKeyAttributeNames();
976 if (pkeyNames == null || pkeyNames.size() == 0) {
977 LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE,
978 "node primary" + " key because descriptor primary key names is empty");
982 return NodeUtils.concatArray(pkeyNames, "/");