From b889beb825c82c38d96b74bed2d6c98cb4e0cb53 Mon Sep 17 00:00:00 2001 From: da490c Date: Sat, 25 Aug 2018 02:19:05 -0400 Subject: implemented sparky graph extensibility Add additional interface abstraction points to make it easier to extend and customize the graph logic, and made it easier to interwork with resources and gizmo via spring-bean wiring changes only. Issue-ID: AAI-1503 Change-Id: I5a3dcc81ecc49db89d71f0db27cffbb04c89fbd1 Signed-off-by: da490c --- .../config/application-gizmo.properties | 6 +- .../config/application.properties | 14 +- .../sparky-view-inspect-with-gizmo.xml | 27 +- .../sparky-view-inspect-with-resources.xml | 28 +- .../SchemaVisualizationProcessor.java | 6 +- .../viewandinspect/VisualizationContext.java | 44 + .../VisualizationContextBuilder.java | 9 + .../viewandinspect/VisualizationService.java | 48 + .../config/VisualizationConfigs.java | 45 +- .../context/BaseGizmoVisualizationContext.java | 976 ++++++++++++ .../BaseGizmoVisualizationContextBuilder.java | 71 + .../context/BaseVisualizationContext.java | 1607 +++++++++++++++++++ .../context/BaseVisualizationContextBuilder.java | 70 + .../entity/D3VisualizationOutput.java | 11 - .../sparky/viewandinspect/entity/GraphMeta.java | 56 +- .../viewandinspect/entity/InlineMessage.java | 66 - .../services/BaseGizmoVisualizationContext.java | 985 ------------ .../services/BaseVisualizationContext.java | 1617 -------------------- .../services/BaseVisualizationService.java | 103 +- .../services/VisualizationContext.java | 51 - .../services/VisualizationService.java | 48 - .../services/VisualizationTransformer.java | 15 +- .../BaseVisualizationContextTest.java | 292 +++- .../BaseVisualizationServiceTest.java | 53 +- .../SchemaVisualizationProcessorTest.java | 3 +- .../config/VisualizationConfigTest.java | 2 - .../entity/D3VisualizationOutputTest.java | 9 +- .../viewandinspect/entity/GraphMetaTest.java | 7 - .../viewandinspect/entity/InlineMessageTest.java | 55 - .../BaseGizmoVisualizationContextTest.java | 1 + .../services/BaseVisualizationContextTest.java | 1 + 31 files changed, 3274 insertions(+), 3052 deletions(-) create mode 100644 sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/VisualizationContext.java create mode 100644 sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/VisualizationContextBuilder.java create mode 100644 sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/VisualizationService.java create mode 100644 sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/context/BaseGizmoVisualizationContext.java create mode 100644 sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/context/BaseGizmoVisualizationContextBuilder.java create mode 100644 sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/context/BaseVisualizationContext.java create mode 100644 sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/context/BaseVisualizationContextBuilder.java delete mode 100644 sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/entity/InlineMessage.java delete mode 100644 sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/services/BaseGizmoVisualizationContext.java delete mode 100644 sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/services/BaseVisualizationContext.java delete mode 100644 sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/services/VisualizationContext.java delete mode 100644 sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/services/VisualizationService.java delete mode 100644 sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/entity/InlineMessageTest.java diff --git a/sparkybe-onap-application/config/application-gizmo.properties b/sparkybe-onap-application/config/application-gizmo.properties index 69750c7..cd01a73 100644 --- a/sparkybe-onap-application/config/application-gizmo.properties +++ b/sparkybe-onap-application/config/application-gizmo.properties @@ -1,8 +1,8 @@ gizmo.hostname=127.0.0.1 -gizmo.port=9520 +gizmo.port=30266 gizmo.authType=SSL_CERT gizmo.client-cert=client-cert-onap.p12 gizmo.client-cert-password=1y0q1uvc1uum1uvg1pil1pjl1uuq1uvk1uuu1y10 gizmo.trust-store=tomcat_keystore -gizmo.apiVersion=v12 -gizmo.maxSelfLinkTraversalDepth=2 +gizmo.apiVersion=v13 +gizmo.maxSelfLinkTraversalDepth=10 diff --git a/sparkybe-onap-application/config/application.properties b/sparkybe-onap-application/config/application.properties index 4c6b1c0..8dbb190 100644 --- a/sparkybe-onap-application/config/application.properties +++ b/sparkybe-onap-application/config/application.properties @@ -5,10 +5,20 @@ spring.mvc.favicon.enabled=false #possible values: camel,http,ssl,portal,fe-dev,fe-prod,oxm-default,oxm-override,[resources|gizmo],sync,oxm-schema-dev,oxm-schema-prod #For oxm loading there needs to be a combo of [oxm-default OR oxm-override] AND [oxm-schema-dev OR oxm-schema-prod] -spring.profiles.active=camel,ssl,fe-dev,oxm-schema-dev,resources,sync,oxm-override + +# +# Gizmo profile +# +spring.profiles.active=camel,http,fe-dev,oxm-schema-dev,gizmo,oxm-override + +# +# Resources profile +# +#spring.profiles.active=camel,http,fe-dev,oxm-schema-dev,resources,sync,oxm-override + searchservice.hostname=127.0.0.1 -searchservice.port=9502 +searchservice.port=30262 searchservice.client-cert=client-cert-onap.p12 searchservice.client-cert-password=1y0q1uvc1uum1uvg1pil1pjl1uuq1uvk1uuu1y10 searchservice.truststore=tomcat_keystore diff --git a/sparkybe-onap-application/config/spring-beans/sparky-view-inspect-with-gizmo.xml b/sparkybe-onap-application/config/spring-beans/sparky-view-inspect-with-gizmo.xml index 9afd8a3..17cfc38 100644 --- a/sparkybe-onap-application/config/spring-beans/sparky-view-inspect-with-gizmo.xml +++ b/sparkybe-onap-application/config/spring-beans/sparky-view-inspect-with-gizmo.xml @@ -14,7 +14,6 @@ - @@ -39,20 +38,24 @@ - + + + + + + + + - - - - - - - - - - + + + + + + - @@ -41,21 +40,22 @@ + + + + + + + - - - - - - - - - - - - + + + + + + getNodeCache(); + + public long getContextId(); + +} diff --git a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/VisualizationContextBuilder.java b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/VisualizationContextBuilder.java new file mode 100644 index 0000000..6286fde --- /dev/null +++ b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/VisualizationContextBuilder.java @@ -0,0 +1,9 @@ +package org.onap.aai.sparky.viewandinspect; + +public interface VisualizationContextBuilder { + + public VisualizationContext getVisualizationContext() throws Exception; + + public void shutdown(); + +} diff --git a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/VisualizationService.java b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/VisualizationService.java new file mode 100644 index 0000000..7a090b0 --- /dev/null +++ b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/VisualizationService.java @@ -0,0 +1,48 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 Amdocs + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.sparky.viewandinspect; + + +import org.onap.aai.restclient.client.OperationResult; +import org.onap.aai.sparky.viewandinspect.entity.QueryRequest; + +public interface VisualizationService { + + /** + * Analyze query request body. + * + * @param queryRequestJson the query request json + * @return the query request + */ + + public QueryRequest analyzeQueryRequestBody(String queryRequestJson); + + /** + * Builds the visualization using generic query. + * + * @param queryRequest the query request + * @return the operation result + */ + public OperationResult buildVisualization(QueryRequest queryRequest); + + public void shutdown(); + +} diff --git a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/config/VisualizationConfigs.java b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/config/VisualizationConfigs.java index b0834ca..acca775 100644 --- a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/config/VisualizationConfigs.java +++ b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/config/VisualizationConfigs.java @@ -47,16 +47,12 @@ public class VisualizationConfigs { private ArrayList shallowEntities; - private boolean gizmoEnabled; - private SparkyResourceLoader resourceLoader; /** * Instantiates a new visualization config. */ - public VisualizationConfigs() { - this.gizmoEnabled = false; - } + public VisualizationConfigs() {} public ArrayList getShallowEntities() { return shallowEntities; @@ -135,14 +131,6 @@ public class VisualizationConfigs { this.numOfThreadsToFetchNodeIntegrity = numOfThreadsToFetchNodeIntegrity; } - public boolean isGizmoEnabled() { - return gizmoEnabled; - } - - public void setGizmoEnabled(boolean gizmoEnabled) { - this.gizmoEnabled = gizmoEnabled; - } - public SparkyResourceLoader getResourceLoader() { return resourceLoader; } @@ -152,19 +140,22 @@ public class VisualizationConfigs { } @Override - public String toString() { - return "VisualizationConfigs [maxSelfLinkTraversalDepth=" + maxSelfLinkTraversalDepth - + ", visualizationDebugEnabled=" + visualizationDebugEnabled + ", " - + (aaiEntityNodeDescriptors != null ? "aaiEntityNodeDescriptors=" + aaiEntityNodeDescriptors + ", " - : "") - + (generalNodeClassName != null ? "generalNodeClassName=" + generalNodeClassName + ", " : "") - + (searchNodeClassName != null ? "searchNodeClassName=" + searchNodeClassName + ", " : "") - + (selectedSearchedNodeClassName != null - ? "selectedSearchedNodeClassName=" + selectedSearchedNodeClassName + ", " : "") - + "numOfThreadsToFetchNodeIntegrity=" + numOfThreadsToFetchNodeIntegrity - + ", makeAllNeighborsBidirectional=" + makeAllNeighborsBidirectional + ", " - + (shallowEntities != null ? "shallowEntities=" + shallowEntities + ", " : "") + "gizmoEnabled=" - + gizmoEnabled + "]"; - } + public String toString() { + return "VisualizationConfigs [maxSelfLinkTraversalDepth=" + maxSelfLinkTraversalDepth + + ", visualizationDebugEnabled=" + visualizationDebugEnabled + ", " + + (aaiEntityNodeDescriptors != null + ? "aaiEntityNodeDescriptors=" + aaiEntityNodeDescriptors + ", " : "") + + (generalNodeClassName != null ? "generalNodeClassName=" + generalNodeClassName + ", " + : "") + + (searchNodeClassName != null ? "searchNodeClassName=" + searchNodeClassName + ", " : "") + + (selectedSearchedNodeClassName != null + ? "selectedSearchedNodeClassName=" + selectedSearchedNodeClassName + ", " : "") + + "numOfThreadsToFetchNodeIntegrity=" + numOfThreadsToFetchNodeIntegrity + + ", makeAllNeighborsBidirectional=" + makeAllNeighborsBidirectional + ", " + + (shallowEntities != null ? "shallowEntities=" + shallowEntities + ", " : "") + + (resourceLoader != null ? "resourceLoader=" + resourceLoader : "") + "]"; + } + + } diff --git a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/context/BaseGizmoVisualizationContext.java b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/context/BaseGizmoVisualizationContext.java new file mode 100644 index 0000000..f058c48 --- /dev/null +++ b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/context/BaseGizmoVisualizationContext.java @@ -0,0 +1,976 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 Amdocs + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.sparky.viewandinspect.context; + +import static java.util.concurrent.CompletableFuture.supplyAsync; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.AtomicInteger; + +import org.onap.aai.cl.api.Logger; +import org.onap.aai.cl.eelf.LoggerFactory; +import org.onap.aai.restclient.client.OperationResult; +import org.onap.aai.sparky.config.oxm.OxmEntityDescriptor; +import org.onap.aai.sparky.config.oxm.OxmEntityLookup; +import org.onap.aai.sparky.dal.GizmoAdapter; +import org.onap.aai.sparky.logging.AaiUiMsgs; +import org.onap.aai.sparky.sync.entity.SearchableEntity; +import org.onap.aai.sparky.util.NodeUtils; +import org.onap.aai.sparky.viewandinspect.VisualizationContext; +import org.onap.aai.sparky.viewandinspect.config.SparkyConstants; +import org.onap.aai.sparky.viewandinspect.config.VisualizationConfigs; +import org.onap.aai.sparky.viewandinspect.entity.ActiveInventoryNode; +import org.onap.aai.sparky.viewandinspect.entity.GizmoEntity; +import org.onap.aai.sparky.viewandinspect.entity.GizmoRelationshipEntity; +import org.onap.aai.sparky.viewandinspect.entity.GizmoRelationshipHint; +import org.onap.aai.sparky.viewandinspect.entity.NodeProcessingTransaction; +import org.onap.aai.sparky.viewandinspect.entity.QueryParams; +import org.onap.aai.sparky.viewandinspect.enumeration.NodeProcessingAction; +import org.onap.aai.sparky.viewandinspect.enumeration.NodeProcessingState; +import org.onap.aai.sparky.viewandinspect.task.PerformGizmoNodeSelfLinkProcessingTask; + +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.PropertyNamingStrategy; + +/** + * The Class SelfLinkNodeCollector. + */ +public class BaseGizmoVisualizationContext implements VisualizationContext { + + private static final int MAX_DEPTH_EVALUATION_ATTEMPTS = 100; + + private static final Logger LOG = + LoggerFactory.getInstance().getLogger(BaseGizmoVisualizationContext.class); + + protected final GizmoAdapter gizmoAdapter; + + protected AtomicInteger numLinksDiscovered; + protected AtomicInteger numSuccessfulLinkResolveFromCache; + protected AtomicInteger numSuccessfulLinkResolveFromFromServer; + protected AtomicInteger numFailedLinkResolve; + protected AtomicInteger aaiWorkOnHand; + + protected VisualizationConfigs visualizationConfigs; + + protected AtomicInteger totalLinksRetrieved; + + protected final long contextId; + protected final String contextIdStr; + protected long lastProcessStatesSummaryLogInMs = -1; + + + protected ObjectMapper mapper; + + protected ExecutorService graphExecutorService; + protected OxmEntityLookup oxmEntityLookup; + protected boolean rootNodeFound; + + /* + * The node cache is intended to be a flat structure indexed by a primary key to avoid needlessly + * re-requesting the same self-links over-and-over again, to speed up the overall render time and + * more importantly to reduce the network cost of determining information we already have. + */ + protected ConcurrentHashMap nodeCache; + + /** + * Instantiates a new self link node collector. + * + * @param loader the loader + * @throws Exception the exception + */ + public BaseGizmoVisualizationContext(long contextId, GizmoAdapter gizmoAdapter, + ExecutorService graphExecutorService, VisualizationConfigs visualizationConfigs, + OxmEntityLookup oxmEntityLookup) throws Exception { + + this.contextId = contextId; + this.contextIdStr = "[Context-Id=" + contextId + "]"; + this.gizmoAdapter = gizmoAdapter; + this.graphExecutorService = graphExecutorService; + this.visualizationConfigs = visualizationConfigs; + this.oxmEntityLookup = oxmEntityLookup; + + this.nodeCache = new ConcurrentHashMap(); + this.numLinksDiscovered = new AtomicInteger(0); + this.totalLinksRetrieved = new AtomicInteger(0); + this.numSuccessfulLinkResolveFromCache = new AtomicInteger(0); + this.numSuccessfulLinkResolveFromFromServer = new AtomicInteger(0); + this.numFailedLinkResolve = new AtomicInteger(0); + this.aaiWorkOnHand = new AtomicInteger(0); + + this.mapper = new ObjectMapper(); + mapper.setSerializationInclusion(Include.NON_EMPTY); + mapper.setPropertyNamingStrategy(new PropertyNamingStrategy.KebabCaseStrategy()); + this.rootNodeFound = false; + } + + protected boolean isRootNodeFound() { + return rootNodeFound; + } + + protected void setRootNodeFound(boolean rootNodeFound) { + this.rootNodeFound = rootNodeFound; + } + + public long getContextId() { + return contextId; + } + + public GizmoAdapter getGizmoAdapter() { + return gizmoAdapter; + } + + /** + * Process self link response. + * + * @param nodeId the node id + */ + protected void processSelfLinkResponse(String nodeId) { + + if (nodeId == null) { + LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, + "Cannot process self link" + " response because nodeId is null"); + return; + } + + ActiveInventoryNode ain = nodeCache.get(nodeId); + + if (ain == null) { + LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, + "Cannot process self link response" + " because can't find node for id = " + nodeId); + return; + } + + GizmoEntity gizmoEntity = null; + + try { + gizmoEntity = mapper.readValue(ain.getOpResult().getResult(), GizmoEntity.class); + } catch (Exception exc) { + exc.printStackTrace(); + LOG.error(AaiUiMsgs.SELF_LINK_JSON_PARSE_ERROR, "Failed to marshal json" + + " response str into JsonNode with error, " + exc.getLocalizedMessage()); + ain.changeState(NodeProcessingState.ERROR, + NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_ERROR); + return; + } + + if (gizmoEntity == null) { + + LOG.error(AaiUiMsgs.SELF_LINK_JSON_PARSE_ERROR, + "Failed to parse json node str." + " Parse resulted a null value."); + ain.changeState(NodeProcessingState.ERROR, + NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_ERROR); + return; + } + + /* + * Now that we have the gizmo entity we can populate the AIN node with it, as well as the + * relationships + */ + + ain.setEntityType(gizmoEntity.getType()); + + ain.setPrimaryKeyName(getEntityTypePrimaryKeyName(gizmoEntity.getType())); + + OxmEntityDescriptor descriptor = oxmEntityLookup.getEntityDescriptors().get(gizmoEntity.getType()); + + if (descriptor != null) { + ain.setPrimaryKeyValue(getPrimaryKeyValues(gizmoEntity.getProperties(), + descriptor.getPrimaryKeyAttributeNames())); + } else { + LOG.error(AaiUiMsgs.ERROR_GENERIC, "Could not determine oxm descriptor for entity type = " + gizmoEntity.getType()); + } + + gizmoEntity.getProperties().forEach((key, value) -> { + ain.getProperties().put(key, value); + }); + + // add edit attributes link + if (ain.getSelfLink() != null) { + ain.addProperty(SparkyConstants.URI_ATTR_NAME, ain.getSelfLink()); + } + + /* + * Only discover neighbors if our depth is less than the Max-Traversal-Depth + */ + + if (ain.getNodeDepth() < this.visualizationConfigs.getMaxSelfLinkTraversalDepth()) { + + /* + * I think the next thing to do is: + * + * 1. Calculate the source / target node id 2. Add the nodeId to the incoming / outgoing links + * collection 3. Add the node to the node cache for processing + */ + + String resourceLink = null; + String relationshipNodeId = null; + ActiveInventoryNode relationshipNode = null; + + for (GizmoRelationshipHint inRelationship : gizmoEntity.getIn()) { + + if (inRelationship.getSource() != null) { + + resourceLink = NodeUtils.extractRawGizmoPathWithoutVersion(inRelationship.getSource()); + relationshipNodeId = NodeUtils.generateUniqueShaDigest(resourceLink); + + if (!nodeCache.containsKey(relationshipNodeId)) { + + relationshipNode = new ActiveInventoryNode(visualizationConfigs, oxmEntityLookup); + relationshipNode.setNodeId(relationshipNodeId); + relationshipNode.setSelfLink(resourceLink); + relationshipNode.changeState(NodeProcessingState.SELF_LINK_UNRESOLVED, + NodeProcessingAction.NEW_NODE_PROCESSED); + + ain.addInboundNeighbor(relationshipNodeId); + + addNode(relationshipNode); + + } + } + + } + + for (GizmoRelationshipHint outRelationship : gizmoEntity.getOut()) { + + if (outRelationship.getTarget() != null) { + + resourceLink = NodeUtils.extractRawGizmoPathWithoutVersion(outRelationship.getTarget()); + relationshipNodeId = NodeUtils.generateUniqueShaDigest(resourceLink); + + if (!nodeCache.containsKey(relationshipNodeId)) { + + relationshipNode = new ActiveInventoryNode(visualizationConfigs, oxmEntityLookup); + relationshipNode.setNodeId(relationshipNodeId); + relationshipNode.setSelfLink(resourceLink); + relationshipNode.changeState(NodeProcessingState.SELF_LINK_UNRESOLVED, + NodeProcessingAction.NEW_NODE_PROCESSED); + + ain.addOutboundNeighbor(relationshipNodeId); + + addNode(relationshipNode); + + } + } + + } + } + + ain.changeState(NodeProcessingState.READY, NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_OK); + + } + + /** + * Perform self link resolve. + * + * @param nodeId the node id + */ + protected void performSelfLinkResolve(String nodeId) { + + if (nodeId == null) { + LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, + "Resolve of self-link" + " has been skipped because provided nodeId is null"); + return; + } + + ActiveInventoryNode ain = nodeCache.get(nodeId); + + if (ain == null) { + LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, "Failed to find node with id, " + nodeId + + ", from node cache. Resolve self-link method has been skipped."); + return; + } + + if (!ain.isSelfLinkPendingResolve()) { + + ain.setSelfLinkPendingResolve(true); + + // kick off async self-link resolution + + if (LOG.isDebugEnabled()) { + LOG.debug(AaiUiMsgs.DEBUG_GENERIC, + "About to process node in SELF_LINK_UNPROCESSED State, link = " + ain.getSelfLink()); + } + + numLinksDiscovered.incrementAndGet(); + + /* + * If the current node is the search target, we want to see everything the node has to offer + * from the self-link and not filter it to a single node. + */ + + NodeProcessingTransaction txn = new NodeProcessingTransaction(); + txn.setProcessingNode(ain); + txn.setRequestParameters(null); + aaiWorkOnHand.incrementAndGet(); + supplyAsync(new PerformGizmoNodeSelfLinkProcessingTask(txn, null, gizmoAdapter), + graphExecutorService).whenComplete((nodeTxn, error) -> { + + if (error != null) { + + /* + * an error processing the self link should probably result in the node processing + * state shifting to ERROR + */ + + nodeTxn.getProcessingNode().setSelflinkRetrievalFailure(true); + + nodeTxn.getProcessingNode().changeState(NodeProcessingState.ERROR, + NodeProcessingAction.SELF_LINK_RESOLVE_ERROR); + + nodeTxn.getProcessingNode().setSelfLinkPendingResolve(false); + + } else { + + totalLinksRetrieved.incrementAndGet(); + + OperationResult opResult = nodeTxn.getOpResult(); + + if (opResult != null && opResult.wasSuccessful()) { + + if (!opResult.wasSuccessful()) { + numFailedLinkResolve.incrementAndGet(); + } + + if (opResult.isFromCache()) { + numSuccessfulLinkResolveFromCache.incrementAndGet(); + } else { + numSuccessfulLinkResolveFromFromServer.incrementAndGet(); + } + + // success path + nodeTxn.getProcessingNode().setOpResult(opResult); + nodeTxn.getProcessingNode().changeState( + NodeProcessingState.SELF_LINK_RESPONSE_UNPROCESSED, + NodeProcessingAction.SELF_LINK_RESOLVE_OK); + + nodeTxn.getProcessingNode().setSelfLinkProcessed(true); + nodeTxn.getProcessingNode().setSelfLinkPendingResolve(false); + + } else { + LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, + "Self Link retrieval for link," + txn.getSelfLinkWithModifiers() + + ", failed with error code," + nodeTxn.getOpResult().getResultCode() + + ", and message," + nodeTxn.getOpResult().getResult()); + + nodeTxn.getProcessingNode().setSelflinkRetrievalFailure(true); + nodeTxn.getProcessingNode().setSelfLinkProcessed(true); + + nodeTxn.getProcessingNode().changeState(NodeProcessingState.ERROR, + NodeProcessingAction.SELF_LINK_RESOLVE_ERROR); + + nodeTxn.getProcessingNode().setSelfLinkPendingResolve(false); + + } + } + + aaiWorkOnHand.decrementAndGet(); + + }); + + } + + } + + public GizmoRelationshipEntity getGizmoRelationshipEntity(String gizmoJsonResponse) { + + GizmoRelationshipEntity gizmoRelationship = null; + try { + gizmoRelationship = mapper.readValue(gizmoJsonResponse, GizmoRelationshipEntity.class); + } catch (IOException exc) { + LOG.error(AaiUiMsgs.ERROR_GENERIC, "Failed to map json to GizmoRelationshipEntity. Error: " + exc.getMessage()); + } + + return gizmoRelationship; + + } + + public String getPrimaryKeyValues(Map properties, List pkeyNames) { + + StringBuilder sb = new StringBuilder(64); + + if (pkeyNames.size() > 0) { + String primaryKey = properties.get(pkeyNames.get(0)); + if (primaryKey != null) { + sb.append(primaryKey); + } else { + // this should be a fatal error because unless we can + // successfully retrieve all the expected keys we'll end up + // with a garbage node + LOG.error(AaiUiMsgs.EXTRACTION_ERROR, "ERROR: Failed to extract" + " keyName, " + + pkeyNames.get(0) + ", from properties , " + properties); + return null; + } + + for (int i = 1; i < pkeyNames.size(); i++) { + + String kv = properties.get(pkeyNames.get(i)); + if (kv != null) { + sb.append("/").append(kv); + } else { + // this should be a fatal error because unless we can + // successfully retrieve all the expected keys we'll end up + // with a garbage node + LOG.error(AaiUiMsgs.EXTRACTION_ERROR, "ERROR: failed to extract keyName, " + + pkeyNames.get(i) + ", from properties, " + properties); + return null; + } + } + + return sb.toString(); + + } + + return null; + + } + + + + /** + * Find and mark root node. + * + * @param queryParams the query params + * @return true, if successful + */ + protected void findAndMarkRootNode(QueryParams queryParams) { + + if (isRootNodeFound()) { + return; + } + + for (ActiveInventoryNode cacheNode : nodeCache.values()) { + + if (queryParams.getSearchTargetNodeId().equals(cacheNode.getNodeId())) { + cacheNode.setNodeDepth(0); + cacheNode.setRootNode(true); + LOG.info(AaiUiMsgs.ROOT_NODE_DISCOVERED, queryParams.getSearchTargetNodeId()); + setRootNodeFound(true); + } + } + + } + + public void addNode(ActiveInventoryNode node) { + + if (node == null) { + return; + } + + nodeCache.putIfAbsent(node.getNodeId(), node); + } + + public VisualizationConfigs getVisualizationConfigs() { + return visualizationConfigs; + } + + public void setVisualizationConfigs(VisualizationConfigs visualizationConfigs) { + this.visualizationConfigs = visualizationConfigs; + } + + public OxmEntityLookup getOxmEntityLookup() { + return oxmEntityLookup; + } + + public void setOxmEntityLookup(OxmEntityLookup oxmEntityLookup) { + this.oxmEntityLookup = oxmEntityLookup; + } + + public ObjectMapper getMapper() { + return mapper; + } + + public void setMapper(ObjectMapper mapper) { + this.mapper = mapper; + } + + private void dumpThrottledWorkOnHandLog() { + dumpThrottledWorkOnHandLog(false); + } + + protected void dumpThrottledWorkOnHandLog(boolean override) { + + if ((lastProcessStatesSummaryLogInMs < 0) + || ((System.currentTimeMillis() > (lastProcessStatesSummaryLogInMs + 5000))) || override) { + + lastProcessStatesSummaryLogInMs = System.currentTimeMillis(); + + int numInit = 0; + int numReady = 0; + int numError = 0; + int numSelfLinkUnresolved = 0; + int numSelfLinkResponseUnprocessed = 0; + + for (ActiveInventoryNode cacheNode : nodeCache.values()) { + + switch (cacheNode.getState()) { + + case INIT: { + numInit++; + break; + } + + case READY: { + numReady++; + break; + } + case ERROR: { + numError++; + break; + } + + case SELF_LINK_UNRESOLVED: { + numSelfLinkUnresolved++; + break; + } + + case SELF_LINK_RESPONSE_UNPROCESSED: { + numSelfLinkResponseUnprocessed++; + break; + } + + default: + break; + } + + } + + LOG.info(AaiUiMsgs.INFO_GENERIC, + String.format( + "ProcessCurrentStates for ContextId=%s, [PendingTxns=%d, numInit=%d, numSelfLinkUnresolved=%d, numSelfLinkResponseUnProcessed=%d, numReady=%d, numError=%d]", + contextIdStr, aaiWorkOnHand.get(), numInit, numSelfLinkUnresolved, numSelfLinkResponseUnprocessed, + numReady, numError)); + } + + } + + /** + * Process current node states. + * + * @param rootNodeDiscovered the root node discovered + */ + protected void processCurrentNodeStates(QueryParams queryParams) { + /* + * Force an evaluation of node depths before determining if we should limit state-based + * traversal or processing. + */ + + findAndMarkRootNode(queryParams); + + verifyOutboundNeighbors(); + + for (ActiveInventoryNode cacheNode : nodeCache.values()) { + + if (LOG.isDebugEnabled()) { + LOG.debug(AaiUiMsgs.DEBUG_GENERIC, "processCurrentNodeState(), nid = " + + cacheNode.getNodeId() + " , nodeDepth = " + cacheNode.getNodeDepth()); + } + + switch (cacheNode.getState()) { + + case INIT: { + processInitialState(cacheNode.getNodeId()); + break; + } + + case READY: + case ERROR: { + break; + } + + case SELF_LINK_UNRESOLVED: { + performSelfLinkResolve(cacheNode.getNodeId()); + break; + } + + case SELF_LINK_RESPONSE_UNPROCESSED: { + processSelfLinkResponse(cacheNode.getNodeId()); + break; + } + + default: + break; + } + + } + + dumpThrottledWorkOnHandLog(); + + } + + + + public int getNumSuccessfulLinkResolveFromCache() { + return numSuccessfulLinkResolveFromCache.get(); + } + + public int getNumSuccessfulLinkResolveFromFromServer() { + return numSuccessfulLinkResolveFromFromServer.get(); + } + + public int getNumFailedLinkResolve() { + return numFailedLinkResolve.get(); + } + + public ConcurrentHashMap getNodeCache() { + return nodeCache; + } + + + + /** + * Process initial state. + * + * @param nodeId the node id + */ + protected void processInitialState(String nodeId) { + + if (nodeId == null) { + LOG.error(AaiUiMsgs.FAILED_TO_PROCESS_INITIAL_STATE, "Node id is null"); + return; + } + + ActiveInventoryNode cachedNode = nodeCache.get(nodeId); + + if (cachedNode == null) { + LOG.error(AaiUiMsgs.FAILED_TO_PROCESS_INITIAL_STATE, + "Node cannot be" + " found for nodeId, " + nodeId); + return; + } + + if (cachedNode.getSelfLink() == null) { + + if (cachedNode.getNodeId() == null) { + + /* + * if the self link is null at the INIT state, which could be valid if this node is a + * complex attribute group which didn't originate from a self-link, but in that situation + * both the node id and node key should already be set. + */ + + cachedNode.changeState(NodeProcessingState.ERROR, NodeProcessingAction.NODE_IDENTITY_ERROR); + + } + + if (cachedNode.getNodeId() != null) { + + /* + * This should be the success path branch if the self-link is not set + */ + + cachedNode.changeState(NodeProcessingState.SELF_LINK_RESPONSE_UNPROCESSED, + NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_OK); + + } + + } else { + + if (cachedNode.hasResolvedSelfLink()) { + LOG.error(AaiUiMsgs.INVALID_RESOLVE_STATE_DURING_INIT); + cachedNode.changeState(NodeProcessingState.ERROR, + NodeProcessingAction.UNEXPECTED_STATE_TRANSITION); + } else { + cachedNode.changeState(NodeProcessingState.SELF_LINK_UNRESOLVED, + NodeProcessingAction.SELF_LINK_SET); + } + } + } + + /** + * Process skeleton node. + * + * @param skeletonNode the skeleton node + * @param queryParams the query params + */ + protected void processSearchableEntity(SearchableEntity searchTargetEntity, + QueryParams queryParams) { + + if (searchTargetEntity == null) { + return; + } + + if (searchTargetEntity.getId() == null) { + LOG.error(AaiUiMsgs.FAILED_TO_PROCESS_SKELETON_NODE, "Failed to process skeleton" + + " node because nodeId is null for node, " + searchTargetEntity.getLink()); + return; + } + + ActiveInventoryNode newNode = + new ActiveInventoryNode(this.visualizationConfigs, oxmEntityLookup); + + newNode.setNodeId(searchTargetEntity.getId()); + + newNode.setNodeDepth(0); + newNode.setRootNode(true); + LOG.info(AaiUiMsgs.ROOT_NODE_DISCOVERED, queryParams.getSearchTargetNodeId()); + setRootNodeFound(true); + + newNode.setSelfLink(searchTargetEntity.getLink()); + + nodeCache.putIfAbsent(newNode.getNodeId(), newNode); + } + + protected int getTotalWorkOnHand() { + + int numNodesWithPendingStates = 0; + + if (isRootNodeFound()) { + evaluateNodeDepths(); + } + + for (ActiveInventoryNode n : nodeCache.values()) { + + switch (n.getState()) { + + case READY: + case ERROR: { + // do nothing, these are our normal + // exit states + break; + } + + default: { + + /* + * for all other states, there is work to be done + */ + numNodesWithPendingStates++; + } + + } + + } + + return (aaiWorkOnHand.get() + numNodesWithPendingStates); + + } + + /** + * Checks for out standing work. + * + * @return true, if successful + */ + protected void processOutstandingWork(QueryParams queryParams) { + + while (getTotalWorkOnHand() > 0) { + + /* + * Force an evaluation of node depths before determining if we should limit state-based + * traversal or processing. + */ + + processCurrentNodeStates(queryParams); + + try { + Thread.sleep(10); + } catch (InterruptedException exc) { + LOG.error(AaiUiMsgs.PROCESSING_LOOP_INTERUPTED, exc.getMessage()); + Thread.currentThread().interrupt(); + return; + } + + } + + dumpThrottledWorkOnHandLog(true); + + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.aai.sparky.viewandinspect.services.VisualizationContext#processSelfLinks(org.onap.aai. + * sparky.sync.entity.SearchableEntity, org.onap.aai.sparky.viewandinspect.entity.QueryParams) + */ + @Override + public void processSelfLinks(SearchableEntity searchtargetEntity, QueryParams queryParams) { + + try { + + + if (searchtargetEntity == null) { + LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, + contextIdStr + " - Failed to" + " processSelfLinks, searchtargetEntity is null"); + return; + } + + long startTimeInMs = System.currentTimeMillis(); + + processSearchableEntity(searchtargetEntity, queryParams); + + /* + * This method is blocking until we decouple it with a CountDownLatch await condition, and + * make the internal graph processing more event-y. + */ + + processOutstandingWork(queryParams); + + long totalResolveTime = (System.currentTimeMillis() - startTimeInMs); + + long opTime = System.currentTimeMillis() - startTimeInMs; + + LOG.info(AaiUiMsgs.ALL_TRANSACTIONS_RESOLVED, String.valueOf(totalResolveTime), + String.valueOf(totalLinksRetrieved.get()), String.valueOf(opTime)); + + } catch (Exception exc) { + LOG.error(AaiUiMsgs.VISUALIZATION_OUTPUT_ERROR, exc.getMessage()); + } + + } + + /** + * Verify outbound neighbors. + */ + protected void verifyOutboundNeighbors() { + + for (ActiveInventoryNode srcNode : nodeCache.values()) { + + for (String targetNodeId : srcNode.getOutboundNeighbors()) { + + ActiveInventoryNode targetNode = nodeCache.get(targetNodeId); + + if (targetNode != null && srcNode.getNodeId() != null) { + + targetNode.addInboundNeighbor(srcNode.getNodeId()); + + if (this.visualizationConfigs.makeAllNeighborsBidirectional()) { + targetNode.addOutboundNeighbor(srcNode.getNodeId()); + } + + } + + } + + } + + } + + /** + * Evaluate node depths. + */ + protected void evaluateNodeDepths() { + + int numChanged = -1; + int numAttempts = 0; + + while (numChanged != 0) { + + numChanged = 0; + numAttempts++; + + for (ActiveInventoryNode srcNode : nodeCache.values()) { + + if (srcNode.getState() == NodeProcessingState.INIT) { + + /* + * this maybe the only state that we don't want to to process the node depth on, because + * typically it won't have any valid fields set, and it may remain in a partial state + * until we have processed the self-link. + */ + + continue; + + } + + for (String targetNodeId : srcNode.getOutboundNeighbors()) { + ActiveInventoryNode targetNode = nodeCache.get(targetNodeId); + + if (targetNode != null) { + + if (targetNode.changeDepth(srcNode.getNodeDepth() + 1)) { + numChanged++; + } + } + } + + for (String targetNodeId : srcNode.getInboundNeighbors()) { + ActiveInventoryNode targetNode = nodeCache.get(targetNodeId); + + if (targetNode != null) { + + if (targetNode.changeDepth(srcNode.getNodeDepth() + 1)) { + numChanged++; + } + } + } + } + + if (numAttempts >= MAX_DEPTH_EVALUATION_ATTEMPTS) { + LOG.info(AaiUiMsgs.MAX_EVALUATION_ATTEMPTS_EXCEEDED); + return; + } + + } + + if (LOG.isDebugEnabled()) { + if (numAttempts > 0) { + LOG.debug(AaiUiMsgs.DEBUG_GENERIC, + "Evaluate node depths completed in " + numAttempts + " attempts"); + } else { + LOG.debug(AaiUiMsgs.DEBUG_GENERIC, + "Evaluate node depths completed in 0 attempts because all nodes at correct depth"); + } + } + + } + + + /** + * Gets the entity type primary key name. + * + * @param entityType the entity type + * @return the entity type primary key name + */ + + + protected String getEntityTypePrimaryKeyName(String entityType) { + + if (entityType == null) { + LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE, + "node primary key" + " name because entity type is null"); + return null; + } + + OxmEntityDescriptor descriptor = oxmEntityLookup.getEntityDescriptors().get(entityType); + + if (descriptor == null) { + LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE, + "oxm entity" + " descriptor for entityType = " + entityType); + return null; + } + + List pkeyNames = descriptor.getPrimaryKeyAttributeNames(); + + if (pkeyNames == null || pkeyNames.size() == 0) { + LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE, + "node primary" + " key because descriptor primary key names is empty"); + return null; + } + + return NodeUtils.concatArray(pkeyNames, "/"); + + } + +} diff --git a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/context/BaseGizmoVisualizationContextBuilder.java b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/context/BaseGizmoVisualizationContextBuilder.java new file mode 100644 index 0000000..a0f0fd5 --- /dev/null +++ b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/context/BaseGizmoVisualizationContextBuilder.java @@ -0,0 +1,71 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 Amdocs + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.sparky.viewandinspect.context; + +import java.security.SecureRandom; +import java.util.concurrent.ExecutorService; + +import org.onap.aai.cl.api.Logger; +import org.onap.aai.cl.eelf.LoggerFactory; +import org.onap.aai.sparky.config.oxm.OxmEntityLookup; +import org.onap.aai.sparky.dal.GizmoAdapter; +import org.onap.aai.sparky.util.NodeUtils; +import org.onap.aai.sparky.viewandinspect.VisualizationContext; +import org.onap.aai.sparky.viewandinspect.VisualizationContextBuilder; +import org.onap.aai.sparky.viewandinspect.config.VisualizationConfigs; + +public class BaseGizmoVisualizationContextBuilder implements VisualizationContextBuilder { + + private static final Logger LOG = + LoggerFactory.getInstance().getLogger(BaseGizmoVisualizationContextBuilder.class); + + private SecureRandom secureRandom = new SecureRandom(); + private GizmoAdapter gizmoAdapter; + private final ExecutorService executorService; + private VisualizationConfigs visualizationConfig; + private OxmEntityLookup oxmEntityLookup; + + + public BaseGizmoVisualizationContextBuilder(GizmoAdapter gizmoAdapter, int numWorkers, + VisualizationConfigs visualizationConfig, OxmEntityLookup oxmEntityLookup) { + this.gizmoAdapter = gizmoAdapter; + this.executorService = NodeUtils.createNamedExecutor("SLNC-WORKER", numWorkers, LOG); + this.visualizationConfig = visualizationConfig; + this.oxmEntityLookup = oxmEntityLookup; + } + + @Override + public VisualizationContext getVisualizationContext() throws Exception { + + return new BaseGizmoVisualizationContext(secureRandom.nextLong(), this.gizmoAdapter, + executorService, visualizationConfig, oxmEntityLookup); + + } + + @Override + public void shutdown() { + if (executorService != null) { + executorService.shutdown(); + } + + } + +} \ No newline at end of file diff --git a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/context/BaseVisualizationContext.java b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/context/BaseVisualizationContext.java new file mode 100644 index 0000000..18c6647 --- /dev/null +++ b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/context/BaseVisualizationContext.java @@ -0,0 +1,1607 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 Amdocs + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.sparky.viewandinspect.context; + +import static java.util.concurrent.CompletableFuture.supplyAsync; + +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.http.client.utils.URIBuilder; +import org.onap.aai.cl.api.Logger; +import org.onap.aai.cl.eelf.LoggerFactory; +import org.onap.aai.restclient.client.OperationResult; +import org.onap.aai.sparky.config.oxm.OxmEntityDescriptor; +import org.onap.aai.sparky.config.oxm.OxmEntityLookup; +import org.onap.aai.sparky.dal.ActiveInventoryAdapter; +import org.onap.aai.sparky.logging.AaiUiMsgs; +import org.onap.aai.sparky.sync.entity.SearchableEntity; +import org.onap.aai.sparky.util.NodeUtils; +import org.onap.aai.sparky.viewandinspect.VisualizationContext; +import org.onap.aai.sparky.viewandinspect.config.SparkyConstants; +import org.onap.aai.sparky.viewandinspect.config.VisualizationConfigs; +import org.onap.aai.sparky.viewandinspect.entity.ActiveInventoryNode; +import org.onap.aai.sparky.viewandinspect.entity.NodeProcessingTransaction; +import org.onap.aai.sparky.viewandinspect.entity.QueryParams; +import org.onap.aai.sparky.viewandinspect.entity.Relationship; +import org.onap.aai.sparky.viewandinspect.entity.RelationshipData; +import org.onap.aai.sparky.viewandinspect.entity.RelationshipList; +import org.onap.aai.sparky.viewandinspect.entity.SelfLinkDeterminationTransaction; +import org.onap.aai.sparky.viewandinspect.enumeration.NodeProcessingAction; +import org.onap.aai.sparky.viewandinspect.enumeration.NodeProcessingState; +import org.onap.aai.sparky.viewandinspect.task.PerformNodeSelfLinkProcessingTask; +import org.onap.aai.sparky.viewandinspect.task.PerformSelfLinkDeterminationTask; + +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.PropertyNamingStrategy; + +/** + * The Class SelfLinkNodeCollector. + */ +public class BaseVisualizationContext implements VisualizationContext { + + protected static final int MAX_DEPTH_EVALUATION_ATTEMPTS = 100; + protected static final String DEPTH_ALL_MODIFIER = "?depth=all"; + protected static final String NODES_ONLY_MODIFIER = "?nodes-only"; + protected static final String SERVICE_INSTANCE = "service-instance"; + + private static final Logger LOG = LoggerFactory.getInstance().getLogger( + BaseVisualizationContext.class); + protected final ActiveInventoryAdapter aaiAdapter; + + protected int maxSelfLinkTraversalDepth; + protected AtomicInteger numLinksDiscovered; + protected AtomicInteger numSuccessfulLinkResolveFromCache; + protected AtomicInteger numSuccessfulLinkResolveFromFromServer; + protected AtomicInteger numFailedLinkResolve; + protected AtomicInteger aaiWorkOnHand; + + protected VisualizationConfigs visualizationConfigs; + + protected AtomicInteger totalLinksRetrieved; + + protected final long contextId; + protected final String contextIdStr; + + protected ObjectMapper mapper; + + protected ExecutorService aaiExecutorService; + protected OxmEntityLookup oxmEntityLookup; + protected boolean rootNodeFound; + + /* + * The node cache is intended to be a flat structure indexed by a primary key to avoid needlessly + * re-requesting the same self-links over-and-over again, to speed up the overall render time and + * more importantly to reduce the network cost of determining information we already have. + */ + protected ConcurrentHashMap nodeCache; + + /** + * Instantiates a new self link node collector. + * + * @param loader the loader + * @throws Exception the exception + */ + public BaseVisualizationContext(long contextId, ActiveInventoryAdapter aaiAdapter, + ExecutorService aaiExecutorService, VisualizationConfigs visualizationConfigs, + OxmEntityLookup oxmEntityLookup) + throws Exception { + + this.contextId = contextId; + this.contextIdStr = "[Context-Id=" + contextId + "]"; + this.aaiAdapter = aaiAdapter; + this.aaiExecutorService = aaiExecutorService; + this.visualizationConfigs = visualizationConfigs; + this.oxmEntityLookup = oxmEntityLookup; + + this.nodeCache = new ConcurrentHashMap(); + this.numLinksDiscovered = new AtomicInteger(0); + this.totalLinksRetrieved = new AtomicInteger(0); + this.numSuccessfulLinkResolveFromCache = new AtomicInteger(0); + this.numSuccessfulLinkResolveFromFromServer = new AtomicInteger(0); + this.numFailedLinkResolve = new AtomicInteger(0); + this.aaiWorkOnHand = new AtomicInteger(0); + + this.maxSelfLinkTraversalDepth = this.visualizationConfigs.getMaxSelfLinkTraversalDepth(); + + this.mapper = new ObjectMapper(); + mapper.setSerializationInclusion(Include.NON_EMPTY); + mapper.setPropertyNamingStrategy(new PropertyNamingStrategy.KebabCaseStrategy()); + this.rootNodeFound = false; + } + + protected boolean isRootNodeFound() { + return rootNodeFound; + } + + protected void setRootNodeFound(boolean rootNodeFound) { + this.rootNodeFound = rootNodeFound; + } + + public long getContextId() { + return contextId; + } + + /** + * A utility method for extracting all entity-type primary key values from a provided self-link + * and return a set of generic-query API keys. + * + * @param parentEntityType + * @param link + * @return a list of key values that can be used for this entity with the AAI generic-query API + */ + protected List extractQueryParamsFromSelfLink(String link) { + + List queryParams = new ArrayList(); + + if (link == null) { + LOG.error(AaiUiMsgs.QUERY_PARAM_EXTRACTION_ERROR, "self link is null"); + return queryParams; + } + + Map entityDescriptors = oxmEntityLookup.getEntityDescriptors(); + + try { + + URIBuilder urlBuilder = new URIBuilder(link); + String urlPath = urlBuilder.getPath(); + + OxmEntityDescriptor descriptor = null; + String[] urlPathElements = urlPath.split("/"); + List primaryKeyNames = null; + int index = 0; + String entityType = null; + + while (index < urlPathElements.length) { + + descriptor = entityDescriptors.get(urlPathElements[index]); + + if (descriptor != null) { + entityType = urlPathElements[index]; + primaryKeyNames = descriptor.getPrimaryKeyAttributeNames(); + + /* + * Make sure from what ever index we matched the parent entity-type on that we can extract + * additional path elements for the primary key values. + */ + + if (index + primaryKeyNames.size() < urlPathElements.length) { + + for (String primaryKeyName : primaryKeyNames) { + index++; + queryParams.add(entityType + "." + primaryKeyName + ":" + urlPathElements[index]); + } + } else { + LOG.error(AaiUiMsgs.QUERY_PARAM_EXTRACTION_ERROR, + "Could not extract query parametrs for entity-type = '" + entityType + + "' from self-link = " + link); + } + } + + index++; + } + + } catch (URISyntaxException exc) { + + LOG.error(AaiUiMsgs.QUERY_PARAM_EXTRACTION_ERROR, + "Error extracting query parameters from self-link = " + link + ". Error = " + + exc.getMessage()); + } + + return queryParams; + + } + + /** + * Decode complex attribute group. + * + * @param ain the ain + * @param attributeGroup the attribute group + * @return boolean indicating whether operation was successful (true), / failure(false). + */ + public boolean decodeComplexAttributeGroup(ActiveInventoryNode ain, JsonNode attributeGroup) { + + try { + + Iterator> entityArrays = attributeGroup.fields(); + Entry entityArray = null; + + if (entityArrays == null) { + LOG.error(AaiUiMsgs.ATTRIBUTE_GROUP_FAILURE, attributeGroup.toString()); + ain.changeState(NodeProcessingState.ERROR, NodeProcessingAction.NEIGHBORS_PROCESSED_ERROR); + return false; + } + + while (entityArrays.hasNext()) { + + entityArray = entityArrays.next(); + + String entityType = entityArray.getKey(); + JsonNode entityArrayObject = entityArray.getValue(); + + if (entityArrayObject.isArray()) { + + Iterator entityCollection = entityArrayObject.elements(); + JsonNode entity = null; + while (entityCollection.hasNext()) { + entity = entityCollection.next(); + + if (LOG.isDebugEnabled()) { + LOG.debug(AaiUiMsgs.DEBUG_GENERIC, "decodeComplexAttributeGroup()," + + " entity = " + entity.toString()); + } + + /** + * Here's what we are going to do: + * + *
  • In the ActiveInventoryNode, on construction maintain a collection of queryParams + * that is added to for the purpose of discovering parent->child hierarchies. + * + *
  • When we hit this block of the code then we'll use the queryParams to feed the + * generic query to resolve the self-link asynchronously. + * + *
  • Upon successful link determination, then and only then will we create a new node + * in the nodeCache and process the child + * + */ + + ActiveInventoryNode newNode = new ActiveInventoryNode(this.visualizationConfigs, oxmEntityLookup); + newNode.setEntityType(entityType); + + /* + * This is partially a lie because we actually don't have a self-link for complex nodes + * discovered in this way. + */ + newNode.setSelfLinkProcessed(true); + newNode.changeState(NodeProcessingState.SELF_LINK_RESPONSE_UNPROCESSED, + NodeProcessingAction.COMPLEX_ATTRIBUTE_GROUP_PARSE_OK); + + /* + * copy parent query params into new child + */ + + if (SERVICE_INSTANCE.equals(entityType)) { + + /* + * 1707 AAI has an issue being tracked with AAI-8932 where the generic-query cannot be + * resolved if all the service-instance path keys are provided. The query only works + * if only the service-instance key and valude are passed due to a historical reason. + * A fix is being worked on for 1707, and when it becomes available we can revert this + * small change. + */ + + newNode.clearQueryParams(); + + } else { + + /* + * For all other entity-types we want to copy the parent query parameters into the new node + * query parameters. + */ + + for (String queryParam : ain.getQueryParams()) { + newNode.addQueryParam(queryParam); + } + + } + + + if (!addComplexGroupToNode(newNode, entity)) { + LOG.error(AaiUiMsgs.ATTRIBUTE_GROUP_FAILURE, "Failed to add child to parent for child = " + entity.toString()); + } + + if (!addNodeQueryParams(newNode)) { + LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE_NODE_ID, "Error determining node id and key for node = " + newNode.dumpNodeTree(true) + + " skipping relationship processing"); + newNode.changeState(NodeProcessingState.ERROR, + NodeProcessingAction.NODE_IDENTITY_ERROR); + return false; + } else { + + newNode.changeState(NodeProcessingState.NEIGHBORS_UNPROCESSED, + NodeProcessingAction.COMPLEX_ATTRIBUTE_GROUP_PARSE_OK); + + } + + + /* + * Order matters for the query params. We need to set the parent ones before the child + * node + */ + + String selfLinkQuery = + aaiAdapter.getGenericQueryForSelfLink(entityType, newNode.getQueryParams()); + + /** + *
  • get the self-link + *
  • add it to the new node + *
  • generate node id + *
  • add node to node cache + *
  • add node id to parent outbound links list + *
  • process node children (should be automatic) (but don't query and resolve + * self-link as we already have all the data) + */ + + SelfLinkDeterminationTransaction txn = new SelfLinkDeterminationTransaction(); + + txn.setQueryString(selfLinkQuery); + txn.setNewNode(newNode); + txn.setParentNodeId(ain.getNodeId()); + aaiWorkOnHand.incrementAndGet(); + supplyAsync(new PerformSelfLinkDeterminationTask(txn, null, aaiAdapter), + aaiExecutorService).whenComplete((nodeTxn, error) -> { + + if (error != null) { + LOG.error(AaiUiMsgs.SELF_LINK_DETERMINATION_FAILED_GENERIC, selfLinkQuery); + } else { + + OperationResult opResult = nodeTxn.getOpResult(); + + ActiveInventoryNode newChildNode = txn.getNewNode(); + + if (opResult != null && opResult.wasSuccessful()) { + + if (!opResult.wasSuccessful()) { + numFailedLinkResolve.incrementAndGet(); + } + + if (opResult.isFromCache()) { + numSuccessfulLinkResolveFromCache.incrementAndGet(); + } else { + numSuccessfulLinkResolveFromFromServer.incrementAndGet(); + } + + /* + * extract the self-link from the operational result. + */ + + Collection entityLinks = new ArrayList(); + JsonNode genericQueryResult = null; + try { + genericQueryResult = + NodeUtils.convertJsonStrToJsonNode(nodeTxn.getOpResult().getResult()); + } catch (Exception exc) { + LOG.error(AaiUiMsgs.JSON_CONVERSION_ERROR, JsonNode.class.toString(), exc.getMessage()); + } + + NodeUtils.extractObjectsByKey(genericQueryResult, "resource-link", + entityLinks); + + String selfLink = null; + + if (entityLinks.size() != 1) { + + LOG.error(AaiUiMsgs.SELF_LINK_DETERMINATION_FAILED_UNEXPECTED_LINKS, String.valueOf(entityLinks.size())); + + } else { + selfLink = ((JsonNode) entityLinks.toArray()[0]).asText(); + selfLink = ActiveInventoryAdapter.extractResourcePath(selfLink); + + newChildNode.setSelfLink(selfLink); + newChildNode.setNodeId(NodeUtils.generateUniqueShaDigest(selfLink)); + + String uri = NodeUtils.calculateEditAttributeUri(selfLink); + if (uri != null) { + newChildNode.addProperty(SparkyConstants.URI_ATTR_NAME, uri); + } + + ActiveInventoryNode parent = nodeCache.get(txn.getParentNodeId()); + + if (parent != null) { + parent.addOutboundNeighbor(newChildNode.getNodeId()); + newChildNode.addInboundNeighbor(parent.getNodeId()); + } + + newChildNode.setSelfLinkPendingResolve(false); + newChildNode.setSelfLinkProcessed(true); + newChildNode.changeState(NodeProcessingState.NEIGHBORS_UNPROCESSED, + NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_OK); + + nodeCache.putIfAbsent(newChildNode.getNodeId(), newChildNode); + + } + + } else { + LOG.error(AaiUiMsgs.SELF_LINK_RETRIEVAL_FAILED, txn.getQueryString(), + String.valueOf(nodeTxn.getOpResult().getResultCode()), nodeTxn.getOpResult().getResult()); + newChildNode.setSelflinkRetrievalFailure(true); + newChildNode.setSelfLinkProcessed(true); + newChildNode.setSelfLinkPendingResolve(false); + + newChildNode.changeState(NodeProcessingState.ERROR, + NodeProcessingAction.SELF_LINK_DETERMINATION_ERROR); + + } + + } + + aaiWorkOnHand.decrementAndGet(); + + }); + + } + + return true; + + } else { + LOG.error(AaiUiMsgs.UNHANDLED_OBJ_TYPE_FOR_ENTITY_TYPE, entityType); + } + + } + } catch (Exception exc) { + LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, "Exception caught while" + + " decoding complex attribute group - " + exc.getMessage()); + } + + return false; + + } + + /** + * Process self link response. + * + * @param nodeId the node id + */ + protected void processSelfLinkResponse(String nodeId) { + + if (nodeId == null) { + LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, "Cannot process self link" + + " response because nodeId is null"); + return; + } + + ActiveInventoryNode ain = nodeCache.get(nodeId); + + if (ain == null) { + LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, "Cannot process self link response" + + " because can't find node for id = " + nodeId); + return; + } + + JsonNode jsonNode = null; + + try { + jsonNode = mapper.readValue(ain.getOpResult().getResult(), JsonNode.class); + } catch (Exception exc) { + LOG.error(AaiUiMsgs.SELF_LINK_JSON_PARSE_ERROR, "Failed to marshal json" + + " response str into JsonNode with error, " + exc.getLocalizedMessage()); + ain.changeState(NodeProcessingState.ERROR, + NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_ERROR); + return; + } + + if (jsonNode == null) { + LOG.error(AaiUiMsgs.SELF_LINK_JSON_PARSE_ERROR, "Failed to parse json node str." + + " Parse resulted a null value."); + ain.changeState(NodeProcessingState.ERROR, + NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_ERROR); + return; + } + + Iterator> fieldNames = jsonNode.fields(); + Entry field = null; + + RelationshipList relationshipList = null; + + while (fieldNames.hasNext()) { + + field = fieldNames.next(); + String fieldName = field.getKey(); + + if ("relationship-list".equals(fieldName)) { + + try { + relationshipList = mapper.readValue(field.getValue().toString(), RelationshipList.class); + + if (relationshipList != null) { + ain.addRelationshipList(relationshipList); + } + + } catch (Exception exc) { + LOG.error(AaiUiMsgs.SELF_LINK_JSON_PARSE_ERROR, "Failed to parse relationship-list" + + " attribute. Parse resulted in error, " + exc.getLocalizedMessage()); + ain.changeState(NodeProcessingState.ERROR, + NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_ERROR); + return; + } + + } else { + + JsonNode nodeValue = field.getValue(); + + if(nodeValue!=null) { + if (nodeValue.isValueNode()) { + String key = fieldName; + handleNodeValue(ain, fieldName, key, nodeValue.asText()); + } else if (nodeValue.isArray()) { + String key = field.getKey(); + handleNodeValue(ain, fieldName, key, nodeValue.toString()); + } else { + ain.addComplexGroup(nodeValue); + } + + } + } + + } + + String uri = NodeUtils.calculateEditAttributeUri(ain.getSelfLink()); + if (uri != null) { + ain.addProperty(SparkyConstants.URI_ATTR_NAME, uri); + } + + /* + * We need a special behavior for intermediate entities from the REST model + * + * Tenants are not top level entities, and when we want to visualization + * their children, we need to construct keys that include the parent entity query + * keys, the current entity type keys, and the child keys. We'll always have the + * current entity and children, but never the parent entity in the current (1707) REST + * data model. + * + * We have two possible solutions: + * + * 1) Try to use the custom-query approach to learn about the entity keys + * - this could be done, but it could be very expensive for large objects. When we do the first + * query to get a tenant, it will list all the in and out edges related to this entity, + * there is presently no way to filter this. But the approach could be made to work and it would be + * somewhat data-model driven, other than the fact that we have to first realize that the entity + * that is being searched for is not top-level entity. Once we have globally unique ids for resources + * this logic will not be needed and everything will be simpler. The only reason we are in this logic + * at all is to be able to calculate a url for the child entities so we can hash it to generate + * a globally unique id that can be safely used for the node. + * + * *2* Extract the keys from the pathed self-link. + * This is a bad solution and I don't like it but it will be fast for all resource types, as the + * information is already encoded in the URI. When we get to a point where we switch to a better + * globally unique entity identity model, then a lot of the code being used to calculate an entity url + * to in-turn generate a deterministic globally unique id will disappear. + * + * + * right now we have the following: + * + * - cloud-regions/cloud-region/{cloud-region-id}/{cloud-owner-id}/tenants/tenant/{tenant-id} + * + */ + + /* + * For all entity types use the self-link extraction method to be consistent. Once we have a + * globally unique identity mechanism for entities, this logic can be revisited. + */ + ain.clearQueryParams(); + ain.addQueryParams(extractQueryParamsFromSelfLink(ain.getSelfLink())); + ain.changeState(NodeProcessingState.NEIGHBORS_UNPROCESSED, + NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_OK); + + + } + + protected void handleNodeValue(ActiveInventoryNode ain, String fieldName, String key, String value) { + if (oxmEntityLookup.getEntityDescriptors().get(fieldName) == null) { + + /* + * entity property name is not an entity, thus we can add this property name and value + * to our property set + */ + + ain.addProperty(key, value); + + } + } + + /** + * Perform self link resolve. + * + * @param nodeId the node id + */ + protected void performSelfLinkResolve(String nodeId) { + + if (nodeId == null) { + LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, "Resolve of self-link" + + " has been skipped because provided nodeId is null"); + return; + } + + ActiveInventoryNode ain = nodeCache.get(nodeId); + + if (ain == null) { + LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, "Failed to find node with id, " + nodeId + + ", from node cache. Resolve self-link method has been skipped."); + return; + } + + if (!ain.isSelfLinkPendingResolve()) { + + ain.setSelfLinkPendingResolve(true); + + // kick off async self-link resolution + + if (LOG.isDebugEnabled()) { + LOG.debug(AaiUiMsgs.DEBUG_GENERIC, + "About to process node in SELF_LINK_UNPROCESSED State, link = " + ain.getSelfLink()); + } + + numLinksDiscovered.incrementAndGet(); + + String depthModifier = DEPTH_ALL_MODIFIER; + + /* + * If the current node is the search target, we want to see everything the node has to offer + * from the self-link and not filter it to a single node. + */ + + if (visualizationConfigs.getShallowEntities().contains(ain.getEntityType()) + && !ain.isRootNode()) { + depthModifier = NODES_ONLY_MODIFIER; + } + + NodeProcessingTransaction txn = new NodeProcessingTransaction(); + txn.setProcessingNode(ain); + txn.setRequestParameters(depthModifier); + aaiWorkOnHand.incrementAndGet(); + supplyAsync( + new PerformNodeSelfLinkProcessingTask(txn, depthModifier, aaiAdapter), + aaiExecutorService).whenComplete((nodeTxn, error) -> { + + if (error != null) { + + /* + * an error processing the self link should probably result in the node processing + * state shifting to ERROR + */ + + nodeTxn.getProcessingNode().setSelflinkRetrievalFailure(true); + + nodeTxn.getProcessingNode().changeState(NodeProcessingState.ERROR, + NodeProcessingAction.SELF_LINK_RESOLVE_ERROR); + + nodeTxn.getProcessingNode().setSelfLinkPendingResolve(false); + + } else { + + totalLinksRetrieved.incrementAndGet(); + + OperationResult opResult = nodeTxn.getOpResult(); + + if (opResult != null && opResult.wasSuccessful()) { + + if (!opResult.wasSuccessful()) { + numFailedLinkResolve.incrementAndGet(); + } + + if (opResult.isFromCache()) { + numSuccessfulLinkResolveFromCache.incrementAndGet(); + } else { + numSuccessfulLinkResolveFromFromServer.incrementAndGet(); + } + + // success path + nodeTxn.getProcessingNode().setOpResult(opResult); + nodeTxn.getProcessingNode().changeState( + NodeProcessingState.SELF_LINK_RESPONSE_UNPROCESSED, + NodeProcessingAction.SELF_LINK_RESOLVE_OK); + + nodeTxn.getProcessingNode().setSelfLinkProcessed(true); + nodeTxn.getProcessingNode().setSelfLinkPendingResolve(false); + + } else { + LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, "Self Link retrieval for link," + + txn.getSelfLinkWithModifiers() + ", failed with error code," + + nodeTxn.getOpResult().getResultCode() + ", and message," + + nodeTxn.getOpResult().getResult()); + + nodeTxn.getProcessingNode().setSelflinkRetrievalFailure(true); + nodeTxn.getProcessingNode().setSelfLinkProcessed(true); + + nodeTxn.getProcessingNode().changeState(NodeProcessingState.ERROR, + NodeProcessingAction.SELF_LINK_RESOLVE_ERROR); + + nodeTxn.getProcessingNode().setSelfLinkPendingResolve(false); + + } + } + + aaiWorkOnHand.decrementAndGet(); + + }); + + } + + } + + + /** + * Process neighbors. + * + * @param nodeId the node id + */ + protected void processNeighbors(String nodeId) { + + if (nodeId == null) { + LOG.error(AaiUiMsgs.SELF_LINK_PROCESS_NEIGHBORS_ERROR, "Failed to process" + + " neighbors because nodeId is null."); + return; + } + + ActiveInventoryNode ain = nodeCache.get(nodeId); + + if (ain == null) { + LOG.error(AaiUiMsgs.SELF_LINK_PROCESS_NEIGHBORS_ERROR, "Failed to process" + + " neighbors because node could not be found in nodeCache with id, " + nodeId); + return; + } + + /* + * process complex attribute and relationships + */ + + boolean neighborsProcessedSuccessfully = true; + + for (JsonNode n : ain.getComplexGroups()) { + neighborsProcessedSuccessfully &= decodeComplexAttributeGroup(ain, n); + } + + for (RelationshipList relationshipList : ain.getRelationshipLists()) { + neighborsProcessedSuccessfully &= addSelfLinkRelationshipChildren(ain, relationshipList); + } + + + if (neighborsProcessedSuccessfully) { + ain.changeState(NodeProcessingState.READY, NodeProcessingAction.NEIGHBORS_PROCESSED_OK); + } else { + ain.changeState(NodeProcessingState.ERROR, NodeProcessingAction.NEIGHBORS_PROCESSED_ERROR); + } + + + /* + * If neighbors fail to process, there is already a call to change the state within the + * relationship and neighbor processing functions. + */ + + } + + /** + * Find and mark root node. + * + * @param queryParams the query params + * @return true, if successful + */ + protected void findAndMarkRootNode(QueryParams queryParams) { + + if (isRootNodeFound()) { + return; + } + + for (ActiveInventoryNode cacheNode : nodeCache.values()) { + + if (queryParams.getSearchTargetNodeId().equals(cacheNode.getNodeId())) { + cacheNode.setNodeDepth(0); + cacheNode.setRootNode(true); + LOG.info(AaiUiMsgs.ROOT_NODE_DISCOVERED, queryParams.getSearchTargetNodeId()); + setRootNodeFound(true); + } + } + + } + + /** + * Process current node states. + * + */ + protected void processCurrentNodeStates(QueryParams queryParams) { + /* + * Force an evaluation of node depths before determining if we should limit state-based + * traversal or processing. + */ + + findAndMarkRootNode(queryParams); + + verifyOutboundNeighbors(); + + for (ActiveInventoryNode cacheNode : nodeCache.values()) { + + if (LOG.isDebugEnabled()) { + LOG.debug(AaiUiMsgs.DEBUG_GENERIC, + "processCurrentNodeState(), nid = " + + cacheNode.getNodeId() + " , nodeDepth = " + cacheNode.getNodeDepth()); + } + + switch (cacheNode.getState()) { + + case INIT: { + processInitialState(cacheNode.getNodeId()); + break; + } + + case READY: + case ERROR: { + break; + } + + case SELF_LINK_UNRESOLVED: { + performSelfLinkResolve(cacheNode.getNodeId()); + break; + } + + case SELF_LINK_RESPONSE_UNPROCESSED: { + processSelfLinkResponse(cacheNode.getNodeId()); + break; + } + + case NEIGHBORS_UNPROCESSED: { + + /* + * We use the rootNodeDiscovered flag to ignore depth retrieval thresholds until the root + * node is identified. Then the evaluative depth calculations should re-balance the graph + * around the root node. + */ + + if (!isRootNodeFound() || cacheNode.getNodeDepth() < this.visualizationConfigs + .getMaxSelfLinkTraversalDepth()) { + + if (LOG.isDebugEnabled()) { + LOG.debug(AaiUiMsgs.DEBUG_GENERIC, + "processCurrentNodeState() -- Node at max depth," + + " halting processing at current state = -- " + + cacheNode.getState() + " nodeId = " + cacheNode.getNodeId()); + } + + processNeighbors(cacheNode.getNodeId()); + + } + + break; + } + default: + break; + } + + } + + } + + /** + * Adds the complex group to node. + * + * @param targetNode the target node + * @param attributeGroup the attribute group + * @return true, if successful + */ + protected boolean addComplexGroupToNode(ActiveInventoryNode targetNode, JsonNode attributeGroup) { + + if (attributeGroup == null) { + targetNode.changeState(NodeProcessingState.ERROR, + NodeProcessingAction.COMPLEX_ATTRIBUTE_GROUP_PARSE_OK); + return false; + } + + RelationshipList relationshipList = null; + + if (attributeGroup.isObject()) { + + Iterator> fields = attributeGroup.fields(); + Entry field = null; + String fieldName; + JsonNode fieldValue; + + while (fields.hasNext()) { + field = fields.next(); + fieldName = field.getKey(); + fieldValue = field.getValue(); + + if (fieldValue.isObject()) { + + if (fieldName.equals("relationship-list")) { + + try { + relationshipList = + mapper.readValue(field.getValue().toString(), RelationshipList.class); + + if (relationshipList != null) { + targetNode.addRelationshipList(relationshipList); + } + + } catch (Exception exc) { + LOG.error(AaiUiMsgs.SELF_LINK_JSON_PARSE_ERROR, "Failed to parse" + + " relationship-list attribute. Parse resulted in error, " + + exc.getLocalizedMessage()); + targetNode.changeState(NodeProcessingState.ERROR, + NodeProcessingAction.COMPLEX_ATTRIBUTE_GROUP_PARSE_ERROR); + return false; + } + + } else { + targetNode.addComplexGroup(fieldValue); + } + + } else if (fieldValue.isArray()) { + if (LOG.isDebugEnabled()) { + LOG.debug(AaiUiMsgs.DEBUG_GENERIC, + "Unexpected array type with a key = " + fieldName); + } + } else if (fieldValue.isValueNode()) { + if (oxmEntityLookup.getEntityDescriptors().get(field.getKey()) == null) { + /* + * property key is not an entity type, add it to our property set. + */ + targetNode.addProperty(field.getKey(), fieldValue.asText()); + } + + } + } + + } else if (attributeGroup.isArray()) { + if (LOG.isDebugEnabled()) { + LOG.debug(AaiUiMsgs.DEBUG_GENERIC, + "Unexpected array type for attributeGroup = " + attributeGroup); + } + } else if (attributeGroup.isValueNode()) { + if (LOG.isDebugEnabled()) { + LOG.debug(AaiUiMsgs.DEBUG_GENERIC, + "Unexpected value type for attributeGroup = " + attributeGroup); + } + } + + return true; + } + + public int getNumSuccessfulLinkResolveFromCache() { + return numSuccessfulLinkResolveFromCache.get(); + } + + public int getNumSuccessfulLinkResolveFromFromServer() { + return numSuccessfulLinkResolveFromFromServer.get(); + } + + public int getNumFailedLinkResolve() { + return numFailedLinkResolve.get(); + } + + public void setMaxSelfLinkTraversalDepth(int depth) { + this.maxSelfLinkTraversalDepth = depth; + } + + public int getMaxSelfLinkTraversalDepth() { + return this.maxSelfLinkTraversalDepth; + } + + public ConcurrentHashMap getNodeCache() { + return nodeCache; + } + + /** + * Gets the relationship primary key values. + * + * @param r the r + * @param entityType the entity type + * @param pkeyNames the pkey names + * @return the relationship primary key values + */ + protected String getRelationshipPrimaryKeyValues(Relationship r, String entityType, + List pkeyNames) { + + StringBuilder sb = new StringBuilder(64); + + if (pkeyNames.size() > 0) { + String primaryKey = extractKeyValueFromRelationData(r, entityType + "." + pkeyNames.get(0)); + if (primaryKey != null) { + + sb.append(primaryKey); + + } else { + // this should be a fatal error because unless we can + // successfully retrieve all the expected keys we'll end up + // with a garbage node + LOG.error(AaiUiMsgs.EXTRACTION_ERROR, "ERROR: Failed to extract" + + " keyName, " + entityType + "." + pkeyNames.get(0) + + ", from relationship data, " + r.toString()); + return null; + } + + for (int i = 1; i < pkeyNames.size(); i++) { + + String kv = extractKeyValueFromRelationData(r, entityType + "." + pkeyNames.get(i)); + if (kv != null) { + sb.append("/").append(kv); + } else { + // this should be a fatal error because unless we can + // successfully retrieve all the expected keys we'll end up + // with a garbage node + LOG.error(AaiUiMsgs.EXTRACTION_ERROR, "ERROR: failed to extract keyName, " + + entityType + "." + pkeyNames.get(i) + + ", from relationship data, " + r.toString()); + return null; + } + } + + return sb.toString(); + + } + + return null; + + } + + /** + * Extract key value from relation data. + * + * @param r the r + * @param keyName the key name + * @return the string + */ + protected String extractKeyValueFromRelationData(Relationship r, String keyName) { + + RelationshipData[] rdList = r.getRelationshipData(); + + for (RelationshipData relData : rdList) { + + if (relData.getRelationshipKey().equals(keyName)) { + return relData.getRelationshipValue(); + } + } + + return null; + } + + /** + * Determine node id and key. + * + * @param ain the ain + * @return true, if successful + */ + protected boolean addNodeQueryParams(ActiveInventoryNode ain) { + + if (ain == null) { + LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE_NODE_ID, "ActiveInventoryNode is null"); + return false; + } + + List pkeyNames = + oxmEntityLookup.getEntityDescriptors().get(ain.getEntityType()).getPrimaryKeyAttributeNames(); + + if (pkeyNames == null || pkeyNames.size() == 0) { + LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE_NODE_ID, "Primary key names is empty"); + return false; + } + + StringBuilder sb = new StringBuilder(64); + + if (pkeyNames.size() > 0) { + String primaryKey = ain.getProperties().get(pkeyNames.get(0)); + if (primaryKey != null) { + sb.append(primaryKey); + } else { + // this should be a fatal error because unless we can + // successfully retrieve all the expected keys we'll end up + // with a garbage node + LOG.error(AaiUiMsgs.EXTRACTION_ERROR, "ERROR: Failed to extract keyName, " + + pkeyNames.get(0) + ", from entity properties"); + return false; + } + + for (int i = 1; i < pkeyNames.size(); i++) { + + String kv = ain.getProperties().get(pkeyNames.get(i)); + if (kv != null) { + sb.append("/").append(kv); + } else { + // this should be a fatal error because unless we can + // successfully retrieve all the expected keys we'll end up + // with a garbage node + LOG.error(AaiUiMsgs.EXTRACTION_ERROR, "ERROR: Failed to extract keyName, " + + pkeyNames.get(i) + ", from entity properties"); + return false; + } + } + + /*final String nodeId = NodeUtils.generateUniqueShaDigest(ain.getEntityType(), + NodeUtils.concatArray(pkeyNames, "/"), sb.toString());*/ + + //ain.setNodeId(nodeId); + ain.setPrimaryKeyName(NodeUtils.concatArray(pkeyNames, "/")); + ain.setPrimaryKeyValue(sb.toString()); + + if (ain.getEntityType() != null && ain.getPrimaryKeyName() != null + && ain.getPrimaryKeyValue() != null) { + ain.addQueryParam( + ain.getEntityType() + "." + ain.getPrimaryKeyName() + ":" + ain.getPrimaryKeyValue()); + } + return true; + + } + + return false; + + } + + /** + * Adds the self link relationship children. + * + * @param processingNode the processing node + * @param relationshipList the relationship list + * @return true, if successful + */ + protected boolean addSelfLinkRelationshipChildren(ActiveInventoryNode processingNode, + RelationshipList relationshipList) { + + if (relationshipList == null) { + LOG.debug(AaiUiMsgs.DEBUG_GENERIC, "No relationships added to parent node = " + + processingNode.getNodeId() + " because relationshipList is empty"); + processingNode.changeState(NodeProcessingState.ERROR, + NodeProcessingAction.NEIGHBORS_PROCESSED_ERROR); + return false; + } + + Relationship[] relationshipArray = relationshipList.getRelationshipList(); + OxmEntityDescriptor descriptor = null; + + if (relationshipArray != null) { + + ActiveInventoryNode newNode = null; + String resourcePath = null; + + for (Relationship r : relationshipArray) { + + resourcePath = ActiveInventoryAdapter.extractResourcePath(r.getRelatedLink()); + + String nodeId = NodeUtils.generateUniqueShaDigest(resourcePath); + + if (nodeId == null) { + + LOG.error(AaiUiMsgs.SKIPPING_RELATIONSHIP, r.toString()); + processingNode.changeState(NodeProcessingState.ERROR, + NodeProcessingAction.NODE_IDENTITY_ERROR); + return false; + } + + newNode = new ActiveInventoryNode(this.visualizationConfigs, oxmEntityLookup); + + String entityType = r.getRelatedTo(); + + if (r.getRelationshipData() != null) { + for (RelationshipData rd : r.getRelationshipData()) { + newNode.addQueryParam(rd.getRelationshipKey() + ":" + rd.getRelationshipValue()); + } + } + + descriptor = oxmEntityLookup.getEntityDescriptors().get(r.getRelatedTo()); + + newNode.setNodeId(nodeId); + newNode.setEntityType(entityType); + newNode.setSelfLink(resourcePath); + + processingNode.addOutboundNeighbor(nodeId); + + if (descriptor != null) { + + List pkeyNames = descriptor.getPrimaryKeyAttributeNames(); + + newNode.changeState(NodeProcessingState.SELF_LINK_UNRESOLVED, + NodeProcessingAction.SELF_LINK_SET); + + newNode.setPrimaryKeyName(NodeUtils.concatArray(pkeyNames, "/")); + + String primaryKeyValues = getRelationshipPrimaryKeyValues(r, entityType, pkeyNames); + newNode.setPrimaryKeyValue(primaryKeyValues); + + } else { + + LOG.error(AaiUiMsgs.VISUALIZATION_OUTPUT_ERROR, + "Failed to parse entity because OXM descriptor could not be found for type = " + + r.getRelatedTo()); + + newNode.changeState(NodeProcessingState.ERROR, + NodeProcessingAction.NEIGHBORS_PROCESSED_ERROR); + + } + + if (nodeCache.putIfAbsent(nodeId, newNode) != null) { + if (LOG.isDebugEnabled()) { + LOG.debug(AaiUiMsgs.DEBUG_GENERIC, + "Failed to add node to nodeCache because it already exists. Node id = " + + newNode.getNodeId()); + } + } + + } + + } + + return true; + + } + + /** + * Process initial state. + * + * @param nodeId the node id + */ + protected void processInitialState(String nodeId) { + + if (nodeId == null) { + LOG.error(AaiUiMsgs.FAILED_TO_PROCESS_INITIAL_STATE, "Node id is null"); + return; + } + + ActiveInventoryNode cachedNode = nodeCache.get(nodeId); + + if (cachedNode == null) { + LOG.error(AaiUiMsgs.FAILED_TO_PROCESS_INITIAL_STATE, "Node cannot be" + + " found for nodeId, " + nodeId); + return; + } + + if (cachedNode.getSelfLink() == null) { + + if (cachedNode.getNodeId() == null ) { + + /* + * if the self link is null at the INIT state, which could be valid if this node is a + * complex attribute group which didn't originate from a self-link, but in that situation + * both the node id and node key should already be set. + */ + + cachedNode.changeState(NodeProcessingState.ERROR, NodeProcessingAction.NODE_IDENTITY_ERROR); + + } + + if (cachedNode.getNodeId() != null) { + + /* + * This should be the success path branch if the self-link is not set + */ + + cachedNode.changeState(NodeProcessingState.SELF_LINK_RESPONSE_UNPROCESSED, + NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_OK); + + } + + } else { + + if (cachedNode.hasResolvedSelfLink()) { + LOG.error(AaiUiMsgs.INVALID_RESOLVE_STATE_DURING_INIT); + cachedNode.changeState(NodeProcessingState.ERROR, + NodeProcessingAction.UNEXPECTED_STATE_TRANSITION); + } else { + cachedNode.changeState(NodeProcessingState.SELF_LINK_UNRESOLVED, + NodeProcessingAction.SELF_LINK_SET); + } + } + } + + /** + * Process skeleton node. + * + * @param skeletonNode the skeleton node + * @param queryParams the query params + */ + protected void processSearchableEntity(SearchableEntity searchTargetEntity, QueryParams queryParams) { + + if (searchTargetEntity == null) { + return; + } + + if (searchTargetEntity.getId() == null) { + LOG.error(AaiUiMsgs.FAILED_TO_PROCESS_SKELETON_NODE, "Failed to process skeleton" + + " node because nodeId is null for node, " + searchTargetEntity.getLink()); + return; + } + + ActiveInventoryNode newNode = new ActiveInventoryNode(this.visualizationConfigs, oxmEntityLookup); + + newNode.setNodeId(searchTargetEntity.getId()); + newNode.setEntityType(searchTargetEntity.getEntityType()); + newNode.setPrimaryKeyName(getEntityTypePrimaryKeyName(searchTargetEntity.getEntityType())); + newNode.setPrimaryKeyValue(searchTargetEntity.getEntityPrimaryKeyValue()); + + if (newNode.getEntityType() != null && newNode.getPrimaryKeyName() != null + && newNode.getPrimaryKeyValue() != null) { + newNode.addQueryParam( + newNode.getEntityType() + "." + newNode.getPrimaryKeyName() + ":" + newNode.getPrimaryKeyValue()); + } + /* + * This code may need some explanation. In any graph there will be a single root node. The root + * node is really the center of the universe, and for now, we are tagging the search target as + * the root node. Everything else in the visualization of the graph will be centered around this + * node as the focal point of interest. + * + * Due to it's special nature, there will only ever be one root node, and it's node depth will + * always be equal to zero. + */ + + if (!isRootNodeFound()) { + if (queryParams.getSearchTargetNodeId().equals(newNode.getNodeId())) { + newNode.setNodeDepth(0); + newNode.setRootNode(true); + LOG.info(AaiUiMsgs.ROOT_NODE_DISCOVERED, queryParams.getSearchTargetNodeId()); + setRootNodeFound(true); + } + } + + newNode.setSelfLink(searchTargetEntity.getLink()); + + nodeCache.putIfAbsent(newNode.getNodeId(), newNode); + } + + protected int getTotalWorkOnHand() { + + int numNodesWithPendingStates = 0; + + if( isRootNodeFound()) { + evaluateNodeDepths(); + } + + for (ActiveInventoryNode n : nodeCache.values()) { + + switch (n.getState()) { + + case READY: + case ERROR: { + // do nothing, these are our normal + // exit states + break; + } + + case NEIGHBORS_UNPROCESSED: { + + if (n.getNodeDepth() < this.visualizationConfigs.getMaxSelfLinkTraversalDepth()) { + /* + * Only process our neighbors relationships if our current depth is less than the max + * depth + */ + numNodesWithPendingStates++; + } + + break; + } + + default: { + + /* + * for all other states, there is work to be done + */ + numNodesWithPendingStates++; + } + + } + + } + + LOG.debug(AaiUiMsgs.OUTSTANDING_WORK_PENDING_NODES, + String.valueOf(numNodesWithPendingStates)); + + int totalWorkOnHand = aaiWorkOnHand.get() + numNodesWithPendingStates; + + return totalWorkOnHand; + + } + + /** + * Checks for out standing work. + * + * @return true, if successful + */ + protected void processOutstandingWork(QueryParams queryParams) { + + while (getTotalWorkOnHand() > 0) { + + /* + * Force an evaluation of node depths before determining if we should limit state-based + * traversal or processing. + */ + + processCurrentNodeStates(queryParams); + + try { + Thread.sleep(10); + } catch (InterruptedException exc) { + LOG.error(AaiUiMsgs.PROCESSING_LOOP_INTERUPTED, exc.getMessage()); + Thread.currentThread().interrupt(); + return; + } + + } + + } + + /* (non-Javadoc) + * @see org.onap.aai.sparky.viewandinspect.services.VisualizationContext#processSelfLinks(org.onap.aai.sparky.sync.entity.SearchableEntity, org.onap.aai.sparky.viewandinspect.entity.QueryParams) + */ + @Override + public void processSelfLinks(SearchableEntity searchtargetEntity, QueryParams queryParams) { + + try { + + + if (searchtargetEntity == null) { + LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, contextIdStr + " - Failed to" + + " processSelfLinks, searchtargetEntity is null"); + return; + } + + long startTimeInMs = System.currentTimeMillis(); + + processSearchableEntity(searchtargetEntity, queryParams); + + /* + * This method is blocking until we decouple it with a CountDownLatch await condition, + * and make the internal graph processing more event-y. + */ + + processOutstandingWork(queryParams); + + long totalResolveTime = (System.currentTimeMillis() - startTimeInMs); + + long opTime = System.currentTimeMillis() - startTimeInMs; + + LOG.info(AaiUiMsgs.ALL_TRANSACTIONS_RESOLVED, String.valueOf(totalResolveTime), + String.valueOf(totalLinksRetrieved.get()), String.valueOf(opTime)); + + } catch (Exception exc) { + LOG.error(AaiUiMsgs.VISUALIZATION_OUTPUT_ERROR, exc.getMessage()); + } + + } + + /** + * Verify outbound neighbors. + */ + protected void verifyOutboundNeighbors() { + + for (ActiveInventoryNode srcNode : nodeCache.values()) { + + for (String targetNodeId : srcNode.getOutboundNeighbors()) { + + ActiveInventoryNode targetNode = nodeCache.get(targetNodeId); + + if (targetNode != null && srcNode.getNodeId() != null) { + + targetNode.addInboundNeighbor(srcNode.getNodeId()); + + if (this.visualizationConfigs.makeAllNeighborsBidirectional()) { + targetNode.addOutboundNeighbor(srcNode.getNodeId()); + } + + } + + } + + } + + } + + /** + * Evaluate node depths. + */ + protected void evaluateNodeDepths() { + + int numChanged = -1; + int numAttempts = 0; + + while (numChanged != 0) { + + numChanged = 0; + numAttempts++; + + for (ActiveInventoryNode srcNode : nodeCache.values()) { + + if (srcNode.getState() == NodeProcessingState.INIT) { + + /* + * this maybe the only state that we don't want to to process the node depth on, because + * typically it won't have any valid fields set, and it may remain in a partial state + * until we have processed the self-link. + */ + + continue; + + } + + for (String targetNodeId : srcNode.getOutboundNeighbors()) { + ActiveInventoryNode targetNode = nodeCache.get(targetNodeId); + + if (targetNode != null) { + + if (targetNode.changeDepth(srcNode.getNodeDepth() + 1)) { + numChanged++; + } + } + } + + for (String targetNodeId : srcNode.getInboundNeighbors()) { + ActiveInventoryNode targetNode = nodeCache.get(targetNodeId); + + if (targetNode != null) { + + if (targetNode.changeDepth(srcNode.getNodeDepth() + 1)) { + numChanged++; + } + } + } + } + + if (numAttempts >= MAX_DEPTH_EVALUATION_ATTEMPTS) { + LOG.info(AaiUiMsgs.MAX_EVALUATION_ATTEMPTS_EXCEEDED); + return; + } + + } + + if (LOG.isDebugEnabled()) { + if (numAttempts > 0) { + LOG.debug(AaiUiMsgs.DEBUG_GENERIC, + "Evaluate node depths completed in " + numAttempts + " attempts"); + } else { + LOG.debug(AaiUiMsgs.DEBUG_GENERIC, + "Evaluate node depths completed in 0 attempts because all nodes at correct depth"); + } + } + + } + + + /** + * Gets the entity type primary key name. + * + * @param entityType the entity type + * @return the entity type primary key name + */ + + + protected String getEntityTypePrimaryKeyName(String entityType) { + + if (entityType == null) { + LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE, "node primary key" + + " name because entity type is null"); + return null; + } + + OxmEntityDescriptor descriptor = oxmEntityLookup.getEntityDescriptors().get(entityType); + + if (descriptor == null) { + LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE, "oxm entity" + + " descriptor for entityType = " + entityType); + return null; + } + + List pkeyNames = descriptor.getPrimaryKeyAttributeNames(); + + if (pkeyNames == null || pkeyNames.size() == 0) { + LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE, "node primary" + + " key because descriptor primary key names is empty"); + return null; + } + + return NodeUtils.concatArray(pkeyNames, "/"); + + } + +} diff --git a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/context/BaseVisualizationContextBuilder.java b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/context/BaseVisualizationContextBuilder.java new file mode 100644 index 0000000..7ffc9d0 --- /dev/null +++ b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/context/BaseVisualizationContextBuilder.java @@ -0,0 +1,70 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 Amdocs + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.sparky.viewandinspect.context; + +import java.security.SecureRandom; +import java.util.concurrent.ExecutorService; + +import org.onap.aai.cl.api.Logger; +import org.onap.aai.cl.eelf.LoggerFactory; +import org.onap.aai.sparky.config.oxm.OxmEntityLookup; +import org.onap.aai.sparky.dal.ActiveInventoryAdapter; +import org.onap.aai.sparky.util.NodeUtils; +import org.onap.aai.sparky.viewandinspect.VisualizationContext; +import org.onap.aai.sparky.viewandinspect.VisualizationContextBuilder; +import org.onap.aai.sparky.viewandinspect.config.VisualizationConfigs; + +public class BaseVisualizationContextBuilder implements VisualizationContextBuilder { + + private static final Logger LOG = + LoggerFactory.getInstance().getLogger(BaseVisualizationContextBuilder.class); + + private SecureRandom secureRandom = new SecureRandom(); + private ActiveInventoryAdapter aaiAdapter; + private final ExecutorService executorService; + private VisualizationConfigs visualizationConfig; + private OxmEntityLookup oxmEntityLookup; + + + public BaseVisualizationContextBuilder(ActiveInventoryAdapter aaiAdapter, int numWorkers, + VisualizationConfigs visualizationConfig, OxmEntityLookup oxmEntityLookup) { + this.aaiAdapter = aaiAdapter; + this.executorService = NodeUtils.createNamedExecutor("SLNC-WORKER", numWorkers, LOG); + this.visualizationConfig = visualizationConfig; + this.oxmEntityLookup = oxmEntityLookup; + } + + @Override + public VisualizationContext getVisualizationContext() throws Exception { + return new BaseVisualizationContext(secureRandom.nextLong(), this.aaiAdapter, executorService, + visualizationConfig, oxmEntityLookup); + + } + + @Override + public void shutdown() { + if (executorService != null) { + executorService.shutdown(); + } + + } + +} \ No newline at end of file diff --git a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/entity/D3VisualizationOutput.java b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/entity/D3VisualizationOutput.java index 9599726..ea63065 100644 --- a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/entity/D3VisualizationOutput.java +++ b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/entity/D3VisualizationOutput.java @@ -31,7 +31,6 @@ public class D3VisualizationOutput { public GraphMeta graphMeta; public List nodes; public List links; - public InlineMessage inlineMessage; /** * Instantiates a new d 3 visualization output. @@ -39,7 +38,6 @@ public class D3VisualizationOutput { public D3VisualizationOutput() { nodes = new ArrayList(); links = new ArrayList(); - inlineMessage = null; } public GraphMeta getGraphMeta() { @@ -77,13 +75,4 @@ public class D3VisualizationOutput { this.links.addAll(links); } - public InlineMessage getInlineMessage() { - return inlineMessage; - } - - public void setInlineMessage(InlineMessage inlineMessage) { - this.inlineMessage = inlineMessage; - } - - } diff --git a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/entity/GraphMeta.java b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/entity/GraphMeta.java index b827e74..0ccefdb 100644 --- a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/entity/GraphMeta.java +++ b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/entity/GraphMeta.java @@ -34,12 +34,8 @@ public class GraphMeta { private int numLinks; private long renderTimeInMs; - - private int numLinksResolvedSuccessfullyFromCache; - - private int numLinksResolvedSuccessfullyFromServer; - - private int numLinkResolveFailed; + + private int numNodeWithProcessingErrors; private EntityTypeAggregation entitySummary; @@ -67,31 +63,6 @@ public class GraphMeta { this.aaiEntityNodeDescriptors = aaiEntityNodeDefinitions; } - public int getNumLinksResolvedSuccessfullyFromCache() { - return numLinksResolvedSuccessfullyFromCache; - } - - public void setNumLinksResolvedSuccessfullyFromCache(int numLinksResolvedSuccessfullyFromCache) { - this.numLinksResolvedSuccessfullyFromCache = numLinksResolvedSuccessfullyFromCache; - } - - public int getNumLinksResolvedSuccessfullyFromServer() { - return numLinksResolvedSuccessfullyFromServer; - } - - public void setNumLinksResolvedSuccessfullyFromServer( - int numLinksResolvedSuccessfullyFromServer) { - this.numLinksResolvedSuccessfullyFromServer = numLinksResolvedSuccessfullyFromServer; - } - - public int getNumLinkResolveFailed() { - return numLinkResolveFailed; - } - - public void setNumLinkResolveFailed(int numLinkResolveFailed) { - this.numLinkResolveFailed = numLinkResolveFailed; - } - public int getNumNodes() { return numNodes; } @@ -116,6 +87,14 @@ public class GraphMeta { this.renderTimeInMs = renderTimeInMs; } + public int getNumNodeWithProcessingErrors() { + return numNodeWithProcessingErrors; + } + + public void setNumNodeWithProcessingErrors(int numNodeWithProcessingErrors) { + this.numNodeWithProcessingErrors = numNodeWithProcessingErrors; + } + /** * Peg counter. * @@ -125,19 +104,12 @@ public class GraphMeta { entitySummary.pegCounter(counterName); } - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ @Override public String toString() { - return "GraphMeta [" - + (aaiEntityNodeDescriptors != null - ? "aaiEntityNodeDescriptors=" + aaiEntityNodeDescriptors + ", " : "") - + "numNodes=" + numNodes + ", numLinks=" + numLinks + ", renderTimeInMs=" + renderTimeInMs - + ", numLinksResolvedSuccessfullyFromCache=" + numLinksResolvedSuccessfullyFromCache - + ", numLinksResolvedSuccessfullyFromServer=" + numLinksResolvedSuccessfullyFromServer - + ", numLinkResolveFailed=" + numLinkResolveFailed + ", " - + (entitySummary != null ? "entitySummary=" + entitySummary : "") + "]"; + return "GraphMeta [aaiEntityNodeDescriptors=" + aaiEntityNodeDescriptors + ", numNodes=" + + numNodes + ", numLinks=" + numLinks + ", renderTimeInMs=" + renderTimeInMs + + ", numNodeWithProcessingErrors=" + numNodeWithProcessingErrors + ", entitySummary=" + + entitySummary + "]"; } } diff --git a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/entity/InlineMessage.java b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/entity/InlineMessage.java deleted file mode 100644 index 5ddc20f..0000000 --- a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/entity/InlineMessage.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 Amdocs - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ -package org.onap.aai.sparky.viewandinspect.entity; - -/** - * The Class InlineMessage. - */ -public class InlineMessage { - - private String level; - private String message; - - /** - * Instantiates a new inline message. - * - * @param level the level - * @param message the message - */ - public InlineMessage(String level, String message) { - this.level = level; - this.message = message; - } - - public String getLevel() { - return level; - } - - public void setLevel(String level) { - this.level = level; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return level + " : " + message; - } - -} diff --git a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/services/BaseGizmoVisualizationContext.java b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/services/BaseGizmoVisualizationContext.java deleted file mode 100644 index 11a771b..0000000 --- a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/services/BaseGizmoVisualizationContext.java +++ /dev/null @@ -1,985 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 Amdocs - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ -package org.onap.aai.sparky.viewandinspect.services; - -import static java.util.concurrent.CompletableFuture.supplyAsync; - -import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.atomic.AtomicInteger; - -import org.onap.aai.cl.api.Logger; -import org.onap.aai.cl.eelf.LoggerFactory; -import org.onap.aai.restclient.client.OperationResult; -import org.onap.aai.sparky.config.oxm.OxmEntityDescriptor; -import org.onap.aai.sparky.config.oxm.OxmEntityLookup; -import org.onap.aai.sparky.dal.GizmoAdapter; -import org.onap.aai.sparky.logging.AaiUiMsgs; -import org.onap.aai.sparky.sync.entity.SearchableEntity; -import org.onap.aai.sparky.util.NodeUtils; -import org.onap.aai.sparky.viewandinspect.config.SparkyConstants; -import org.onap.aai.sparky.viewandinspect.config.VisualizationConfigs; -import org.onap.aai.sparky.viewandinspect.entity.ActiveInventoryNode; -import org.onap.aai.sparky.viewandinspect.entity.GizmoEntity; -import org.onap.aai.sparky.viewandinspect.entity.GizmoRelationshipEntity; -import org.onap.aai.sparky.viewandinspect.entity.GizmoRelationshipHint; -import org.onap.aai.sparky.viewandinspect.entity.InlineMessage; -import org.onap.aai.sparky.viewandinspect.entity.NodeProcessingTransaction; -import org.onap.aai.sparky.viewandinspect.entity.QueryParams; -import org.onap.aai.sparky.viewandinspect.enumeration.NodeProcessingAction; -import org.onap.aai.sparky.viewandinspect.enumeration.NodeProcessingState; -import org.onap.aai.sparky.viewandinspect.task.PerformGizmoNodeSelfLinkProcessingTask; - -import com.fasterxml.jackson.annotation.JsonInclude.Include; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; - -/** - * The Class SelfLinkNodeCollector. - */ -public class BaseGizmoVisualizationContext implements VisualizationContext { - - private static final int MAX_DEPTH_EVALUATION_ATTEMPTS = 100; - - private static final Logger LOG = - LoggerFactory.getInstance().getLogger(BaseGizmoVisualizationContext.class); - - private final GizmoAdapter gizmoAdapter; - - private AtomicInteger numLinksDiscovered; - private AtomicInteger numSuccessfulLinkResolveFromCache; - private AtomicInteger numSuccessfulLinkResolveFromFromServer; - private AtomicInteger numFailedLinkResolve; - private AtomicInteger aaiWorkOnHand; - - private VisualizationConfigs visualizationConfigs; - - private AtomicInteger totalLinksRetrieved; - - private final long contextId; - private final String contextIdStr; - private long lastProcessStatesSummaryLogInMs = -1; - - - private ObjectMapper mapper; - private InlineMessage inlineMessage = null; - - private ExecutorService graphExecutorService; - private OxmEntityLookup oxmEntityLookup; - private boolean rootNodeFound; - - /* - * The node cache is intended to be a flat structure indexed by a primary key to avoid needlessly - * re-requesting the same self-links over-and-over again, to speed up the overall render time and - * more importantly to reduce the network cost of determining information we already have. - */ - private ConcurrentHashMap nodeCache; - - /** - * Instantiates a new self link node collector. - * - * @param loader the loader - * @throws Exception the exception - */ - public BaseGizmoVisualizationContext(long contextId, GizmoAdapter gizmoAdapter, - ExecutorService graphExecutorService, VisualizationConfigs visualizationConfigs, - OxmEntityLookup oxmEntityLookup) throws Exception { - - this.contextId = contextId; - this.contextIdStr = "[Context-Id=" + contextId + "]"; - this.gizmoAdapter = gizmoAdapter; - this.graphExecutorService = graphExecutorService; - this.visualizationConfigs = visualizationConfigs; - this.oxmEntityLookup = oxmEntityLookup; - - this.nodeCache = new ConcurrentHashMap(); - this.numLinksDiscovered = new AtomicInteger(0); - this.totalLinksRetrieved = new AtomicInteger(0); - this.numSuccessfulLinkResolveFromCache = new AtomicInteger(0); - this.numSuccessfulLinkResolveFromFromServer = new AtomicInteger(0); - this.numFailedLinkResolve = new AtomicInteger(0); - this.aaiWorkOnHand = new AtomicInteger(0); - - this.mapper = new ObjectMapper(); - mapper.setSerializationInclusion(Include.NON_EMPTY); - mapper.setPropertyNamingStrategy(new PropertyNamingStrategy.KebabCaseStrategy()); - this.rootNodeFound = false; - } - - protected boolean isRootNodeFound() { - return rootNodeFound; - } - - protected void setRootNodeFound(boolean rootNodeFound) { - this.rootNodeFound = rootNodeFound; - } - - public long getContextId() { - return contextId; - } - - public GizmoAdapter getGizmoAdapter() { - return gizmoAdapter; - } - - /** - * Process self link response. - * - * @param nodeId the node id - */ - private void processSelfLinkResponse(String nodeId) { - - if (nodeId == null) { - LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, - "Cannot process self link" + " response because nodeId is null"); - return; - } - - ActiveInventoryNode ain = nodeCache.get(nodeId); - - if (ain == null) { - LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, - "Cannot process self link response" + " because can't find node for id = " + nodeId); - return; - } - - GizmoEntity gizmoEntity = null; - - try { - gizmoEntity = mapper.readValue(ain.getOpResult().getResult(), GizmoEntity.class); - } catch (Exception exc) { - exc.printStackTrace(); - LOG.error(AaiUiMsgs.SELF_LINK_JSON_PARSE_ERROR, "Failed to marshal json" - + " response str into JsonNode with error, " + exc.getLocalizedMessage()); - ain.changeState(NodeProcessingState.ERROR, - NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_ERROR); - return; - } - - if (gizmoEntity == null) { - - LOG.error(AaiUiMsgs.SELF_LINK_JSON_PARSE_ERROR, - "Failed to parse json node str." + " Parse resulted a null value."); - ain.changeState(NodeProcessingState.ERROR, - NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_ERROR); - return; - } - - /* - * Now that we have the gizmo entity we can populate the AIN node with it, as well as the - * relationships - */ - - ain.setEntityType(gizmoEntity.getType()); - - ain.setPrimaryKeyName(getEntityTypePrimaryKeyName(gizmoEntity.getType())); - - OxmEntityDescriptor descriptor = oxmEntityLookup.getEntityDescriptors().get(gizmoEntity.getType()); - - if (descriptor != null) { - ain.setPrimaryKeyValue(getPrimaryKeyValues(gizmoEntity.getProperties(), - descriptor.getPrimaryKeyAttributeNames())); - } else { - LOG.error(AaiUiMsgs.ERROR_GENERIC, "Could not determine oxm descriptor for entity type = " + gizmoEntity.getType()); - } - - gizmoEntity.getProperties().forEach((key, value) -> { - ain.getProperties().put(key, value); - }); - - // add edit attributes link - if (ain.getSelfLink() != null) { - ain.addProperty(SparkyConstants.URI_ATTR_NAME, ain.getSelfLink()); - } - - /* - * Only discover neighbors if our depth is less than the Max-Traversal-Depth - */ - - if (ain.getNodeDepth() < this.visualizationConfigs.getMaxSelfLinkTraversalDepth()) { - - /* - * I think the next thing to do is: - * - * 1. Calculate the source / target node id 2. Add the nodeId to the incoming / outgoing links - * collection 3. Add the node to the node cache for processing - */ - - String resourceLink = null; - String relationshipNodeId = null; - ActiveInventoryNode relationshipNode = null; - - for (GizmoRelationshipHint inRelationship : gizmoEntity.getIn()) { - - if (inRelationship.getSource() != null) { - - resourceLink = NodeUtils.extractRawGizmoPathWithoutVersion(inRelationship.getSource()); - relationshipNodeId = NodeUtils.generateUniqueShaDigest(resourceLink); - - if (!nodeCache.containsKey(relationshipNodeId)) { - - relationshipNode = new ActiveInventoryNode(visualizationConfigs, oxmEntityLookup); - relationshipNode.setNodeId(relationshipNodeId); - relationshipNode.setSelfLink(resourceLink); - relationshipNode.changeState(NodeProcessingState.SELF_LINK_UNRESOLVED, - NodeProcessingAction.NEW_NODE_PROCESSED); - - ain.addInboundNeighbor(relationshipNodeId); - - addNode(relationshipNode); - - } - } - - } - - for (GizmoRelationshipHint outRelationship : gizmoEntity.getOut()) { - - if (outRelationship.getTarget() != null) { - - resourceLink = NodeUtils.extractRawGizmoPathWithoutVersion(outRelationship.getTarget()); - relationshipNodeId = NodeUtils.generateUniqueShaDigest(resourceLink); - - if (!nodeCache.containsKey(relationshipNodeId)) { - - relationshipNode = new ActiveInventoryNode(visualizationConfigs, oxmEntityLookup); - relationshipNode.setNodeId(relationshipNodeId); - relationshipNode.setSelfLink(resourceLink); - relationshipNode.changeState(NodeProcessingState.SELF_LINK_UNRESOLVED, - NodeProcessingAction.NEW_NODE_PROCESSED); - - ain.addOutboundNeighbor(relationshipNodeId); - - addNode(relationshipNode); - - } - } - - } - } - - ain.changeState(NodeProcessingState.READY, NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_OK); - - } - - /** - * Perform self link resolve. - * - * @param nodeId the node id - */ - private void performSelfLinkResolve(String nodeId) { - - if (nodeId == null) { - LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, - "Resolve of self-link" + " has been skipped because provided nodeId is null"); - return; - } - - ActiveInventoryNode ain = nodeCache.get(nodeId); - - if (ain == null) { - LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, "Failed to find node with id, " + nodeId - + ", from node cache. Resolve self-link method has been skipped."); - return; - } - - if (!ain.isSelfLinkPendingResolve()) { - - ain.setSelfLinkPendingResolve(true); - - // kick off async self-link resolution - - if (LOG.isDebugEnabled()) { - LOG.debug(AaiUiMsgs.DEBUG_GENERIC, - "About to process node in SELF_LINK_UNPROCESSED State, link = " + ain.getSelfLink()); - } - - numLinksDiscovered.incrementAndGet(); - - /* - * If the current node is the search target, we want to see everything the node has to offer - * from the self-link and not filter it to a single node. - */ - - NodeProcessingTransaction txn = new NodeProcessingTransaction(); - txn.setProcessingNode(ain); - txn.setRequestParameters(null); - aaiWorkOnHand.incrementAndGet(); - supplyAsync(new PerformGizmoNodeSelfLinkProcessingTask(txn, null, gizmoAdapter), - graphExecutorService).whenComplete((nodeTxn, error) -> { - - if (error != null) { - - /* - * an error processing the self link should probably result in the node processing - * state shifting to ERROR - */ - - nodeTxn.getProcessingNode().setSelflinkRetrievalFailure(true); - - nodeTxn.getProcessingNode().changeState(NodeProcessingState.ERROR, - NodeProcessingAction.SELF_LINK_RESOLVE_ERROR); - - nodeTxn.getProcessingNode().setSelfLinkPendingResolve(false); - - } else { - - totalLinksRetrieved.incrementAndGet(); - - OperationResult opResult = nodeTxn.getOpResult(); - - if (opResult != null && opResult.wasSuccessful()) { - - if (!opResult.wasSuccessful()) { - numFailedLinkResolve.incrementAndGet(); - } - - if (opResult.isFromCache()) { - numSuccessfulLinkResolveFromCache.incrementAndGet(); - } else { - numSuccessfulLinkResolveFromFromServer.incrementAndGet(); - } - - // success path - nodeTxn.getProcessingNode().setOpResult(opResult); - nodeTxn.getProcessingNode().changeState( - NodeProcessingState.SELF_LINK_RESPONSE_UNPROCESSED, - NodeProcessingAction.SELF_LINK_RESOLVE_OK); - - nodeTxn.getProcessingNode().setSelfLinkProcessed(true); - nodeTxn.getProcessingNode().setSelfLinkPendingResolve(false); - - } else { - LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, - "Self Link retrieval for link," + txn.getSelfLinkWithModifiers() - + ", failed with error code," + nodeTxn.getOpResult().getResultCode() - + ", and message," + nodeTxn.getOpResult().getResult()); - - nodeTxn.getProcessingNode().setSelflinkRetrievalFailure(true); - nodeTxn.getProcessingNode().setSelfLinkProcessed(true); - - nodeTxn.getProcessingNode().changeState(NodeProcessingState.ERROR, - NodeProcessingAction.SELF_LINK_RESOLVE_ERROR); - - nodeTxn.getProcessingNode().setSelfLinkPendingResolve(false); - - } - } - - aaiWorkOnHand.decrementAndGet(); - - }); - - } - - } - - public GizmoRelationshipEntity getGizmoRelationshipEntity(String gizmoJsonResponse) { - - GizmoRelationshipEntity gizmoRelationship = null; - try { - gizmoRelationship = mapper.readValue(gizmoJsonResponse, GizmoRelationshipEntity.class); - } catch (IOException exc) { - LOG.error(AaiUiMsgs.ERROR_GENERIC, "Failed to map json to GizmoRelationshipEntity. Error: " + exc.getMessage()); - } - - return gizmoRelationship; - - } - - public String getPrimaryKeyValues(Map properties, List pkeyNames) { - - StringBuilder sb = new StringBuilder(64); - - if (pkeyNames.size() > 0) { - String primaryKey = properties.get(pkeyNames.get(0)); - if (primaryKey != null) { - sb.append(primaryKey); - } else { - // this should be a fatal error because unless we can - // successfully retrieve all the expected keys we'll end up - // with a garbage node - LOG.error(AaiUiMsgs.EXTRACTION_ERROR, "ERROR: Failed to extract" + " keyName, " - + pkeyNames.get(0) + ", from properties , " + properties); - return null; - } - - for (int i = 1; i < pkeyNames.size(); i++) { - - String kv = properties.get(pkeyNames.get(i)); - if (kv != null) { - sb.append("/").append(kv); - } else { - // this should be a fatal error because unless we can - // successfully retrieve all the expected keys we'll end up - // with a garbage node - LOG.error(AaiUiMsgs.EXTRACTION_ERROR, "ERROR: failed to extract keyName, " - + pkeyNames.get(i) + ", from properties, " + properties); - return null; - } - } - - return sb.toString(); - - } - - return null; - - } - - - - /** - * Find and mark root node. - * - * @param queryParams the query params - * @return true, if successful - */ - private void findAndMarkRootNode(QueryParams queryParams) { - - if (isRootNodeFound()) { - return; - } - - for (ActiveInventoryNode cacheNode : nodeCache.values()) { - - if (queryParams.getSearchTargetNodeId().equals(cacheNode.getNodeId())) { - cacheNode.setNodeDepth(0); - cacheNode.setRootNode(true); - LOG.info(AaiUiMsgs.ROOT_NODE_DISCOVERED, queryParams.getSearchTargetNodeId()); - setRootNodeFound(true); - } - } - - } - - public void addNode(ActiveInventoryNode node) { - - if (node == null) { - return; - } - - nodeCache.putIfAbsent(node.getNodeId(), node); - } - - public VisualizationConfigs getVisualizationConfigs() { - return visualizationConfigs; - } - - public void setVisualizationConfigs(VisualizationConfigs visualizationConfigs) { - this.visualizationConfigs = visualizationConfigs; - } - - public OxmEntityLookup getOxmEntityLookup() { - return oxmEntityLookup; - } - - public void setOxmEntityLookup(OxmEntityLookup oxmEntityLookup) { - this.oxmEntityLookup = oxmEntityLookup; - } - - public ObjectMapper getMapper() { - return mapper; - } - - public void setMapper(ObjectMapper mapper) { - this.mapper = mapper; - } - - private void dumpThrottledWorkOnHandLog() { - dumpThrottledWorkOnHandLog(false); - } - - private void dumpThrottledWorkOnHandLog(boolean override) { - - if ((lastProcessStatesSummaryLogInMs < 0) - || ((System.currentTimeMillis() > (lastProcessStatesSummaryLogInMs + 5000))) || override) { - - lastProcessStatesSummaryLogInMs = System.currentTimeMillis(); - - int numInit = 0; - int numReady = 0; - int numError = 0; - int numSelfLinkUnresolved = 0; - int numSelfLinkResponseUnprocessed = 0; - - for (ActiveInventoryNode cacheNode : nodeCache.values()) { - - switch (cacheNode.getState()) { - - case INIT: { - numInit++; - break; - } - - case READY: { - numReady++; - break; - } - case ERROR: { - numError++; - break; - } - - case SELF_LINK_UNRESOLVED: { - numSelfLinkUnresolved++; - break; - } - - case SELF_LINK_RESPONSE_UNPROCESSED: { - numSelfLinkResponseUnprocessed++; - break; - } - - default: - break; - } - - } - - LOG.info(AaiUiMsgs.INFO_GENERIC, - String.format( - "ProcessCurrentStates for ContextId=%s, [PendingTxns=%d, numInit=%d, numSelfLinkUnresolved=%d, numSelfLinkResponseUnProcessed=%d, numReady=%d, numError=%d]", - contextIdStr, aaiWorkOnHand.get(), numInit, numSelfLinkUnresolved, numSelfLinkResponseUnprocessed, - numReady, numError)); - } - - } - - /** - * Process current node states. - * - * @param rootNodeDiscovered the root node discovered - */ - private void processCurrentNodeStates(QueryParams queryParams) { - /* - * Force an evaluation of node depths before determining if we should limit state-based - * traversal or processing. - */ - - findAndMarkRootNode(queryParams); - - verifyOutboundNeighbors(); - - for (ActiveInventoryNode cacheNode : nodeCache.values()) { - - if (LOG.isDebugEnabled()) { - LOG.debug(AaiUiMsgs.DEBUG_GENERIC, "processCurrentNodeState(), nid = " - + cacheNode.getNodeId() + " , nodeDepth = " + cacheNode.getNodeDepth()); - } - - switch (cacheNode.getState()) { - - case INIT: { - processInitialState(cacheNode.getNodeId()); - break; - } - - case READY: - case ERROR: { - break; - } - - case SELF_LINK_UNRESOLVED: { - performSelfLinkResolve(cacheNode.getNodeId()); - break; - } - - case SELF_LINK_RESPONSE_UNPROCESSED: { - processSelfLinkResponse(cacheNode.getNodeId()); - break; - } - - default: - break; - } - - } - - dumpThrottledWorkOnHandLog(); - - } - - - - public int getNumSuccessfulLinkResolveFromCache() { - return numSuccessfulLinkResolveFromCache.get(); - } - - public int getNumSuccessfulLinkResolveFromFromServer() { - return numSuccessfulLinkResolveFromFromServer.get(); - } - - public int getNumFailedLinkResolve() { - return numFailedLinkResolve.get(); - } - - public InlineMessage getInlineMessage() { - return inlineMessage; - } - - public void setInlineMessage(InlineMessage inlineMessage) { - this.inlineMessage = inlineMessage; - } - - public ConcurrentHashMap getNodeCache() { - return nodeCache; - } - - - - /** - * Process initial state. - * - * @param nodeId the node id - */ - private void processInitialState(String nodeId) { - - if (nodeId == null) { - LOG.error(AaiUiMsgs.FAILED_TO_PROCESS_INITIAL_STATE, "Node id is null"); - return; - } - - ActiveInventoryNode cachedNode = nodeCache.get(nodeId); - - if (cachedNode == null) { - LOG.error(AaiUiMsgs.FAILED_TO_PROCESS_INITIAL_STATE, - "Node cannot be" + " found for nodeId, " + nodeId); - return; - } - - if (cachedNode.getSelfLink() == null) { - - if (cachedNode.getNodeId() == null) { - - /* - * if the self link is null at the INIT state, which could be valid if this node is a - * complex attribute group which didn't originate from a self-link, but in that situation - * both the node id and node key should already be set. - */ - - cachedNode.changeState(NodeProcessingState.ERROR, NodeProcessingAction.NODE_IDENTITY_ERROR); - - } - - if (cachedNode.getNodeId() != null) { - - /* - * This should be the success path branch if the self-link is not set - */ - - cachedNode.changeState(NodeProcessingState.SELF_LINK_RESPONSE_UNPROCESSED, - NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_OK); - - } - - } else { - - if (cachedNode.hasResolvedSelfLink()) { - LOG.error(AaiUiMsgs.INVALID_RESOLVE_STATE_DURING_INIT); - cachedNode.changeState(NodeProcessingState.ERROR, - NodeProcessingAction.UNEXPECTED_STATE_TRANSITION); - } else { - cachedNode.changeState(NodeProcessingState.SELF_LINK_UNRESOLVED, - NodeProcessingAction.SELF_LINK_SET); - } - } - } - - /** - * Process skeleton node. - * - * @param skeletonNode the skeleton node - * @param queryParams the query params - */ - private void processSearchableEntity(SearchableEntity searchTargetEntity, - QueryParams queryParams) { - - if (searchTargetEntity == null) { - return; - } - - if (searchTargetEntity.getId() == null) { - LOG.error(AaiUiMsgs.FAILED_TO_PROCESS_SKELETON_NODE, "Failed to process skeleton" - + " node because nodeId is null for node, " + searchTargetEntity.getLink()); - return; - } - - ActiveInventoryNode newNode = - new ActiveInventoryNode(this.visualizationConfigs, oxmEntityLookup); - - newNode.setNodeId(searchTargetEntity.getId()); - - newNode.setNodeDepth(0); - newNode.setRootNode(true); - LOG.info(AaiUiMsgs.ROOT_NODE_DISCOVERED, queryParams.getSearchTargetNodeId()); - setRootNodeFound(true); - - newNode.setSelfLink(searchTargetEntity.getLink()); - - nodeCache.putIfAbsent(newNode.getNodeId(), newNode); - } - - private int getTotalWorkOnHand() { - - int numNodesWithPendingStates = 0; - - if (isRootNodeFound()) { - evaluateNodeDepths(); - } - - for (ActiveInventoryNode n : nodeCache.values()) { - - switch (n.getState()) { - - case READY: - case ERROR: { - // do nothing, these are our normal - // exit states - break; - } - - default: { - - /* - * for all other states, there is work to be done - */ - numNodesWithPendingStates++; - } - - } - - } - - return (aaiWorkOnHand.get() + numNodesWithPendingStates); - - } - - /** - * Checks for out standing work. - * - * @return true, if successful - */ - private void processOutstandingWork(QueryParams queryParams) { - - while (getTotalWorkOnHand() > 0) { - - /* - * Force an evaluation of node depths before determining if we should limit state-based - * traversal or processing. - */ - - processCurrentNodeStates(queryParams); - - try { - Thread.sleep(10); - } catch (InterruptedException exc) { - LOG.error(AaiUiMsgs.PROCESSING_LOOP_INTERUPTED, exc.getMessage()); - Thread.currentThread().interrupt(); - return; - } - - } - - dumpThrottledWorkOnHandLog(true); - - } - - /* - * (non-Javadoc) - * - * @see - * org.onap.aai.sparky.viewandinspect.services.VisualizationContext#processSelfLinks(org.onap.aai. - * sparky.sync.entity.SearchableEntity, org.onap.aai.sparky.viewandinspect.entity.QueryParams) - */ - @Override - public void processSelfLinks(SearchableEntity searchtargetEntity, QueryParams queryParams) { - - try { - - - if (searchtargetEntity == null) { - LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, - contextIdStr + " - Failed to" + " processSelfLinks, searchtargetEntity is null"); - return; - } - - long startTimeInMs = System.currentTimeMillis(); - - processSearchableEntity(searchtargetEntity, queryParams); - - /* - * This method is blocking until we decouple it with a CountDownLatch await condition, and - * make the internal graph processing more event-y. - */ - - processOutstandingWork(queryParams); - - long totalResolveTime = (System.currentTimeMillis() - startTimeInMs); - - long opTime = System.currentTimeMillis() - startTimeInMs; - - LOG.info(AaiUiMsgs.ALL_TRANSACTIONS_RESOLVED, String.valueOf(totalResolveTime), - String.valueOf(totalLinksRetrieved.get()), String.valueOf(opTime)); - - } catch (Exception exc) { - LOG.error(AaiUiMsgs.VISUALIZATION_OUTPUT_ERROR, exc.getMessage()); - } - - } - - /** - * Verify outbound neighbors. - */ - private void verifyOutboundNeighbors() { - - for (ActiveInventoryNode srcNode : nodeCache.values()) { - - for (String targetNodeId : srcNode.getOutboundNeighbors()) { - - ActiveInventoryNode targetNode = nodeCache.get(targetNodeId); - - if (targetNode != null && srcNode.getNodeId() != null) { - - targetNode.addInboundNeighbor(srcNode.getNodeId()); - - if (this.visualizationConfigs.makeAllNeighborsBidirectional()) { - targetNode.addOutboundNeighbor(srcNode.getNodeId()); - } - - } - - } - - } - - } - - /** - * Evaluate node depths. - */ - private void evaluateNodeDepths() { - - int numChanged = -1; - int numAttempts = 0; - - while (numChanged != 0) { - - numChanged = 0; - numAttempts++; - - for (ActiveInventoryNode srcNode : nodeCache.values()) { - - if (srcNode.getState() == NodeProcessingState.INIT) { - - /* - * this maybe the only state that we don't want to to process the node depth on, because - * typically it won't have any valid fields set, and it may remain in a partial state - * until we have processed the self-link. - */ - - continue; - - } - - for (String targetNodeId : srcNode.getOutboundNeighbors()) { - ActiveInventoryNode targetNode = nodeCache.get(targetNodeId); - - if (targetNode != null) { - - if (targetNode.changeDepth(srcNode.getNodeDepth() + 1)) { - numChanged++; - } - } - } - - for (String targetNodeId : srcNode.getInboundNeighbors()) { - ActiveInventoryNode targetNode = nodeCache.get(targetNodeId); - - if (targetNode != null) { - - if (targetNode.changeDepth(srcNode.getNodeDepth() + 1)) { - numChanged++; - } - } - } - } - - if (numAttempts >= MAX_DEPTH_EVALUATION_ATTEMPTS) { - LOG.info(AaiUiMsgs.MAX_EVALUATION_ATTEMPTS_EXCEEDED); - return; - } - - } - - if (LOG.isDebugEnabled()) { - if (numAttempts > 0) { - LOG.debug(AaiUiMsgs.DEBUG_GENERIC, - "Evaluate node depths completed in " + numAttempts + " attempts"); - } else { - LOG.debug(AaiUiMsgs.DEBUG_GENERIC, - "Evaluate node depths completed in 0 attempts because all nodes at correct depth"); - } - } - - } - - - /** - * Gets the entity type primary key name. - * - * @param entityType the entity type - * @return the entity type primary key name - */ - - - private String getEntityTypePrimaryKeyName(String entityType) { - - if (entityType == null) { - LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE, - "node primary key" + " name because entity type is null"); - return null; - } - - OxmEntityDescriptor descriptor = oxmEntityLookup.getEntityDescriptors().get(entityType); - - if (descriptor == null) { - LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE, - "oxm entity" + " descriptor for entityType = " + entityType); - return null; - } - - List pkeyNames = descriptor.getPrimaryKeyAttributeNames(); - - if (pkeyNames == null || pkeyNames.size() == 0) { - LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE, - "node primary" + " key because descriptor primary key names is empty"); - return null; - } - - return NodeUtils.concatArray(pkeyNames, "/"); - - } - -} diff --git a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/services/BaseVisualizationContext.java b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/services/BaseVisualizationContext.java deleted file mode 100644 index 0cbd52e..0000000 --- a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/services/BaseVisualizationContext.java +++ /dev/null @@ -1,1617 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 Amdocs - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ -package org.onap.aai.sparky.viewandinspect.services; - -import static java.util.concurrent.CompletableFuture.supplyAsync; - -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.atomic.AtomicInteger; - -import org.apache.http.client.utils.URIBuilder; -import org.onap.aai.cl.api.Logger; -import org.onap.aai.cl.eelf.LoggerFactory; -import org.onap.aai.restclient.client.OperationResult; -import org.onap.aai.sparky.config.oxm.OxmEntityDescriptor; -import org.onap.aai.sparky.config.oxm.OxmEntityLookup; -import org.onap.aai.sparky.dal.ActiveInventoryAdapter; -import org.onap.aai.sparky.logging.AaiUiMsgs; -import org.onap.aai.sparky.sync.entity.SearchableEntity; -import org.onap.aai.sparky.util.NodeUtils; -import org.onap.aai.sparky.viewandinspect.config.SparkyConstants; -import org.onap.aai.sparky.viewandinspect.config.VisualizationConfigs; -import org.onap.aai.sparky.viewandinspect.entity.ActiveInventoryNode; -import org.onap.aai.sparky.viewandinspect.entity.InlineMessage; -import org.onap.aai.sparky.viewandinspect.entity.NodeProcessingTransaction; -import org.onap.aai.sparky.viewandinspect.entity.QueryParams; -import org.onap.aai.sparky.viewandinspect.entity.Relationship; -import org.onap.aai.sparky.viewandinspect.entity.RelationshipData; -import org.onap.aai.sparky.viewandinspect.entity.RelationshipList; -import org.onap.aai.sparky.viewandinspect.entity.SelfLinkDeterminationTransaction; -import org.onap.aai.sparky.viewandinspect.enumeration.NodeProcessingAction; -import org.onap.aai.sparky.viewandinspect.enumeration.NodeProcessingState; -import org.onap.aai.sparky.viewandinspect.task.PerformNodeSelfLinkProcessingTask; -import org.onap.aai.sparky.viewandinspect.task.PerformSelfLinkDeterminationTask; - -import com.fasterxml.jackson.annotation.JsonInclude.Include; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; - -/** - * The Class SelfLinkNodeCollector. - */ -public class BaseVisualizationContext implements VisualizationContext { - - private static final int MAX_DEPTH_EVALUATION_ATTEMPTS = 100; - private static final String DEPTH_ALL_MODIFIER = "?depth=all"; - private static final String NODES_ONLY_MODIFIER = "?nodes-only"; - private static final String SERVICE_INSTANCE = "service-instance"; - - private static final Logger LOG = LoggerFactory.getInstance().getLogger( - BaseVisualizationContext.class); - private final ActiveInventoryAdapter aaiAdapter; - - private int maxSelfLinkTraversalDepth; - private AtomicInteger numLinksDiscovered; - private AtomicInteger numSuccessfulLinkResolveFromCache; - private AtomicInteger numSuccessfulLinkResolveFromFromServer; - private AtomicInteger numFailedLinkResolve; - private AtomicInteger aaiWorkOnHand; - - private VisualizationConfigs visualizationConfigs; - - private AtomicInteger totalLinksRetrieved; - - private final long contextId; - private final String contextIdStr; - - private ObjectMapper mapper; - private InlineMessage inlineMessage = null; - - private ExecutorService aaiExecutorService; - private OxmEntityLookup oxmEntityLookup; - private boolean rootNodeFound; - - /* - * The node cache is intended to be a flat structure indexed by a primary key to avoid needlessly - * re-requesting the same self-links over-and-over again, to speed up the overall render time and - * more importantly to reduce the network cost of determining information we already have. - */ - private ConcurrentHashMap nodeCache; - - /** - * Instantiates a new self link node collector. - * - * @param loader the loader - * @throws Exception the exception - */ - public BaseVisualizationContext(long contextId, ActiveInventoryAdapter aaiAdapter, - ExecutorService aaiExecutorService, VisualizationConfigs visualizationConfigs, - OxmEntityLookup oxmEntityLookup) - throws Exception { - - this.contextId = contextId; - this.contextIdStr = "[Context-Id=" + contextId + "]"; - this.aaiAdapter = aaiAdapter; - this.aaiExecutorService = aaiExecutorService; - this.visualizationConfigs = visualizationConfigs; - this.oxmEntityLookup = oxmEntityLookup; - - this.nodeCache = new ConcurrentHashMap(); - this.numLinksDiscovered = new AtomicInteger(0); - this.totalLinksRetrieved = new AtomicInteger(0); - this.numSuccessfulLinkResolveFromCache = new AtomicInteger(0); - this.numSuccessfulLinkResolveFromFromServer = new AtomicInteger(0); - this.numFailedLinkResolve = new AtomicInteger(0); - this.aaiWorkOnHand = new AtomicInteger(0); - - this.maxSelfLinkTraversalDepth = this.visualizationConfigs.getMaxSelfLinkTraversalDepth(); - - this.mapper = new ObjectMapper(); - mapper.setSerializationInclusion(Include.NON_EMPTY); - mapper.setPropertyNamingStrategy(new PropertyNamingStrategy.KebabCaseStrategy()); - this.rootNodeFound = false; - } - - protected boolean isRootNodeFound() { - return rootNodeFound; - } - - protected void setRootNodeFound(boolean rootNodeFound) { - this.rootNodeFound = rootNodeFound; - } - - public long getContextId() { - return contextId; - } - - /** - * A utility method for extracting all entity-type primary key values from a provided self-link - * and return a set of generic-query API keys. - * - * @param parentEntityType - * @param link - * @return a list of key values that can be used for this entity with the AAI generic-query API - */ - protected List extractQueryParamsFromSelfLink(String link) { - - List queryParams = new ArrayList(); - - if (link == null) { - LOG.error(AaiUiMsgs.QUERY_PARAM_EXTRACTION_ERROR, "self link is null"); - return queryParams; - } - - Map entityDescriptors = oxmEntityLookup.getEntityDescriptors(); - - try { - - URIBuilder urlBuilder = new URIBuilder(link); - String urlPath = urlBuilder.getPath(); - - OxmEntityDescriptor descriptor = null; - String[] urlPathElements = urlPath.split("/"); - List primaryKeyNames = null; - int index = 0; - String entityType = null; - - while (index < urlPathElements.length) { - - descriptor = entityDescriptors.get(urlPathElements[index]); - - if (descriptor != null) { - entityType = urlPathElements[index]; - primaryKeyNames = descriptor.getPrimaryKeyAttributeNames(); - - /* - * Make sure from what ever index we matched the parent entity-type on that we can extract - * additional path elements for the primary key values. - */ - - if (index + primaryKeyNames.size() < urlPathElements.length) { - - for (String primaryKeyName : primaryKeyNames) { - index++; - queryParams.add(entityType + "." + primaryKeyName + ":" + urlPathElements[index]); - } - } else { - LOG.error(AaiUiMsgs.QUERY_PARAM_EXTRACTION_ERROR, - "Could not extract query parametrs for entity-type = '" + entityType - + "' from self-link = " + link); - } - } - - index++; - } - - } catch (URISyntaxException exc) { - - LOG.error(AaiUiMsgs.QUERY_PARAM_EXTRACTION_ERROR, - "Error extracting query parameters from self-link = " + link + ". Error = " - + exc.getMessage()); - } - - return queryParams; - - } - - /** - * Decode complex attribute group. - * - * @param ain the ain - * @param attributeGroup the attribute group - * @return boolean indicating whether operation was successful (true), / failure(false). - */ - public boolean decodeComplexAttributeGroup(ActiveInventoryNode ain, JsonNode attributeGroup) { - - try { - - Iterator> entityArrays = attributeGroup.fields(); - Entry entityArray = null; - - if (entityArrays == null) { - LOG.error(AaiUiMsgs.ATTRIBUTE_GROUP_FAILURE, attributeGroup.toString()); - ain.changeState(NodeProcessingState.ERROR, NodeProcessingAction.NEIGHBORS_PROCESSED_ERROR); - return false; - } - - while (entityArrays.hasNext()) { - - entityArray = entityArrays.next(); - - String entityType = entityArray.getKey(); - JsonNode entityArrayObject = entityArray.getValue(); - - if (entityArrayObject.isArray()) { - - Iterator entityCollection = entityArrayObject.elements(); - JsonNode entity = null; - while (entityCollection.hasNext()) { - entity = entityCollection.next(); - - if (LOG.isDebugEnabled()) { - LOG.debug(AaiUiMsgs.DEBUG_GENERIC, "decodeComplexAttributeGroup()," - + " entity = " + entity.toString()); - } - - /** - * Here's what we are going to do: - * - *
  • In the ActiveInventoryNode, on construction maintain a collection of queryParams - * that is added to for the purpose of discovering parent->child hierarchies. - * - *
  • When we hit this block of the code then we'll use the queryParams to feed the - * generic query to resolve the self-link asynchronously. - * - *
  • Upon successful link determination, then and only then will we create a new node - * in the nodeCache and process the child - * - */ - - ActiveInventoryNode newNode = new ActiveInventoryNode(this.visualizationConfigs, oxmEntityLookup); - newNode.setEntityType(entityType); - - /* - * This is partially a lie because we actually don't have a self-link for complex nodes - * discovered in this way. - */ - newNode.setSelfLinkProcessed(true); - newNode.changeState(NodeProcessingState.SELF_LINK_RESPONSE_UNPROCESSED, - NodeProcessingAction.COMPLEX_ATTRIBUTE_GROUP_PARSE_OK); - - /* - * copy parent query params into new child - */ - - if (SERVICE_INSTANCE.equals(entityType)) { - - /* - * 1707 AAI has an issue being tracked with AAI-8932 where the generic-query cannot be - * resolved if all the service-instance path keys are provided. The query only works - * if only the service-instance key and valude are passed due to a historical reason. - * A fix is being worked on for 1707, and when it becomes available we can revert this - * small change. - */ - - newNode.clearQueryParams(); - - } else { - - /* - * For all other entity-types we want to copy the parent query parameters into the new node - * query parameters. - */ - - for (String queryParam : ain.getQueryParams()) { - newNode.addQueryParam(queryParam); - } - - } - - - if (!addComplexGroupToNode(newNode, entity)) { - LOG.error(AaiUiMsgs.ATTRIBUTE_GROUP_FAILURE, "Failed to add child to parent for child = " + entity.toString()); - } - - if (!addNodeQueryParams(newNode)) { - LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE_NODE_ID, "Error determining node id and key for node = " + newNode.dumpNodeTree(true) - + " skipping relationship processing"); - newNode.changeState(NodeProcessingState.ERROR, - NodeProcessingAction.NODE_IDENTITY_ERROR); - return false; - } else { - - newNode.changeState(NodeProcessingState.NEIGHBORS_UNPROCESSED, - NodeProcessingAction.COMPLEX_ATTRIBUTE_GROUP_PARSE_OK); - - } - - - /* - * Order matters for the query params. We need to set the parent ones before the child - * node - */ - - String selfLinkQuery = - aaiAdapter.getGenericQueryForSelfLink(entityType, newNode.getQueryParams()); - - /** - *
  • get the self-link - *
  • add it to the new node - *
  • generate node id - *
  • add node to node cache - *
  • add node id to parent outbound links list - *
  • process node children (should be automatic) (but don't query and resolve - * self-link as we already have all the data) - */ - - SelfLinkDeterminationTransaction txn = new SelfLinkDeterminationTransaction(); - - txn.setQueryString(selfLinkQuery); - txn.setNewNode(newNode); - txn.setParentNodeId(ain.getNodeId()); - aaiWorkOnHand.incrementAndGet(); - supplyAsync(new PerformSelfLinkDeterminationTask(txn, null, aaiAdapter), - aaiExecutorService).whenComplete((nodeTxn, error) -> { - - if (error != null) { - LOG.error(AaiUiMsgs.SELF_LINK_DETERMINATION_FAILED_GENERIC, selfLinkQuery); - } else { - - OperationResult opResult = nodeTxn.getOpResult(); - - ActiveInventoryNode newChildNode = txn.getNewNode(); - - if (opResult != null && opResult.wasSuccessful()) { - - if (!opResult.wasSuccessful()) { - numFailedLinkResolve.incrementAndGet(); - } - - if (opResult.isFromCache()) { - numSuccessfulLinkResolveFromCache.incrementAndGet(); - } else { - numSuccessfulLinkResolveFromFromServer.incrementAndGet(); - } - - /* - * extract the self-link from the operational result. - */ - - Collection entityLinks = new ArrayList(); - JsonNode genericQueryResult = null; - try { - genericQueryResult = - NodeUtils.convertJsonStrToJsonNode(nodeTxn.getOpResult().getResult()); - } catch (Exception exc) { - LOG.error(AaiUiMsgs.JSON_CONVERSION_ERROR, JsonNode.class.toString(), exc.getMessage()); - } - - NodeUtils.extractObjectsByKey(genericQueryResult, "resource-link", - entityLinks); - - String selfLink = null; - - if (entityLinks.size() != 1) { - - LOG.error(AaiUiMsgs.SELF_LINK_DETERMINATION_FAILED_UNEXPECTED_LINKS, String.valueOf(entityLinks.size())); - - } else { - selfLink = ((JsonNode) entityLinks.toArray()[0]).asText(); - selfLink = ActiveInventoryAdapter.extractResourcePath(selfLink); - - newChildNode.setSelfLink(selfLink); - newChildNode.setNodeId(NodeUtils.generateUniqueShaDigest(selfLink)); - - String uri = NodeUtils.calculateEditAttributeUri(selfLink); - if (uri != null) { - newChildNode.addProperty(SparkyConstants.URI_ATTR_NAME, uri); - } - - ActiveInventoryNode parent = nodeCache.get(txn.getParentNodeId()); - - if (parent != null) { - parent.addOutboundNeighbor(newChildNode.getNodeId()); - newChildNode.addInboundNeighbor(parent.getNodeId()); - } - - newChildNode.setSelfLinkPendingResolve(false); - newChildNode.setSelfLinkProcessed(true); - newChildNode.changeState(NodeProcessingState.NEIGHBORS_UNPROCESSED, - NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_OK); - - nodeCache.putIfAbsent(newChildNode.getNodeId(), newChildNode); - - } - - } else { - LOG.error(AaiUiMsgs.SELF_LINK_RETRIEVAL_FAILED, txn.getQueryString(), - String.valueOf(nodeTxn.getOpResult().getResultCode()), nodeTxn.getOpResult().getResult()); - newChildNode.setSelflinkRetrievalFailure(true); - newChildNode.setSelfLinkProcessed(true); - newChildNode.setSelfLinkPendingResolve(false); - - newChildNode.changeState(NodeProcessingState.ERROR, - NodeProcessingAction.SELF_LINK_DETERMINATION_ERROR); - - } - - } - - aaiWorkOnHand.decrementAndGet(); - - }); - - } - - return true; - - } else { - LOG.error(AaiUiMsgs.UNHANDLED_OBJ_TYPE_FOR_ENTITY_TYPE, entityType); - } - - } - } catch (Exception exc) { - LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, "Exception caught while" - + " decoding complex attribute group - " + exc.getMessage()); - } - - return false; - - } - - /** - * Process self link response. - * - * @param nodeId the node id - */ - private void processSelfLinkResponse(String nodeId) { - - if (nodeId == null) { - LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, "Cannot process self link" - + " response because nodeId is null"); - return; - } - - ActiveInventoryNode ain = nodeCache.get(nodeId); - - if (ain == null) { - LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, "Cannot process self link response" - + " because can't find node for id = " + nodeId); - return; - } - - JsonNode jsonNode = null; - - try { - jsonNode = mapper.readValue(ain.getOpResult().getResult(), JsonNode.class); - } catch (Exception exc) { - LOG.error(AaiUiMsgs.SELF_LINK_JSON_PARSE_ERROR, "Failed to marshal json" - + " response str into JsonNode with error, " + exc.getLocalizedMessage()); - ain.changeState(NodeProcessingState.ERROR, - NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_ERROR); - return; - } - - if (jsonNode == null) { - LOG.error(AaiUiMsgs.SELF_LINK_JSON_PARSE_ERROR, "Failed to parse json node str." - + " Parse resulted a null value."); - ain.changeState(NodeProcessingState.ERROR, - NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_ERROR); - return; - } - - Iterator> fieldNames = jsonNode.fields(); - Entry field = null; - - RelationshipList relationshipList = null; - - while (fieldNames.hasNext()) { - - field = fieldNames.next(); - String fieldName = field.getKey(); - - if ("relationship-list".equals(fieldName)) { - - try { - relationshipList = mapper.readValue(field.getValue().toString(), RelationshipList.class); - - if (relationshipList != null) { - ain.addRelationshipList(relationshipList); - } - - } catch (Exception exc) { - LOG.error(AaiUiMsgs.SELF_LINK_JSON_PARSE_ERROR, "Failed to parse relationship-list" - + " attribute. Parse resulted in error, " + exc.getLocalizedMessage()); - ain.changeState(NodeProcessingState.ERROR, - NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_ERROR); - return; - } - - } else { - - JsonNode nodeValue = field.getValue(); - - if(nodeValue!=null) { - if (nodeValue.isValueNode()) { - String key = fieldName; - handleNodeValue(ain, fieldName, key, nodeValue.asText()); - } else if (nodeValue.isArray()) { - String key = field.getKey(); - handleNodeValue(ain, fieldName, key, nodeValue.toString()); - } else { - ain.addComplexGroup(nodeValue); - } - - } - } - - } - - String uri = NodeUtils.calculateEditAttributeUri(ain.getSelfLink()); - if (uri != null) { - ain.addProperty(SparkyConstants.URI_ATTR_NAME, uri); - } - - /* - * We need a special behavior for intermediate entities from the REST model - * - * Tenants are not top level entities, and when we want to visualization - * their children, we need to construct keys that include the parent entity query - * keys, the current entity type keys, and the child keys. We'll always have the - * current entity and children, but never the parent entity in the current (1707) REST - * data model. - * - * We have two possible solutions: - * - * 1) Try to use the custom-query approach to learn about the entity keys - * - this could be done, but it could be very expensive for large objects. When we do the first - * query to get a tenant, it will list all the in and out edges related to this entity, - * there is presently no way to filter this. But the approach could be made to work and it would be - * somewhat data-model driven, other than the fact that we have to first realize that the entity - * that is being searched for is not top-level entity. Once we have globally unique ids for resources - * this logic will not be needed and everything will be simpler. The only reason we are in this logic - * at all is to be able to calculate a url for the child entities so we can hash it to generate - * a globally unique id that can be safely used for the node. - * - * *2* Extract the keys from the pathed self-link. - * This is a bad solution and I don't like it but it will be fast for all resource types, as the - * information is already encoded in the URI. When we get to a point where we switch to a better - * globally unique entity identity model, then a lot of the code being used to calculate an entity url - * to in-turn generate a deterministic globally unique id will disappear. - * - * - * right now we have the following: - * - * - cloud-regions/cloud-region/{cloud-region-id}/{cloud-owner-id}/tenants/tenant/{tenant-id} - * - */ - - /* - * For all entity types use the self-link extraction method to be consistent. Once we have a - * globally unique identity mechanism for entities, this logic can be revisited. - */ - ain.clearQueryParams(); - ain.addQueryParams(extractQueryParamsFromSelfLink(ain.getSelfLink())); - ain.changeState(NodeProcessingState.NEIGHBORS_UNPROCESSED, - NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_OK); - - - } - - private void handleNodeValue(ActiveInventoryNode ain, String fieldName, String key, String value) { - if (oxmEntityLookup.getEntityDescriptors().get(fieldName) == null) { - - /* - * entity property name is not an entity, thus we can add this property name and value - * to our property set - */ - - ain.addProperty(key, value); - - } - } - - /** - * Perform self link resolve. - * - * @param nodeId the node id - */ - private void performSelfLinkResolve(String nodeId) { - - if (nodeId == null) { - LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, "Resolve of self-link" - + " has been skipped because provided nodeId is null"); - return; - } - - ActiveInventoryNode ain = nodeCache.get(nodeId); - - if (ain == null) { - LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, "Failed to find node with id, " + nodeId - + ", from node cache. Resolve self-link method has been skipped."); - return; - } - - if (!ain.isSelfLinkPendingResolve()) { - - ain.setSelfLinkPendingResolve(true); - - // kick off async self-link resolution - - if (LOG.isDebugEnabled()) { - LOG.debug(AaiUiMsgs.DEBUG_GENERIC, - "About to process node in SELF_LINK_UNPROCESSED State, link = " + ain.getSelfLink()); - } - - numLinksDiscovered.incrementAndGet(); - - String depthModifier = DEPTH_ALL_MODIFIER; - - /* - * If the current node is the search target, we want to see everything the node has to offer - * from the self-link and not filter it to a single node. - */ - - if (visualizationConfigs.getShallowEntities().contains(ain.getEntityType()) - && !ain.isRootNode()) { - depthModifier = NODES_ONLY_MODIFIER; - } - - NodeProcessingTransaction txn = new NodeProcessingTransaction(); - txn.setProcessingNode(ain); - txn.setRequestParameters(depthModifier); - aaiWorkOnHand.incrementAndGet(); - supplyAsync( - new PerformNodeSelfLinkProcessingTask(txn, depthModifier, aaiAdapter), - aaiExecutorService).whenComplete((nodeTxn, error) -> { - - if (error != null) { - - /* - * an error processing the self link should probably result in the node processing - * state shifting to ERROR - */ - - nodeTxn.getProcessingNode().setSelflinkRetrievalFailure(true); - - nodeTxn.getProcessingNode().changeState(NodeProcessingState.ERROR, - NodeProcessingAction.SELF_LINK_RESOLVE_ERROR); - - nodeTxn.getProcessingNode().setSelfLinkPendingResolve(false); - - } else { - - totalLinksRetrieved.incrementAndGet(); - - OperationResult opResult = nodeTxn.getOpResult(); - - if (opResult != null && opResult.wasSuccessful()) { - - if (!opResult.wasSuccessful()) { - numFailedLinkResolve.incrementAndGet(); - } - - if (opResult.isFromCache()) { - numSuccessfulLinkResolveFromCache.incrementAndGet(); - } else { - numSuccessfulLinkResolveFromFromServer.incrementAndGet(); - } - - // success path - nodeTxn.getProcessingNode().setOpResult(opResult); - nodeTxn.getProcessingNode().changeState( - NodeProcessingState.SELF_LINK_RESPONSE_UNPROCESSED, - NodeProcessingAction.SELF_LINK_RESOLVE_OK); - - nodeTxn.getProcessingNode().setSelfLinkProcessed(true); - nodeTxn.getProcessingNode().setSelfLinkPendingResolve(false); - - } else { - LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, "Self Link retrieval for link," - + txn.getSelfLinkWithModifiers() + ", failed with error code," - + nodeTxn.getOpResult().getResultCode() + ", and message," - + nodeTxn.getOpResult().getResult()); - - nodeTxn.getProcessingNode().setSelflinkRetrievalFailure(true); - nodeTxn.getProcessingNode().setSelfLinkProcessed(true); - - nodeTxn.getProcessingNode().changeState(NodeProcessingState.ERROR, - NodeProcessingAction.SELF_LINK_RESOLVE_ERROR); - - nodeTxn.getProcessingNode().setSelfLinkPendingResolve(false); - - } - } - - aaiWorkOnHand.decrementAndGet(); - - }); - - } - - } - - - /** - * Process neighbors. - * - * @param nodeId the node id - */ - private void processNeighbors(String nodeId) { - - if (nodeId == null) { - LOG.error(AaiUiMsgs.SELF_LINK_PROCESS_NEIGHBORS_ERROR, "Failed to process" - + " neighbors because nodeId is null."); - return; - } - - ActiveInventoryNode ain = nodeCache.get(nodeId); - - if (ain == null) { - LOG.error(AaiUiMsgs.SELF_LINK_PROCESS_NEIGHBORS_ERROR, "Failed to process" - + " neighbors because node could not be found in nodeCache with id, " + nodeId); - return; - } - - /* - * process complex attribute and relationships - */ - - boolean neighborsProcessedSuccessfully = true; - - for (JsonNode n : ain.getComplexGroups()) { - neighborsProcessedSuccessfully &= decodeComplexAttributeGroup(ain, n); - } - - for (RelationshipList relationshipList : ain.getRelationshipLists()) { - neighborsProcessedSuccessfully &= addSelfLinkRelationshipChildren(ain, relationshipList); - } - - - if (neighborsProcessedSuccessfully) { - ain.changeState(NodeProcessingState.READY, NodeProcessingAction.NEIGHBORS_PROCESSED_OK); - } else { - ain.changeState(NodeProcessingState.ERROR, NodeProcessingAction.NEIGHBORS_PROCESSED_ERROR); - } - - - /* - * If neighbors fail to process, there is already a call to change the state within the - * relationship and neighbor processing functions. - */ - - } - - /** - * Find and mark root node. - * - * @param queryParams the query params - * @return true, if successful - */ - private void findAndMarkRootNode(QueryParams queryParams) { - - if (isRootNodeFound()) { - return; - } - - for (ActiveInventoryNode cacheNode : nodeCache.values()) { - - if (queryParams.getSearchTargetNodeId().equals(cacheNode.getNodeId())) { - cacheNode.setNodeDepth(0); - cacheNode.setRootNode(true); - LOG.info(AaiUiMsgs.ROOT_NODE_DISCOVERED, queryParams.getSearchTargetNodeId()); - setRootNodeFound(true); - } - } - - } - - /** - * Process current node states. - * - */ - private void processCurrentNodeStates(QueryParams queryParams) { - /* - * Force an evaluation of node depths before determining if we should limit state-based - * traversal or processing. - */ - - findAndMarkRootNode(queryParams); - - verifyOutboundNeighbors(); - - for (ActiveInventoryNode cacheNode : nodeCache.values()) { - - if (LOG.isDebugEnabled()) { - LOG.debug(AaiUiMsgs.DEBUG_GENERIC, - "processCurrentNodeState(), nid = " - + cacheNode.getNodeId() + " , nodeDepth = " + cacheNode.getNodeDepth()); - } - - switch (cacheNode.getState()) { - - case INIT: { - processInitialState(cacheNode.getNodeId()); - break; - } - - case READY: - case ERROR: { - break; - } - - case SELF_LINK_UNRESOLVED: { - performSelfLinkResolve(cacheNode.getNodeId()); - break; - } - - case SELF_LINK_RESPONSE_UNPROCESSED: { - processSelfLinkResponse(cacheNode.getNodeId()); - break; - } - - case NEIGHBORS_UNPROCESSED: { - - /* - * We use the rootNodeDiscovered flag to ignore depth retrieval thresholds until the root - * node is identified. Then the evaluative depth calculations should re-balance the graph - * around the root node. - */ - - if (!isRootNodeFound() || cacheNode.getNodeDepth() < this.visualizationConfigs - .getMaxSelfLinkTraversalDepth()) { - - if (LOG.isDebugEnabled()) { - LOG.debug(AaiUiMsgs.DEBUG_GENERIC, - "processCurrentNodeState() -- Node at max depth," - + " halting processing at current state = -- " - + cacheNode.getState() + " nodeId = " + cacheNode.getNodeId()); - } - - processNeighbors(cacheNode.getNodeId()); - - } - - break; - } - default: - break; - } - - } - - } - - /** - * Adds the complex group to node. - * - * @param targetNode the target node - * @param attributeGroup the attribute group - * @return true, if successful - */ - private boolean addComplexGroupToNode(ActiveInventoryNode targetNode, JsonNode attributeGroup) { - - if (attributeGroup == null) { - targetNode.changeState(NodeProcessingState.ERROR, - NodeProcessingAction.COMPLEX_ATTRIBUTE_GROUP_PARSE_OK); - return false; - } - - RelationshipList relationshipList = null; - - if (attributeGroup.isObject()) { - - Iterator> fields = attributeGroup.fields(); - Entry field = null; - String fieldName; - JsonNode fieldValue; - - while (fields.hasNext()) { - field = fields.next(); - fieldName = field.getKey(); - fieldValue = field.getValue(); - - if (fieldValue.isObject()) { - - if (fieldName.equals("relationship-list")) { - - try { - relationshipList = - mapper.readValue(field.getValue().toString(), RelationshipList.class); - - if (relationshipList != null) { - targetNode.addRelationshipList(relationshipList); - } - - } catch (Exception exc) { - LOG.error(AaiUiMsgs.SELF_LINK_JSON_PARSE_ERROR, "Failed to parse" - + " relationship-list attribute. Parse resulted in error, " - + exc.getLocalizedMessage()); - targetNode.changeState(NodeProcessingState.ERROR, - NodeProcessingAction.COMPLEX_ATTRIBUTE_GROUP_PARSE_ERROR); - return false; - } - - } else { - targetNode.addComplexGroup(fieldValue); - } - - } else if (fieldValue.isArray()) { - if (LOG.isDebugEnabled()) { - LOG.debug(AaiUiMsgs.DEBUG_GENERIC, - "Unexpected array type with a key = " + fieldName); - } - } else if (fieldValue.isValueNode()) { - if (oxmEntityLookup.getEntityDescriptors().get(field.getKey()) == null) { - /* - * property key is not an entity type, add it to our property set. - */ - targetNode.addProperty(field.getKey(), fieldValue.asText()); - } - - } - } - - } else if (attributeGroup.isArray()) { - if (LOG.isDebugEnabled()) { - LOG.debug(AaiUiMsgs.DEBUG_GENERIC, - "Unexpected array type for attributeGroup = " + attributeGroup); - } - } else if (attributeGroup.isValueNode()) { - if (LOG.isDebugEnabled()) { - LOG.debug(AaiUiMsgs.DEBUG_GENERIC, - "Unexpected value type for attributeGroup = " + attributeGroup); - } - } - - return true; - } - - public int getNumSuccessfulLinkResolveFromCache() { - return numSuccessfulLinkResolveFromCache.get(); - } - - public int getNumSuccessfulLinkResolveFromFromServer() { - return numSuccessfulLinkResolveFromFromServer.get(); - } - - public int getNumFailedLinkResolve() { - return numFailedLinkResolve.get(); - } - - public InlineMessage getInlineMessage() { - return inlineMessage; - } - - public void setInlineMessage(InlineMessage inlineMessage) { - this.inlineMessage = inlineMessage; - } - - public void setMaxSelfLinkTraversalDepth(int depth) { - this.maxSelfLinkTraversalDepth = depth; - } - - public int getMaxSelfLinkTraversalDepth() { - return this.maxSelfLinkTraversalDepth; - } - - public ConcurrentHashMap getNodeCache() { - return nodeCache; - } - - /** - * Gets the relationship primary key values. - * - * @param r the r - * @param entityType the entity type - * @param pkeyNames the pkey names - * @return the relationship primary key values - */ - private String getRelationshipPrimaryKeyValues(Relationship r, String entityType, - List pkeyNames) { - - StringBuilder sb = new StringBuilder(64); - - if (pkeyNames.size() > 0) { - String primaryKey = extractKeyValueFromRelationData(r, entityType + "." + pkeyNames.get(0)); - if (primaryKey != null) { - - sb.append(primaryKey); - - } else { - // this should be a fatal error because unless we can - // successfully retrieve all the expected keys we'll end up - // with a garbage node - LOG.error(AaiUiMsgs.EXTRACTION_ERROR, "ERROR: Failed to extract" - + " keyName, " + entityType + "." + pkeyNames.get(0) - + ", from relationship data, " + r.toString()); - return null; - } - - for (int i = 1; i < pkeyNames.size(); i++) { - - String kv = extractKeyValueFromRelationData(r, entityType + "." + pkeyNames.get(i)); - if (kv != null) { - sb.append("/").append(kv); - } else { - // this should be a fatal error because unless we can - // successfully retrieve all the expected keys we'll end up - // with a garbage node - LOG.error(AaiUiMsgs.EXTRACTION_ERROR, "ERROR: failed to extract keyName, " - + entityType + "." + pkeyNames.get(i) - + ", from relationship data, " + r.toString()); - return null; - } - } - - return sb.toString(); - - } - - return null; - - } - - /** - * Extract key value from relation data. - * - * @param r the r - * @param keyName the key name - * @return the string - */ - private String extractKeyValueFromRelationData(Relationship r, String keyName) { - - RelationshipData[] rdList = r.getRelationshipData(); - - for (RelationshipData relData : rdList) { - - if (relData.getRelationshipKey().equals(keyName)) { - return relData.getRelationshipValue(); - } - } - - return null; - } - - /** - * Determine node id and key. - * - * @param ain the ain - * @return true, if successful - */ - private boolean addNodeQueryParams(ActiveInventoryNode ain) { - - if (ain == null) { - LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE_NODE_ID, "ActiveInventoryNode is null"); - return false; - } - - List pkeyNames = - oxmEntityLookup.getEntityDescriptors().get(ain.getEntityType()).getPrimaryKeyAttributeNames(); - - if (pkeyNames == null || pkeyNames.size() == 0) { - LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE_NODE_ID, "Primary key names is empty"); - return false; - } - - StringBuilder sb = new StringBuilder(64); - - if (pkeyNames.size() > 0) { - String primaryKey = ain.getProperties().get(pkeyNames.get(0)); - if (primaryKey != null) { - sb.append(primaryKey); - } else { - // this should be a fatal error because unless we can - // successfully retrieve all the expected keys we'll end up - // with a garbage node - LOG.error(AaiUiMsgs.EXTRACTION_ERROR, "ERROR: Failed to extract keyName, " - + pkeyNames.get(0) + ", from entity properties"); - return false; - } - - for (int i = 1; i < pkeyNames.size(); i++) { - - String kv = ain.getProperties().get(pkeyNames.get(i)); - if (kv != null) { - sb.append("/").append(kv); - } else { - // this should be a fatal error because unless we can - // successfully retrieve all the expected keys we'll end up - // with a garbage node - LOG.error(AaiUiMsgs.EXTRACTION_ERROR, "ERROR: Failed to extract keyName, " - + pkeyNames.get(i) + ", from entity properties"); - return false; - } - } - - /*final String nodeId = NodeUtils.generateUniqueShaDigest(ain.getEntityType(), - NodeUtils.concatArray(pkeyNames, "/"), sb.toString());*/ - - //ain.setNodeId(nodeId); - ain.setPrimaryKeyName(NodeUtils.concatArray(pkeyNames, "/")); - ain.setPrimaryKeyValue(sb.toString()); - - if (ain.getEntityType() != null && ain.getPrimaryKeyName() != null - && ain.getPrimaryKeyValue() != null) { - ain.addQueryParam( - ain.getEntityType() + "." + ain.getPrimaryKeyName() + ":" + ain.getPrimaryKeyValue()); - } - return true; - - } - - return false; - - } - - /** - * Adds the self link relationship children. - * - * @param processingNode the processing node - * @param relationshipList the relationship list - * @return true, if successful - */ - private boolean addSelfLinkRelationshipChildren(ActiveInventoryNode processingNode, - RelationshipList relationshipList) { - - if (relationshipList == null) { - LOG.debug(AaiUiMsgs.DEBUG_GENERIC, "No relationships added to parent node = " - + processingNode.getNodeId() + " because relationshipList is empty"); - processingNode.changeState(NodeProcessingState.ERROR, - NodeProcessingAction.NEIGHBORS_PROCESSED_ERROR); - return false; - } - - Relationship[] relationshipArray = relationshipList.getRelationshipList(); - OxmEntityDescriptor descriptor = null; - String repairedSelfLink = null; - - if (relationshipArray != null) { - - ActiveInventoryNode newNode = null; - String resourcePath = null; - - for (Relationship r : relationshipArray) { - - resourcePath = ActiveInventoryAdapter.extractResourcePath(r.getRelatedLink()); - - String nodeId = NodeUtils.generateUniqueShaDigest(resourcePath); - - if (nodeId == null) { - - LOG.error(AaiUiMsgs.SKIPPING_RELATIONSHIP, r.toString()); - processingNode.changeState(NodeProcessingState.ERROR, - NodeProcessingAction.NODE_IDENTITY_ERROR); - return false; - } - - newNode = new ActiveInventoryNode(this.visualizationConfigs, oxmEntityLookup); - - String entityType = r.getRelatedTo(); - - if (r.getRelationshipData() != null) { - for (RelationshipData rd : r.getRelationshipData()) { - newNode.addQueryParam(rd.getRelationshipKey() + ":" + rd.getRelationshipValue()); - } - } - - descriptor = oxmEntityLookup.getEntityDescriptors().get(r.getRelatedTo()); - - newNode.setNodeId(nodeId); - newNode.setEntityType(entityType); - newNode.setSelfLink(resourcePath); - - processingNode.addOutboundNeighbor(nodeId); - - if (descriptor != null) { - - List pkeyNames = descriptor.getPrimaryKeyAttributeNames(); - - newNode.changeState(NodeProcessingState.SELF_LINK_UNRESOLVED, - NodeProcessingAction.SELF_LINK_SET); - - newNode.setPrimaryKeyName(NodeUtils.concatArray(pkeyNames, "/")); - - String primaryKeyValues = getRelationshipPrimaryKeyValues(r, entityType, pkeyNames); - newNode.setPrimaryKeyValue(primaryKeyValues); - - } else { - - LOG.error(AaiUiMsgs.VISUALIZATION_OUTPUT_ERROR, - "Failed to parse entity because OXM descriptor could not be found for type = " - + r.getRelatedTo()); - - newNode.changeState(NodeProcessingState.ERROR, - NodeProcessingAction.NEIGHBORS_PROCESSED_ERROR); - - } - - if (nodeCache.putIfAbsent(nodeId, newNode) != null) { - if (LOG.isDebugEnabled()) { - LOG.debug(AaiUiMsgs.DEBUG_GENERIC, - "Failed to add node to nodeCache because it already exists. Node id = " - + newNode.getNodeId()); - } - } - - } - - } - - return true; - - } - - /** - * Process initial state. - * - * @param nodeId the node id - */ - private void processInitialState(String nodeId) { - - if (nodeId == null) { - LOG.error(AaiUiMsgs.FAILED_TO_PROCESS_INITIAL_STATE, "Node id is null"); - return; - } - - ActiveInventoryNode cachedNode = nodeCache.get(nodeId); - - if (cachedNode == null) { - LOG.error(AaiUiMsgs.FAILED_TO_PROCESS_INITIAL_STATE, "Node cannot be" - + " found for nodeId, " + nodeId); - return; - } - - if (cachedNode.getSelfLink() == null) { - - if (cachedNode.getNodeId() == null ) { - - /* - * if the self link is null at the INIT state, which could be valid if this node is a - * complex attribute group which didn't originate from a self-link, but in that situation - * both the node id and node key should already be set. - */ - - cachedNode.changeState(NodeProcessingState.ERROR, NodeProcessingAction.NODE_IDENTITY_ERROR); - - } - - if (cachedNode.getNodeId() != null) { - - /* - * This should be the success path branch if the self-link is not set - */ - - cachedNode.changeState(NodeProcessingState.SELF_LINK_RESPONSE_UNPROCESSED, - NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_OK); - - } - - } else { - - if (cachedNode.hasResolvedSelfLink()) { - LOG.error(AaiUiMsgs.INVALID_RESOLVE_STATE_DURING_INIT); - cachedNode.changeState(NodeProcessingState.ERROR, - NodeProcessingAction.UNEXPECTED_STATE_TRANSITION); - } else { - cachedNode.changeState(NodeProcessingState.SELF_LINK_UNRESOLVED, - NodeProcessingAction.SELF_LINK_SET); - } - } - } - - /** - * Process skeleton node. - * - * @param skeletonNode the skeleton node - * @param queryParams the query params - */ - private void processSearchableEntity(SearchableEntity searchTargetEntity, QueryParams queryParams) { - - if (searchTargetEntity == null) { - return; - } - - if (searchTargetEntity.getId() == null) { - LOG.error(AaiUiMsgs.FAILED_TO_PROCESS_SKELETON_NODE, "Failed to process skeleton" - + " node because nodeId is null for node, " + searchTargetEntity.getLink()); - return; - } - - ActiveInventoryNode newNode = new ActiveInventoryNode(this.visualizationConfigs, oxmEntityLookup); - - newNode.setNodeId(searchTargetEntity.getId()); - newNode.setEntityType(searchTargetEntity.getEntityType()); - newNode.setPrimaryKeyName(getEntityTypePrimaryKeyName(searchTargetEntity.getEntityType())); - newNode.setPrimaryKeyValue(searchTargetEntity.getEntityPrimaryKeyValue()); - - if (newNode.getEntityType() != null && newNode.getPrimaryKeyName() != null - && newNode.getPrimaryKeyValue() != null) { - newNode.addQueryParam( - newNode.getEntityType() + "." + newNode.getPrimaryKeyName() + ":" + newNode.getPrimaryKeyValue()); - } - /* - * This code may need some explanation. In any graph there will be a single root node. The root - * node is really the center of the universe, and for now, we are tagging the search target as - * the root node. Everything else in the visualization of the graph will be centered around this - * node as the focal point of interest. - * - * Due to it's special nature, there will only ever be one root node, and it's node depth will - * always be equal to zero. - */ - - if (!isRootNodeFound()) { - if (queryParams.getSearchTargetNodeId().equals(newNode.getNodeId())) { - newNode.setNodeDepth(0); - newNode.setRootNode(true); - LOG.info(AaiUiMsgs.ROOT_NODE_DISCOVERED, queryParams.getSearchTargetNodeId()); - setRootNodeFound(true); - } - } - - newNode.setSelfLink(searchTargetEntity.getLink()); - - nodeCache.putIfAbsent(newNode.getNodeId(), newNode); - } - - private int getTotalWorkOnHand() { - - int numNodesWithPendingStates = 0; - - if( isRootNodeFound()) { - evaluateNodeDepths(); - } - - for (ActiveInventoryNode n : nodeCache.values()) { - - switch (n.getState()) { - - case READY: - case ERROR: { - // do nothing, these are our normal - // exit states - break; - } - - case NEIGHBORS_UNPROCESSED: { - - if (n.getNodeDepth() < this.visualizationConfigs.getMaxSelfLinkTraversalDepth()) { - /* - * Only process our neighbors relationships if our current depth is less than the max - * depth - */ - numNodesWithPendingStates++; - } - - break; - } - - default: { - - /* - * for all other states, there is work to be done - */ - numNodesWithPendingStates++; - } - - } - - } - - LOG.debug(AaiUiMsgs.OUTSTANDING_WORK_PENDING_NODES, - String.valueOf(numNodesWithPendingStates)); - - int totalWorkOnHand = aaiWorkOnHand.get() + numNodesWithPendingStates; - - return totalWorkOnHand; - - } - - /** - * Checks for out standing work. - * - * @return true, if successful - */ - private void processOutstandingWork(QueryParams queryParams) { - - while (getTotalWorkOnHand() > 0) { - - /* - * Force an evaluation of node depths before determining if we should limit state-based - * traversal or processing. - */ - - processCurrentNodeStates(queryParams); - - try { - Thread.sleep(10); - } catch (InterruptedException exc) { - LOG.error(AaiUiMsgs.PROCESSING_LOOP_INTERUPTED, exc.getMessage()); - Thread.currentThread().interrupt(); - return; - } - - } - - } - - /* (non-Javadoc) - * @see org.onap.aai.sparky.viewandinspect.services.VisualizationContext#processSelfLinks(org.onap.aai.sparky.sync.entity.SearchableEntity, org.onap.aai.sparky.viewandinspect.entity.QueryParams) - */ - @Override - public void processSelfLinks(SearchableEntity searchtargetEntity, QueryParams queryParams) { - - try { - - - if (searchtargetEntity == null) { - LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, contextIdStr + " - Failed to" - + " processSelfLinks, searchtargetEntity is null"); - return; - } - - long startTimeInMs = System.currentTimeMillis(); - - processSearchableEntity(searchtargetEntity, queryParams); - - /* - * This method is blocking until we decouple it with a CountDownLatch await condition, - * and make the internal graph processing more event-y. - */ - - processOutstandingWork(queryParams); - - long totalResolveTime = (System.currentTimeMillis() - startTimeInMs); - - long opTime = System.currentTimeMillis() - startTimeInMs; - - LOG.info(AaiUiMsgs.ALL_TRANSACTIONS_RESOLVED, String.valueOf(totalResolveTime), - String.valueOf(totalLinksRetrieved.get()), String.valueOf(opTime)); - - } catch (Exception exc) { - LOG.error(AaiUiMsgs.VISUALIZATION_OUTPUT_ERROR, exc.getMessage()); - } - - } - - /** - * Verify outbound neighbors. - */ - private void verifyOutboundNeighbors() { - - for (ActiveInventoryNode srcNode : nodeCache.values()) { - - for (String targetNodeId : srcNode.getOutboundNeighbors()) { - - ActiveInventoryNode targetNode = nodeCache.get(targetNodeId); - - if (targetNode != null && srcNode.getNodeId() != null) { - - targetNode.addInboundNeighbor(srcNode.getNodeId()); - - if (this.visualizationConfigs.makeAllNeighborsBidirectional()) { - targetNode.addOutboundNeighbor(srcNode.getNodeId()); - } - - } - - } - - } - - } - - /** - * Evaluate node depths. - */ - private void evaluateNodeDepths() { - - int numChanged = -1; - int numAttempts = 0; - - while (numChanged != 0) { - - numChanged = 0; - numAttempts++; - - for (ActiveInventoryNode srcNode : nodeCache.values()) { - - if (srcNode.getState() == NodeProcessingState.INIT) { - - /* - * this maybe the only state that we don't want to to process the node depth on, because - * typically it won't have any valid fields set, and it may remain in a partial state - * until we have processed the self-link. - */ - - continue; - - } - - for (String targetNodeId : srcNode.getOutboundNeighbors()) { - ActiveInventoryNode targetNode = nodeCache.get(targetNodeId); - - if (targetNode != null) { - - if (targetNode.changeDepth(srcNode.getNodeDepth() + 1)) { - numChanged++; - } - } - } - - for (String targetNodeId : srcNode.getInboundNeighbors()) { - ActiveInventoryNode targetNode = nodeCache.get(targetNodeId); - - if (targetNode != null) { - - if (targetNode.changeDepth(srcNode.getNodeDepth() + 1)) { - numChanged++; - } - } - } - } - - if (numAttempts >= MAX_DEPTH_EVALUATION_ATTEMPTS) { - LOG.info(AaiUiMsgs.MAX_EVALUATION_ATTEMPTS_EXCEEDED); - return; - } - - } - - if (LOG.isDebugEnabled()) { - if (numAttempts > 0) { - LOG.debug(AaiUiMsgs.DEBUG_GENERIC, - "Evaluate node depths completed in " + numAttempts + " attempts"); - } else { - LOG.debug(AaiUiMsgs.DEBUG_GENERIC, - "Evaluate node depths completed in 0 attempts because all nodes at correct depth"); - } - } - - } - - - /** - * Gets the entity type primary key name. - * - * @param entityType the entity type - * @return the entity type primary key name - */ - - - private String getEntityTypePrimaryKeyName(String entityType) { - - if (entityType == null) { - LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE, "node primary key" - + " name because entity type is null"); - return null; - } - - OxmEntityDescriptor descriptor = oxmEntityLookup.getEntityDescriptors().get(entityType); - - if (descriptor == null) { - LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE, "oxm entity" - + " descriptor for entityType = " + entityType); - return null; - } - - List pkeyNames = descriptor.getPrimaryKeyAttributeNames(); - - if (pkeyNames == null || pkeyNames.size() == 0) { - LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE, "node primary" - + " key because descriptor primary key names is empty"); - return null; - } - - return NodeUtils.concatArray(pkeyNames, "/"); - - } - -} diff --git a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/services/BaseVisualizationService.java b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/services/BaseVisualizationService.java index 17c9072..992e531 100644 --- a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/services/BaseVisualizationService.java +++ b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/services/BaseVisualizationService.java @@ -21,20 +21,14 @@ package org.onap.aai.sparky.viewandinspect.services; import java.io.IOException; -import java.security.SecureRandom; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; import javax.servlet.ServletException; import org.onap.aai.cl.api.Logger; import org.onap.aai.cl.eelf.LoggerFactory; import org.onap.aai.restclient.client.OperationResult; -import org.onap.aai.sparky.config.oxm.OxmEntityLookup; -import org.onap.aai.sparky.config.oxm.OxmModelLoader; -import org.onap.aai.sparky.dal.ActiveInventoryAdapter; -import org.onap.aai.sparky.dal.GizmoAdapter; import org.onap.aai.sparky.dal.rest.config.RestEndpointConfig; import org.onap.aai.sparky.logging.AaiUiMsgs; import org.onap.aai.sparky.search.SearchServiceAdapter; @@ -42,6 +36,9 @@ import org.onap.aai.sparky.subscription.config.SubscriptionConfig; import org.onap.aai.sparky.sync.config.ElasticSearchSchemaConfig; import org.onap.aai.sparky.sync.entity.SearchableEntity; import org.onap.aai.sparky.util.NodeUtils; +import org.onap.aai.sparky.viewandinspect.VisualizationContext; +import org.onap.aai.sparky.viewandinspect.VisualizationContextBuilder; +import org.onap.aai.sparky.viewandinspect.VisualizationService; import org.onap.aai.sparky.viewandinspect.config.VisualizationConfigs; import org.onap.aai.sparky.viewandinspect.entity.ActiveInventoryNode; import org.onap.aai.sparky.viewandinspect.entity.D3VisualizationOutput; @@ -60,43 +57,30 @@ public class BaseVisualizationService implements VisualizationService { private static final Logger LOG = LoggerFactory.getInstance().getLogger(BaseVisualizationService.class); - private ObjectMapper mapper = new ObjectMapper(); + protected ObjectMapper mapper = new ObjectMapper(); - private final ActiveInventoryAdapter aaiAdapter; - private final GizmoAdapter gizmoAdapter; - private final SearchServiceAdapter searchServiceAdapter; - private final ExecutorService aaiExecutorService; + protected final SearchServiceAdapter searchServiceAdapter; - private ConcurrentHashMap contextMap; - private final SecureRandom secureRandom; - - private VisualizationConfigs visualizationConfigs; - private SubscriptionConfig subConfig; - private RestEndpointConfig endpointConfig; - private ElasticSearchSchemaConfig schemaEConfig; - private OxmEntityLookup oxmEntityLookup; + protected ConcurrentHashMap contextMap; + + protected VisualizationConfigs visualizationConfigs; + protected SubscriptionConfig subConfig; + protected RestEndpointConfig endpointConfig; + protected ElasticSearchSchemaConfig schemaEConfig; + + protected VisualizationContextBuilder contextBuilder; - public BaseVisualizationService(OxmModelLoader loader, VisualizationConfigs visualizationConfigs, - ActiveInventoryAdapter aaiAdapter, GizmoAdapter gizmoAdapter, SearchServiceAdapter searchServiceAdapter, - RestEndpointConfig endpointConfig, ElasticSearchSchemaConfig schemaConfig, - int numActiveInventoryWorkers, OxmEntityLookup oxmEntityLookup, SubscriptionConfig subscriptionConfig) - throws Exception { + public BaseVisualizationService(VisualizationContextBuilder contextBuilder, + VisualizationConfigs visualizationConfigs, SearchServiceAdapter searchServiceAdapter, + RestEndpointConfig endpointConfig, ElasticSearchSchemaConfig schemaConfig, + SubscriptionConfig subscriptionConfig) throws Exception { this.visualizationConfigs = visualizationConfigs; this.endpointConfig = endpointConfig; this.schemaEConfig = schemaConfig; - this.oxmEntityLookup = oxmEntityLookup; this.subConfig = subscriptionConfig; + this.contextBuilder = contextBuilder; - - secureRandom = new SecureRandom(); - - /* - * Fix constructor with properly wired in properties - */ - - this.aaiAdapter = aaiAdapter; - this.gizmoAdapter = gizmoAdapter; this.searchServiceAdapter = searchServiceAdapter; this.mapper = new ObjectMapper(); @@ -104,9 +88,6 @@ public class BaseVisualizationService implements VisualizationService { this.contextMap = new ConcurrentHashMap(); - this.aaiExecutorService = NodeUtils.createNamedExecutor("SLNC-WORKER", - numActiveInventoryWorkers, LOG); - } /** @@ -144,12 +125,12 @@ public class BaseVisualizationService implements VisualizationService { * @param method the method * @param opStartTimeInMs the op start time in ms */ - private void logOptime(String method, long opStartTimeInMs) { + protected void logOptime(String method, long opStartTimeInMs) { LOG.info(AaiUiMsgs.OPERATION_TIME, method, String.valueOf(System.currentTimeMillis() - opStartTimeInMs)); } - private SearchableEntity extractSearchableEntityFromElasticEntity(OperationResult operationResult) { + protected SearchableEntity extractSearchableEntityFromElasticEntity(OperationResult operationResult) { if (operationResult == null || !operationResult.wasSuccessful()) { // error, return empty collection return null; @@ -190,7 +171,8 @@ public class BaseVisualizationService implements VisualizationService { * @param queryRequest the query request * @return the operation result */ - public OperationResult buildVisualizationUsingGenericQuery(QueryRequest queryRequest) { + @Override + public OperationResult buildVisualization(QueryRequest queryRequest) { OperationResult returnValue = new OperationResult(); OperationResult dataCollectionResult; @@ -263,7 +245,7 @@ public class BaseVisualizationService implements VisualizationService { * @throws ServletException the servlet exception * @throws */ - private String getVisualizationOutputBasedonGenericQuery(SearchableEntity searchtargetEntity, + protected String getVisualizationOutputBasedonGenericQuery(SearchableEntity searchtargetEntity, QueryParams queryParams, QueryRequest request) throws ServletException { long opStartTimeInMs = System.currentTimeMillis(); @@ -277,17 +259,12 @@ public class BaseVisualizationService implements VisualizationService { } VisualizationContext visContext = null; - long contextId = secureRandom.nextLong(); + try { - if ( visualizationConfigs.isGizmoEnabled()) { - visContext = new BaseGizmoVisualizationContext(contextId, this.gizmoAdapter, aaiExecutorService, - this.visualizationConfigs, oxmEntityLookup); - } else { - visContext = new BaseVisualizationContext(contextId, this.aaiAdapter, aaiExecutorService, - this.visualizationConfigs, oxmEntityLookup); - } - - contextMap.putIfAbsent(contextId, visContext); + + visContext = contextBuilder.getVisualizationContext(); + contextMap.putIfAbsent(visContext.getContextId(), visContext); + } catch (Exception e1) { LOG.error(AaiUiMsgs.EXCEPTION_CAUGHT, "While building Visualization Context, " + e1.getLocalizedMessage()); @@ -297,7 +274,7 @@ public class BaseVisualizationService implements VisualizationService { long startTimeInMs = System.currentTimeMillis(); visContext.processSelfLinks(searchtargetEntity, queryParams); - contextMap.remove(contextId); + contextMap.remove(visContext.getContextId()); logOptime("collectSelfLinkNodes()", startTimeInMs); @@ -323,7 +300,9 @@ public class BaseVisualizationService implements VisualizationService { LOG.debug(AaiUiMsgs.DEBUG_GENERIC, sb.toString()); } - transformer.buildFlatNodeArrayFromGraphCollection(cachedNodeMap); + GraphMeta graphMeta = new GraphMeta(); + + transformer.buildFlatNodeArrayFromGraphCollection(cachedNodeMap, graphMeta); transformer.buildLinksFromGraphCollection(cachedNodeMap); /* @@ -333,20 +312,12 @@ public class BaseVisualizationService implements VisualizationService { */ transformer.addSearchTargetAttributesToRootNode(); - - GraphMeta graphMeta = new GraphMeta(); D3VisualizationOutput output = getD3VisualizationOutput(opStartTimeInMs, transformer, graphMeta); String jsonResponse = null; if (output != null) { - output.setInlineMessage(visContext.getInlineMessage()); - output.getGraphMeta().setNumLinkResolveFailed(visContext.getNumFailedLinkResolve()); - output.getGraphMeta().setNumLinksResolvedSuccessfullyFromCache( - visContext.getNumSuccessfulLinkResolveFromCache()); - output.getGraphMeta().setNumLinksResolvedSuccessfullyFromServer( - visContext.getNumSuccessfulLinkResolveFromFromServer()); try { jsonResponse = transformer.convertVisualizationOutputToJson(output); @@ -365,7 +336,8 @@ public class BaseVisualizationService implements VisualizationService { } - private D3VisualizationOutput getD3VisualizationOutput(long opStartTimeInMs, VisualizationTransformer transformer, GraphMeta graphMeta) throws ServletException { + protected D3VisualizationOutput getD3VisualizationOutput(long opStartTimeInMs, + VisualizationTransformer transformer, GraphMeta graphMeta) throws ServletException { D3VisualizationOutput output = null; try { output = transformer @@ -378,7 +350,7 @@ public class BaseVisualizationService implements VisualizationService { return output; } - private JsonNode extractSearchServiceContent(JsonNode returnedData){ + protected JsonNode extractSearchServiceContent(JsonNode returnedData){ JsonNode searchResults = returnedData.get("searchResult"); JsonNode searchHits = searchResults.get("hits"); @@ -388,8 +360,11 @@ public class BaseVisualizationService implements VisualizationService { return content; } + @Override public void shutdown() { - aaiExecutorService.shutdown(); + if ( contextBuilder != null ) { + contextBuilder.shutdown(); + } } } diff --git a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/services/VisualizationContext.java b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/services/VisualizationContext.java deleted file mode 100644 index 1641aa3..0000000 --- a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/services/VisualizationContext.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 Amdocs - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ -package org.onap.aai.sparky.viewandinspect.services; - -import java.util.concurrent.ConcurrentHashMap; - -import org.onap.aai.sparky.sync.entity.SearchableEntity; -import org.onap.aai.sparky.viewandinspect.entity.ActiveInventoryNode; -import org.onap.aai.sparky.viewandinspect.entity.InlineMessage; -import org.onap.aai.sparky.viewandinspect.entity.QueryParams; - - -public interface VisualizationContext { - - /** - * Process self links. - * - * @param skeletonNode the skeleton node - * @param queryParams the query params - */ - void processSelfLinks(SearchableEntity searchtargetEntity, QueryParams queryParams); - - ConcurrentHashMap getNodeCache(); - - InlineMessage getInlineMessage(); - - int getNumFailedLinkResolve(); - - int getNumSuccessfulLinkResolveFromCache(); - - int getNumSuccessfulLinkResolveFromFromServer(); - -} diff --git a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/services/VisualizationService.java b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/services/VisualizationService.java deleted file mode 100644 index 7693885..0000000 --- a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/services/VisualizationService.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 Amdocs - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ -package org.onap.aai.sparky.viewandinspect.services; - - -import org.onap.aai.restclient.client.OperationResult; -import org.onap.aai.sparky.viewandinspect.entity.QueryRequest; - -public interface VisualizationService { - - /** - * Analyze query request body. - * - * @param queryRequestJson the query request json - * @return the query request - */ - - QueryRequest analyzeQueryRequestBody(String queryRequestJson); - - /** - * Builds the visualization using generic query. - * - * @param queryRequest the query request - * @return the operation result - */ - OperationResult buildVisualizationUsingGenericQuery(QueryRequest queryRequest); - - void shutdown(); - -} diff --git a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/services/VisualizationTransformer.java b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/services/VisualizationTransformer.java index 7fcacfe..2d70b92 100644 --- a/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/services/VisualizationTransformer.java +++ b/sparkybe-onap-service/src/main/java/org/onap/aai/sparky/viewandinspect/services/VisualizationTransformer.java @@ -33,7 +33,6 @@ import org.onap.aai.cl.eelf.LoggerFactory; import org.onap.aai.sparky.config.SparkyResourceLoader; import org.onap.aai.sparky.logging.AaiUiMsgs; import org.onap.aai.sparky.subscription.config.SubscriptionConfig; -import org.onap.aai.sparky.util.ConfigHelper; import org.onap.aai.sparky.viewandinspect.config.VisualizationConfigs; import org.onap.aai.sparky.viewandinspect.entity.ActiveInventoryNode; import org.onap.aai.sparky.viewandinspect.entity.D3VisualizationOutput; @@ -41,6 +40,7 @@ import org.onap.aai.sparky.viewandinspect.entity.GraphMeta; import org.onap.aai.sparky.viewandinspect.entity.NodeDebug; import org.onap.aai.sparky.viewandinspect.entity.SparkyGraphLink; import org.onap.aai.sparky.viewandinspect.entity.SparkyGraphNode; +import org.onap.aai.sparky.viewandinspect.enumeration.NodeProcessingState; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -263,13 +263,17 @@ public class VisualizationTransformer { * Builds the flat node array from graph collection. * * @param nodeMap the node map + * @param graphMeta information + stats about the graph */ /* * Recursive function to walk multi-graph nodes and children to build a folded resource target * graph. */ - public void buildFlatNodeArrayFromGraphCollection(Map nodeMap) { + public void buildFlatNodeArrayFromGraphCollection(Map nodeMap, + GraphMeta graphMeta) { + int numNodesWithErrors = 0; + for (ActiveInventoryNode n : nodeMap.values()) { if (n.getNodeDepth() <= this.visualizationConfigs.getMaxSelfLinkTraversalDepth()) { @@ -277,6 +281,10 @@ public class VisualizationTransformer { SparkyGraphNode jsonNode = new SparkyGraphNode(n, this.visualizationConfigs, this.subConfig); jsonNode.getNodeMeta().setClassName(this.visualizationConfigs.getGeneralNodeClassName()); + + if (n.getState() == NodeProcessingState.ERROR) { + numNodesWithErrors++; + } if (this.visualizationConfigs.isVisualizationDebugEnabled()) { @@ -296,6 +304,9 @@ public class VisualizationTransformer { } } } + + graphMeta.setNumNodeWithProcessingErrors(numNodesWithErrors); + } } diff --git a/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/BaseVisualizationContextTest.java b/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/BaseVisualizationContextTest.java index 5c027da..d58bf42 100644 --- a/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/BaseVisualizationContextTest.java +++ b/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/BaseVisualizationContextTest.java @@ -1,5 +1,295 @@ package org.onap.aai.sparky.viewandinspect; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.io.InputStream; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; + +import javax.inject.Inject; + +import org.eclipse.persistence.jaxb.JAXBContextProperties; +import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext; +import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContextFactory; +import org.hamcrest.Matcher; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Matchers; +import org.mockito.Mockito; +import org.onap.aai.cl.api.Logger; +import org.onap.aai.cl.eelf.LoggerFactory; +import org.onap.aai.cl.mdc.MdcContext; +import org.onap.aai.restclient.client.OperationResult; +import org.onap.aai.sparky.config.oxm.OxmEntityLookup; +import org.onap.aai.sparky.dal.ActiveInventoryAdapter; +import org.onap.aai.sparky.dal.rest.config.RestEndpointConfig; +import org.onap.aai.sparky.sync.entity.SearchableEntity; +import org.onap.aai.sparky.util.NodeUtils; +import org.onap.aai.sparky.util.StringCollectionContainsMatcher; +import org.onap.aai.sparky.util.TestResourceLoader; +import org.onap.aai.sparky.viewandinspect.config.VisualizationConfigs; +import org.onap.aai.sparky.viewandinspect.context.BaseVisualizationContext; +import org.onap.aai.sparky.viewandinspect.entity.ActiveInventoryNode; +import org.onap.aai.sparky.viewandinspect.entity.QueryParams; +import org.onap.aai.sparky.viewandinspect.enumeration.NodeProcessingState; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + + + +@RunWith(SpringJUnit4ClassRunner.class) +@TestPropertySource(properties = { +"schemaIngestPropLoc = src/test/resources/oxm-reader/schema-ingest-single-oxm.properties" }) +@ContextConfiguration(locations = { "classpath:oxm-reader/oxm-reader-bean.xml" }) + + public class BaseVisualizationContextTest { -} + private static SecureRandom secureRandom = new SecureRandom(); + private static Logger LOG = + LoggerFactory.getInstance().getLogger(BaseVisualizationContextTest.class); + + private BaseVisualizationContext baseVisualizationContext; + private ExecutorService aaiExecutorService; + private VisualizationConfigs visualizationConfig; + + private ActiveInventoryAdapter aaiAdapter; + private RestEndpointConfig aaiRestEndPointConfig; + + @Inject + private OxmEntityLookup oxmEntityLookup; + + @Before + public void init() throws Exception { + + aaiExecutorService = NodeUtils.createNamedExecutor("SLNC-WORKER", 5, LOG); + visualizationConfig = new VisualizationConfigs(); + + ArrayList shallowEntities = new ArrayList(); + shallowEntities.add("cloud-region"); + + visualizationConfig.setShallowEntities(shallowEntities); + visualizationConfig.setMaxSelfLinkTraversalDepth(2); + + + aaiAdapter = Mockito.mock(ActiveInventoryAdapter.class); + + aaiRestEndPointConfig = new RestEndpointConfig(); + aaiRestEndPointConfig.setNumRequestRetries(5); + + Mockito.when(aaiAdapter.getEndpointConfig()).thenReturn(aaiRestEndPointConfig); + + MdcContext.initialize("" + secureRandom.nextLong(), "AAI-UI", "", "partner-name", + "localhost:4242"); + + // all our resources are prefixed already, so the repairSelfLink shouldn't do anything to the link + Mockito.when(aaiAdapter.repairSelfLink(Matchers.contains(""))).thenReturn(""); + + + } + + private Matcher> listContainsValue(String expectedValue) { + return new StringCollectionContainsMatcher(expectedValue); + } + + + @Test + public void validateBasicConstruction() throws Exception { + + long contextId = secureRandom.nextLong(); + + baseVisualizationContext = new BaseVisualizationContext(contextId, aaiAdapter, + aaiExecutorService, visualizationConfig, oxmEntityLookup); + + assertEquals(contextId, baseVisualizationContext.getContextId()); + + } + + @Test + public void validateSmallGraphAssembly() throws Exception { + + /** + * We have a tiny graph that we will validate assembly of: + * + *
  • customer -> tenant + *
  • customer -> service-subscription + *
  • service-subscription -> service-instance-1 + *
  • service-subscription -> service-instance-2 + * + * At the end of this success path, we should have 5 nodes in the node cache. Once we have this + * flow we can experiment with error paths involving resource download failures to ensure graph + * nodes are in the correct state and that expected nodes are successfully represented in the + * cache. + */ + + long contextId = secureRandom.nextLong(); + + baseVisualizationContext = new BaseVisualizationContext(contextId, aaiAdapter, + aaiExecutorService, visualizationConfig, oxmEntityLookup); + + SearchableEntity searchableEntity = new SearchableEntity(); + String customerSelfLink = + "https://server.proxy:8443/aai/v11/business/customers/customer/customer-4"; + String customerNodeId = NodeUtils.generateUniqueShaDigest(customerSelfLink); + + searchableEntity.setId(customerNodeId); + searchableEntity.setEntityType("customer"); + searchableEntity.setEntityPrimaryKeyValue("customer-4"); + searchableEntity.setLink(customerSelfLink); + + QueryParams queryParams = new QueryParams(); + queryParams.setSearchTargetNodeId(customerNodeId); + queryParams.setSearchTargetPrimaryKeyValues("customer-4"); + + // aai customer resource dip + + Mockito + .when(aaiAdapter.queryActiveInventoryWithRetries(Matchers.contains("customer-4"), + Mockito.anyString(), Mockito.anyInt())) + .thenReturn(new OperationResult(200, TestResourceLoader + .getTestResourceDataJson("/sync/aai/aai-resources/customer/customer-4.json"))); + + // aai tenant resource dip + + Mockito + .when(aaiAdapter.queryActiveInventoryWithRetries(Matchers.contains("tenant/tenant-1"), + Mockito.anyString(), Mockito.anyInt())) + .thenReturn(new OperationResult(200, TestResourceLoader + .getTestResourceDataJson("/sync/aai/aai-resources/tenant/tenant-1.json"))); + + // generic-queries for service-subscription + + Mockito + .when(aaiAdapter.getGenericQueryForSelfLink(Matchers.contains("service-subscription"), + Matchers.argThat( + listContainsValue("service-subscription.service-type:service-subscription-2")))) + .thenReturn( + "https://server.proxy:8443/aai/v11/search/generic-query/service-subscription-2"); + + Mockito + .when(aaiAdapter.queryActiveInventoryWithRetries( + Matchers.contains("generic-query/service-subscription-2"), Mockito.anyString(), + Mockito.anyInt())) + .thenReturn(new OperationResult(200, TestResourceLoader.getTestResourceDataJson( + "/sync/aai/aai-traversal/generic-query/service-subscription/service-subscription-2.json"))); + + // generic-queries for service-instance-1 + + Mockito + .when(aaiAdapter.getGenericQueryForSelfLink(Matchers.contains("service-instance"), + Matchers.argThat(listContainsValue("service-instance-id:service-instance-54")))) + .thenReturn( + "https://server.proxy:8443/aai/v11/search/generic-query/service-instance-id/service-instance-54"); + + Mockito + .when(aaiAdapter.queryActiveInventoryWithRetries( + Matchers.contains("generic-query/service-instance-id/service-instance-54"), + Mockito.anyString(), Mockito.anyInt())) + .thenReturn(new OperationResult(200, TestResourceLoader.getTestResourceDataJson( + "/sync/aai/aai-traversal/generic-query/service-instance/service-instance-54.json"))); + + // generic-queries for service-instance-2 + + Mockito + .when(aaiAdapter.getGenericQueryForSelfLink(Matchers.contains("service-instance"), + Matchers.argThat(listContainsValue("service-instance-id:service-instance-55")))) + .thenReturn( + "https://server.proxy:8443/aai/v11/search/generic-query/service-instance-id/service-instance-55"); + + Mockito + .when(aaiAdapter.queryActiveInventoryWithRetries( + Matchers.contains("generic-query/service-instance-id/service-instance-55"), + Mockito.anyString(), Mockito.anyInt())) + .thenReturn(new OperationResult(200, TestResourceLoader.getTestResourceDataJson( + "/sync/aai/aai-traversal/generic-query/service-instance/service-instance-55.json"))); + + + + // start the test + + baseVisualizationContext.processSelfLinks(searchableEntity, queryParams); + + /* + * validation can be in the form of validating nodes + relationships from the node cache + * baseVisualizationContext.getNodeCache(); + */ + + Map nodeCache = baseVisualizationContext.getNodeCache(); + + assertEquals(5, nodeCache.size()); + assertNotNull(nodeCache.get(customerNodeId)); + assertEquals("customer", nodeCache.get(customerNodeId).getEntityType()); + + // verify node collection nodes + + ActiveInventoryNode customerNode = + nodeCache.get("da4101ad19b3c380a1c12ffeda8ab390e1489fb4a22a392c9a1939db63c3dec5"); + ActiveInventoryNode ssNode = + nodeCache.get("f4ceaf19459993c4fc9438a7579dd20d786109f4455e38682c579045b7ae615e"); + ActiveInventoryNode tenantNode = + nodeCache.get("4735439b29e446b339535668238076e4b392eaa3eec218936e12f735179bc55e"); + ActiveInventoryNode s1 = + nodeCache.get("f975ab453b142197af5d0173e0a9cf2aa22d10502f8ad655c8d17de81b066e8f"); + ActiveInventoryNode s2 = + nodeCache.get("de77ef8f76dd6f19662b163527ff839891b9596cac655e3143fdd7ad39e2e4e3"); + + assertNotNull(customerNode); + assertNotNull(ssNode); + assertNotNull(tenantNode); + assertNotNull(s1); + assertNotNull(s2); + + // verify node depths + + assertEquals(0, customerNode.getNodeDepth()); + assertEquals(1, ssNode.getNodeDepth()); + + /* + * I think there is a bug in the way the node depth is represented due to the enforcement of + * bidirectional links being disabled. We may have to circle back to this behavior at some point + * and re-verify that the behavior works properly. + */ + + assertEquals(2, tenantNode.getNodeDepth()); + assertEquals(2, s1.getNodeDepth()); + assertEquals(2, s2.getNodeDepth()); + + // verify node states + + assertEquals(NodeProcessingState.READY, customerNode.getState()); + assertEquals(NodeProcessingState.READY, ssNode.getState()); + + /* + * these nodes have a NEIGHBORS_UNPROCESSED state because the max traversal depth was hit before + * processing all the nested relationships. I think what we should look at is advancing the + * state to READY if in fact there are no relationships to process, which I think could be the + * case sometimes. + */ + assertEquals(NodeProcessingState.NEIGHBORS_UNPROCESSED, tenantNode.getState()); + assertEquals(NodeProcessingState.NEIGHBORS_UNPROCESSED, s1.getState()); + assertEquals(NodeProcessingState.NEIGHBORS_UNPROCESSED, s2.getState()); + + } + + protected DynamicJAXBContext createVersionedOxm() { + Map properties = new HashMap<>(); + ClassLoader classLoader = null; + InputStream iStream = classLoader.getResourceAsStream("example/resources/eclipselink/eclipselink-oxm.xml"); + + properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, iStream); + try{ + return DynamicJAXBContextFactory.createContextFromOXM(this.getClass().getClassLoader(), properties); + } catch (Exception e) { + return null; + } + } + + +} \ No newline at end of file diff --git a/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/BaseVisualizationServiceTest.java b/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/BaseVisualizationServiceTest.java index eeacde1..9be0fe2 100644 --- a/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/BaseVisualizationServiceTest.java +++ b/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/BaseVisualizationServiceTest.java @@ -5,15 +5,11 @@ import static org.junit.Assert.assertEquals; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; -import org.onap.aai.sparky.config.oxm.OxmEntityLookup; -import org.onap.aai.sparky.config.oxm.OxmModelLoader; import org.onap.aai.sparky.dal.ActiveInventoryAdapter; -import org.onap.aai.sparky.dal.GizmoAdapter; import org.onap.aai.sparky.dal.rest.config.RestEndpointConfig; import org.onap.aai.sparky.search.SearchServiceAdapter; import org.onap.aai.sparky.subscription.config.SubscriptionConfig; import org.onap.aai.sparky.sync.config.ElasticSearchSchemaConfig; -import org.onap.aai.sparky.util.OxmModelAndProcessorHelper; import org.onap.aai.sparky.viewandinspect.config.VisualizationConfigs; import org.onap.aai.sparky.viewandinspect.entity.QueryRequest; import org.onap.aai.sparky.viewandinspect.services.BaseVisualizationService; @@ -26,50 +22,49 @@ public class BaseVisualizationServiceTest { private SubscriptionConfig subConfig; private RestEndpointConfig endpointEConfig; private ElasticSearchSchemaConfig schemaEConfig; - private OxmEntityLookup oxmEntityLookup; - private GizmoAdapter mockGizmoAdapter; - + private VisualizationContextBuilder contextBuilder; + private BaseVisualizationService baseVisService; - + @Before public void init() throws Exception { - this.mockAaiAdapter = Mockito.mock(ActiveInventoryAdapter.class); this.mockAaiAdapter = Mockito.mock(ActiveInventoryAdapter.class); this.mocksearchServiceAdapter = Mockito.mock(SearchServiceAdapter.class); - this.mockGizmoAdapter = Mockito.mock(GizmoAdapter.class); this.visualizationConfigs = new VisualizationConfigs(); this.subConfig = new SubscriptionConfig(); this.endpointEConfig = new RestEndpointConfig(); this.schemaEConfig = new ElasticSearchSchemaConfig(); - this.oxmEntityLookup = OxmModelAndProcessorHelper.getInstance().getOxmEntityLookup(); - - OxmModelLoader modelLoader = OxmModelAndProcessorHelper.getInstance().getModelLoader(); - - this.baseVisService = new BaseVisualizationService(modelLoader, visualizationConfigs, - mockAaiAdapter, mockGizmoAdapter, mocksearchServiceAdapter, endpointEConfig, schemaEConfig, 1, - oxmEntityLookup, subConfig); + + this.contextBuilder = Mockito.mock(VisualizationContextBuilder.class); + + this.baseVisService = new BaseVisualizationService(contextBuilder, visualizationConfigs, + mocksearchServiceAdapter, endpointEConfig, schemaEConfig, subConfig); } - + @Test public void testAnalyzeQueryRequestBody() { - QueryRequest validResquest = baseVisService.analyzeQueryRequestBody(SchemaVisualizationTestDataBuilder.getQueryRequest()); + QueryRequest validResquest = baseVisService + .analyzeQueryRequestBody(SchemaVisualizationTestDataBuilder.getQueryRequest()); assertEquals(SchemaVisualizationTestDataBuilder.ROOT_NODE_HASH_ID, validResquest.getHashId()); - - QueryRequest nullRequest = baseVisService.analyzeQueryRequestBody("This String should make the request return null eh!"); + + QueryRequest nullRequest = baseVisService + .analyzeQueryRequestBody("This String should make the request return null eh!"); assertEquals(null, nullRequest); } - + @Test public void testBuildVisualizationUsingGenericQuery() { - + initializeMocksForBuildVisualizationUsingGenericQueryTest(); - - QueryRequest rootNodeQuery = baseVisService.analyzeQueryRequestBody(SchemaVisualizationTestDataBuilder.getQueryRequest()); - + + QueryRequest rootNodeQuery = baseVisService + .analyzeQueryRequestBody(SchemaVisualizationTestDataBuilder.getQueryRequest()); + } - + private void initializeMocksForBuildVisualizationUsingGenericQueryTest() { - Mockito.when(mockAaiAdapter.queryActiveInventoryWithRetries(Mockito.anyString(), Mockito.anyString(), Mockito.anyInt())).thenReturn(null); + Mockito.when(mockAaiAdapter.queryActiveInventoryWithRetries(Mockito.anyString(), + Mockito.anyString(), Mockito.anyInt())).thenReturn(null); } - + } diff --git a/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/SchemaVisualizationProcessorTest.java b/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/SchemaVisualizationProcessorTest.java index 83e3ee0..edb6d4c 100644 --- a/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/SchemaVisualizationProcessorTest.java +++ b/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/SchemaVisualizationProcessorTest.java @@ -16,7 +16,6 @@ import org.junit.Test; import org.mockito.Mockito; import org.onap.aai.sparky.util.HttpServletHelper; import org.onap.aai.sparky.viewandinspect.entity.QueryRequest; -import org.onap.aai.sparky.viewandinspect.services.VisualizationService; import org.onap.aai.sparky.viewandinspect.util.SchemaVisualizationTestDataBuilder; import com.fasterxml.jackson.annotation.JsonInclude.Include; @@ -65,7 +64,7 @@ public class SchemaVisualizationProcessorTest { QueryRequest queryBody = nonEmptyMapper.readValue(queryRequest, QueryRequest.class); Mockito.when(mockVisualizationService.analyzeQueryRequestBody(Mockito.anyString())).thenReturn(queryBody); - Mockito.when(mockVisualizationService.buildVisualizationUsingGenericQuery(Mockito.anyObject())).thenReturn(SchemaVisualizationTestDataBuilder.getSchemaVisResult()); + Mockito.when(mockVisualizationService.buildVisualization(Mockito.anyObject())).thenReturn(SchemaVisualizationTestDataBuilder.getSchemaVisResult()); schemaVisProcessor.setVisualizationService(mockVisualizationService); schemaVisProcessor.processVisualizationRequest(exchange); diff --git a/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/config/VisualizationConfigTest.java b/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/config/VisualizationConfigTest.java index dfa4b27..dbbd38d 100644 --- a/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/config/VisualizationConfigTest.java +++ b/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/config/VisualizationConfigTest.java @@ -29,8 +29,6 @@ public class VisualizationConfigTest { visualConfig.setShallowEntities(shallowEntities); assertNotNull(visualConfig.getShallowEntities()); - visualConfig.setGizmoEnabled(true); - assertTrue(visualConfig.isGizmoEnabled()); visualConfig.setMakeAllNeighborsBidirectional(true); assertTrue(visualConfig.makeAllNeighborsBidirectional()); visualConfig.setSelectedSearchedNodeClassName("selectedsearchedNodeClass"); diff --git a/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/entity/D3VisualizationOutputTest.java b/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/entity/D3VisualizationOutputTest.java index e1b9931..4afdc7d 100644 --- a/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/entity/D3VisualizationOutputTest.java +++ b/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/entity/D3VisualizationOutputTest.java @@ -1,20 +1,15 @@ package org.onap.aai.sparky.viewandinspect.entity; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; import java.util.ArrayList; import org.junit.Before; import org.junit.Test; -import org.onap.aai.sparky.viewandinspect.config.VisualizationConfigs; public class D3VisualizationOutputTest { private D3VisualizationOutput d3visualization; - private InlineMessage inlineMessage; private GraphMeta graphMeta; private ArrayList nodes; private ArrayList links; @@ -26,15 +21,13 @@ public class D3VisualizationOutputTest { nodes = new ArrayList(); links = new ArrayList(); graphMeta = new GraphMeta(); - inlineMessage = new InlineMessage("level-1","Violation"); + } @Test public void updateValues() { - d3visualization.setInlineMessage(inlineMessage); - assertNotNull(d3visualization.getInlineMessage()); d3visualization.addLinks(links); d3visualization.addNodes(nodes); d3visualization.setGraphMeta(graphMeta); diff --git a/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/entity/GraphMetaTest.java b/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/entity/GraphMetaTest.java index bd4e454..e60f634 100644 --- a/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/entity/GraphMetaTest.java +++ b/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/entity/GraphMetaTest.java @@ -4,7 +4,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; - import org.junit.Before; import org.junit.Test; import org.onap.aai.sparky.viewandinspect.EntityTypeAggregation; @@ -36,12 +35,6 @@ public class GraphMetaTest { graphMeta.setAaiEntityNodeDescriptors(aaiEntityNodeDescriptors); assertNotNull(graphMeta.getAaiEntityNodeDescriptors()); - graphMeta.setNumLinksResolvedSuccessfullyFromCache(3); - assertEquals(3,graphMeta.getNumLinksResolvedSuccessfullyFromCache()); - graphMeta.setNumLinksResolvedSuccessfullyFromServer(25); - assertEquals(25,graphMeta.getNumLinksResolvedSuccessfullyFromServer()); - graphMeta.setNumLinkResolveFailed(3); - assertEquals(3,graphMeta.getNumLinkResolveFailed()); graphMeta.setNumNodes(25); assertEquals(25,graphMeta.getNumNodes()); graphMeta.setNumLinks(3); diff --git a/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/entity/InlineMessageTest.java b/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/entity/InlineMessageTest.java deleted file mode 100644 index eebc821..0000000 --- a/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/entity/InlineMessageTest.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * ============LICENSE_START=================================================== - * SPARKY (AAI UI service) - * ============================================================================ - * Copyright © 2017 AT&T Intellectual Property. - * Copyright © 2017 Amdocs - * All rights reserved. - * ============================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END===================================================== - * - * ECOMP and OpenECOMP are trademarks - * and service marks of AT&T Intellectual Property. - */ - -package org.onap.aai.sparky.viewandinspect.entity; - -import static org.junit.Assert.assertNotNull; - -import org.junit.Before; -import org.junit.Test; -import org.onap.aai.sparky.viewandinspect.entity.InlineMessage; - -public class InlineMessageTest { - - private InlineMessage inlineMsg; - - @Before - public void init() throws Exception { - inlineMsg = new InlineMessage("InlineMessage1","InlineMessage2"); - - } - - @Test - public void successfulInitializationAndUpdate() { - - inlineMsg.setLevel("Level-1"); - assertNotNull(inlineMsg.getLevel()); - inlineMsg.setMessage("InlineMessage3"); - assertNotNull(inlineMsg.getMessage()); - assertNotNull(inlineMsg.toString()); - } - - -} diff --git a/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/services/BaseGizmoVisualizationContextTest.java b/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/services/BaseGizmoVisualizationContextTest.java index 94050be..79b71bc 100644 --- a/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/services/BaseGizmoVisualizationContextTest.java +++ b/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/services/BaseGizmoVisualizationContextTest.java @@ -20,6 +20,7 @@ import org.onap.aai.sparky.dal.GizmoAdapter; import org.onap.aai.sparky.dal.rest.config.RestEndpointConfig; import org.onap.aai.sparky.util.NodeUtils; import org.onap.aai.sparky.viewandinspect.config.VisualizationConfigs; +import org.onap.aai.sparky.viewandinspect.context.BaseGizmoVisualizationContext; public class BaseGizmoVisualizationContextTest { diff --git a/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/services/BaseVisualizationContextTest.java b/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/services/BaseVisualizationContextTest.java index 1934207..394bfe4 100644 --- a/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/services/BaseVisualizationContextTest.java +++ b/sparkybe-onap-service/src/test/java/org/onap/aai/sparky/viewandinspect/services/BaseVisualizationContextTest.java @@ -34,6 +34,7 @@ import org.onap.aai.sparky.util.NodeUtils; import org.onap.aai.sparky.util.StringCollectionContainsMatcher; import org.onap.aai.sparky.util.TestResourceLoader; import org.onap.aai.sparky.viewandinspect.config.VisualizationConfigs; +import org.onap.aai.sparky.viewandinspect.context.BaseVisualizationContext; import org.onap.aai.sparky.viewandinspect.entity.ActiveInventoryNode; import org.onap.aai.sparky.viewandinspect.entity.QueryParams; import org.onap.aai.sparky.viewandinspect.enumeration.NodeProcessingState; -- cgit 1.2.3-korg