summaryrefslogtreecommitdiffstats
path: root/src/main/java/org/openecomp/sparky/viewandinspect
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/openecomp/sparky/viewandinspect')
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/EntityTypeAggregation.java94
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/config/TierSupportUiConstants.java90
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/config/VisualizationConfig.java199
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/entity/ActiveInventoryNode.java778
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/entity/D3VisualizationOutput.java132
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/entity/EntityEntry.java82
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/entity/GraphMeta.java148
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/entity/InlineMessage.java71
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/entity/JsonNode.java197
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/entity/JsonNodeLink.java76
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/entity/NodeDebug.java60
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/entity/NodeMeta.java212
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/entity/NodeProcessingTransaction.java103
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/entity/QueryParams.java58
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/entity/QueryRequest.java48
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/entity/QuerySearchEntity.java75
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/entity/RelatedToProperty.java65
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/entity/Relationship.java92
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/entity/RelationshipData.java64
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/entity/RelationshipDirectionality.java43
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/entity/RelationshipList.java58
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/entity/SearchResponse.java93
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/entity/SelfLinkDeterminationTransaction.java82
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/entity/Violations.java114
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/enumeration/NodeProcessingAction.java36
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/enumeration/NodeProcessingState.java33
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/services/SearchServiceWrapper.java809
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/services/VisualizationContext.java1649
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/services/VisualizationService.java387
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/services/VisualizationTransformer.java320
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/servlet/SearchServlet.java194
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/servlet/VisualizationServlet.java203
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/task/CollectNodeSelfLinkTask.java60
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/task/PerformNodeSelfLinkProcessingTask.java104
-rw-r--r--src/main/java/org/openecomp/sparky/viewandinspect/task/PerformSelfLinkDeterminationTask.java96
35 files changed, 6925 insertions, 0 deletions
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/EntityTypeAggregation.java b/src/main/java/org/openecomp/sparky/viewandinspect/EntityTypeAggregation.java
new file mode 100644
index 0000000..a99ebeb
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/EntityTypeAggregation.java
@@ -0,0 +1,94 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.openecomp.sparky.util.NodeUtils;
+
+/**
+ * The Class EntityTypeAggregation.
+ */
+public class EntityTypeAggregation {
+
+ private ConcurrentHashMap<String, AtomicInteger> counters;
+
+ /**
+ * Instantiates a new entity type aggregation.
+ */
+ public EntityTypeAggregation() {
+ counters = new ConcurrentHashMap<String, AtomicInteger>();
+ }
+
+ /**
+ * Peg counter.
+ *
+ * @param counterName the counter name
+ */
+ public void pegCounter(String counterName) {
+ counters.putIfAbsent(counterName, new AtomicInteger(0));
+ counters.get(counterName).incrementAndGet();
+ }
+
+ public ConcurrentHashMap<String, AtomicInteger> getCounters() {
+ return counters;
+ }
+
+ /**
+ * The main method.
+ *
+ * @param args the arguments
+ * @throws JsonProcessingException the json processing exception
+ */
+ public static void main(String[] args) throws JsonProcessingException {
+ // TODO Auto-generated method stub
+
+ EntityTypeAggregation eta = new EntityTypeAggregation();
+
+ eta.pegCounter("c1");
+ eta.pegCounter("c1");
+ eta.pegCounter("c1");
+
+ eta.pegCounter("x2");
+ eta.pegCounter("x2");
+ eta.pegCounter("x2");
+ eta.pegCounter("x2");
+
+ eta.pegCounter("z2");
+ eta.pegCounter("z2");
+ eta.pegCounter("z2");
+ eta.pegCounter("z2");
+ eta.pegCounter("z2");
+ eta.pegCounter("z2");
+
+ System.out.println(NodeUtils.convertObjectToJson(eta, true));
+
+ }
+
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/config/TierSupportUiConstants.java b/src/main/java/org/openecomp/sparky/viewandinspect/config/TierSupportUiConstants.java
new file mode 100644
index 0000000..4da07b9
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/config/TierSupportUiConstants.java
@@ -0,0 +1,90 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.config;
+
+/**
+ * The Class TierSupportUiConstants.
+ */
+public class TierSupportUiConstants {
+
+ public static String APP_NAME = "AAIUI";
+
+ /** Default to unix file separator if system property file.separator is null */
+ public static final String FILESEP =
+ (System.getProperty("file.separator") == null) ? "/" : System.getProperty("file.separator");
+
+ public static String CONFIG_HOME = System.getProperty("CONFIG_HOME") + FILESEP;
+ public static String AJSC_HOME = System.getProperty("AJSC_HOME") + FILESEP;
+ public static String CONFIG_ROOT_LOCATION = AJSC_HOME + "bundleconfig" + FILESEP + "etc" + FILESEP;
+ public static String STATIC_CONFIG_APP_LOCATION = CONFIG_ROOT_LOCATION + "appprops" + FILESEP;
+ public static String DYNAMIC_CONFIG_APP_LOCATION = CONFIG_HOME;
+
+ public static String CONFIG_OXM_LOCATION = CONFIG_HOME + "model" + FILESEP;
+
+ public static String CONFIG_AUTH_LOCATION = CONFIG_HOME + "auth" + FILESEP;
+
+ public static String HOST = "host";
+ public static String PORT = "port";
+ public static String RETRIES = "numRequestRetries";
+ public static String RESOURCE_VERSION = "resource-version";
+ public static String URI = "URI";
+
+ public static String USERS_FILE_LOCATION = DYNAMIC_CONFIG_APP_LOCATION + "users.config";
+ public static String ROLES_FILE_LOCATION = DYNAMIC_CONFIG_APP_LOCATION + "roles.config";
+ public static String PORTAL_AUTHENTICATION_FILE_LOCATION = DYNAMIC_CONFIG_APP_LOCATION + "portal" + FILESEP + "portal-authentication.properties";
+
+ public static final String ES_SUGGEST_API = "_suggest";
+ public static final String ES_COUNT_API = "_count";
+ public static final String ES_SEARCH_API = "_search";
+
+ public static final String ENTITY_AUTO_SUGGEST_INDEX_NAME_DEFAULT =
+ "entityautosuggestindex-localhost";
+ public static final String ENTITY_AUTO_SUGGEST_SETTINGS_FILE_DEFAULT =
+ "/etc/autoSuggestSettings.json";
+ public static final String ENTITY_AUTO_SUGGEST_MAPPINGS_FILE_DEFAULT =
+ "/etc/autoSuggestMappings.json";
+ public static final String ENTITY_DYNAMIC_MAPPINGS_FILE_DEFAULT =
+ "/etc/dynamicMappings.json";
+
+ // JUnit testing synchronizer.properties file
+ public static String TEST_CONFIG_FILE =
+ System.getProperty("user.dir") + FILESEP + "bundleconfig-local" + FILESEP + "etc" + FILESEP
+ + "appprops" + FILESEP + "synchronizer.properties";
+
+ // Injected Attributes
+ public static String URI_ATTR_NAME = "uri";
+
+ public static final String URI_VERSION_REGEX_PATTERN = "aai/v[\\d]+/";
+
+ public static final String getConfigPath(String configFile){
+ return AJSC_HOME + FILESEP + configFile;
+ }
+
+ public static final String getAggregationIndexName(String entityType){
+ return "aggregate_" + entityType + "_index";
+ }
+
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/config/VisualizationConfig.java b/src/main/java/org/openecomp/sparky/viewandinspect/config/VisualizationConfig.java
new file mode 100644
index 0000000..3f0a5b5
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/config/VisualizationConfig.java
@@ -0,0 +1,199 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.config;
+
+import java.util.Properties;
+
+import org.openecomp.sparky.util.ConfigHelper;
+
+/**
+ * The Class VisualizationConfig.
+ */
+public class VisualizationConfig {
+
+ private int maxSelfLinkTraversalDepth;
+
+ private boolean visualizationDebugEnabled;
+
+ private String aaiEntityNodeDescriptors;
+
+ private String generalNodeClassName;
+
+ private String searchNodeClassName;
+
+ private String selectedSearchedNodeClassName;
+
+ private String entityTypesToSummarize;
+ private String vnfEntityTypes;
+
+ private boolean makeAllNeighborsBidirectional;
+
+ private static VisualizationConfig instance;
+
+ public static VisualizationConfig getConfig() {
+
+ if (instance == null) {
+ instance = new VisualizationConfig();
+ }
+
+ return instance;
+
+ }
+
+ /**
+ * Instantiates a new visualization config.
+ */
+ public VisualizationConfig() {
+
+ Properties visualizationProps =
+ ConfigHelper.loadConfigFromExplicitPath(TierSupportUiConstants.STATIC_CONFIG_APP_LOCATION
+ + TierSupportUiConstants.FILESEP + "visualization.properties");
+
+ maxSelfLinkTraversalDepth =
+ Integer.parseInt(visualizationProps.getProperty("maxSelfLinkTraversalDepth", "2"));
+ visualizationDebugEnabled =
+ Boolean.parseBoolean(visualizationProps.getProperty("visualizationDebugEnabled", "false"));
+ aaiEntityNodeDescriptors = visualizationProps.getProperty("aaiEntityNodeDescriptors", null);
+ generalNodeClassName =
+ visualizationProps.getProperty("generalNodeClassName", "unknownClassName");
+ searchNodeClassName =
+ visualizationProps.getProperty("searchedNodeClassName", "unknownClassName");
+ selectedSearchedNodeClassName =
+ visualizationProps.getProperty("selectedSearchedNodeClassName", "unknownClassName");
+
+ entityTypesToSummarize = visualizationProps.getProperty("entityTypesToSummarize",
+ "customer,service-instance,complex,pserver,vserver,vnf");
+
+ vnfEntityTypes = visualizationProps.getProperty("vnfEntityTypes", "generic-vnf,newvce,vce,vpe");
+
+ makeAllNeighborsBidirectional = Boolean
+ .parseBoolean(visualizationProps.getProperty("makeAllNeighborsBidirectional", "false"));
+
+ }
+
+
+
+ /**
+ * Make all neighbors bidirectional.
+ *
+ * @return true, if successful
+ */
+ public boolean makeAllNeighborsBidirectional() {
+ return makeAllNeighborsBidirectional;
+ }
+
+ public void setMakeAllNeighborsBidirectional(boolean makeAllNeighborsBidirectional) {
+ this.makeAllNeighborsBidirectional = makeAllNeighborsBidirectional;
+ }
+
+ public String getSelectedSearchedNodeClassName() {
+ return selectedSearchedNodeClassName;
+ }
+
+ public void setSelectedSearchedNodeClassName(String selectedSearchedNodeClassName) {
+ this.selectedSearchedNodeClassName = selectedSearchedNodeClassName;
+ }
+
+ public String getGeneralNodeClassName() {
+ return generalNodeClassName;
+ }
+
+ public void setGeneralNodeClassName(String generalNodeClassName) {
+ this.generalNodeClassName = generalNodeClassName;
+ }
+
+ public String getSearchNodeClassName() {
+ return searchNodeClassName;
+ }
+
+ public void setSearchNodeClassName(String searchNodeClassName) {
+ this.searchNodeClassName = searchNodeClassName;
+ }
+
+ public String getAaiEntityNodeDescriptors() {
+ return aaiEntityNodeDescriptors;
+ }
+
+ public void setAaiEntityNodeDescriptors(String aaiEntityNodeDescriptors) {
+ this.aaiEntityNodeDescriptors = aaiEntityNodeDescriptors;
+ }
+
+ public boolean isVisualizationDebugEnabled() {
+ return visualizationDebugEnabled;
+ }
+
+ public void setVisualizationDebugEnabled(boolean visualizationDebugEnabled) {
+ this.visualizationDebugEnabled = visualizationDebugEnabled;
+ }
+
+ public void setMaxSelfLinkTraversalDepth(int maxSelfLinkTraversalDepth) {
+ this.maxSelfLinkTraversalDepth = maxSelfLinkTraversalDepth;
+ }
+
+ public int getMaxSelfLinkTraversalDepth() {
+ return maxSelfLinkTraversalDepth;
+ }
+
+ public String getEntityTypesToSummarize() {
+ return entityTypesToSummarize;
+ }
+
+ public void setEntityTypesToSummarize(String entityTypesToSummarize) {
+ this.entityTypesToSummarize = entityTypesToSummarize;
+ }
+
+ public String getVnfEntityTypes() {
+ return vnfEntityTypes;
+ }
+
+ public void setVnfEntityTypes(String vnfEntityTypes) {
+ this.vnfEntityTypes = vnfEntityTypes;
+ }
+
+ @Override
+ public String toString() {
+ return "VisualizationConfig [maxSelfLinkTraversalDepth=" + maxSelfLinkTraversalDepth
+ + ", visualizationDebugEnabled=" + visualizationDebugEnabled + ", "
+ + (aaiEntityNodeDescriptors != null
+ ? "aaiEntityNodeDescriptors=" + aaiEntityNodeDescriptors + ", " : "")
+ + (generalNodeClassName != null ? "generalNodeClassName=" + generalNodeClassName + ", "
+ : "")
+ + (searchNodeClassName != null ? "searchNodeClassName=" + searchNodeClassName + ", " : "")
+ + (selectedSearchedNodeClassName != null
+ ? "selectedSearchedNodeClassName=" + selectedSearchedNodeClassName + ", " : "")
+ + (entityTypesToSummarize != null
+ ? "entityTypesToSummarize=" + entityTypesToSummarize + ", " : "")
+ + (vnfEntityTypes != null ? "vnfEntityTypes=" + vnfEntityTypes + ", " : "")
+ + "makeAllNeighborsBidirectional=" + makeAllNeighborsBidirectional + "]";
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+
+
+
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/entity/ActiveInventoryNode.java b/src/main/java/org/openecomp/sparky/viewandinspect/entity/ActiveInventoryNode.java
new file mode 100644
index 0000000..db79ef5
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/entity/ActiveInventoryNode.java
@@ -0,0 +1,778 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.entity;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.openecomp.cl.api.Logger;
+import org.openecomp.cl.eelf.LoggerFactory;
+import org.openecomp.sparky.config.oxm.OxmModelLoader;
+import org.openecomp.sparky.dal.rest.OperationResult;
+import org.openecomp.sparky.logging.AaiUiMsgs;
+import org.openecomp.sparky.viewandinspect.config.VisualizationConfig;
+import org.openecomp.sparky.viewandinspect.enumeration.NodeProcessingAction;
+import org.openecomp.sparky.viewandinspect.enumeration.NodeProcessingState;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * The Class ActiveInventoryNode.
+ */
+public class ActiveInventoryNode {
+
+ private static final Logger LOG = LoggerFactory.getInstance().getLogger(
+ ActiveInventoryNode.class);
+ private static final String URIRegexPattern = "aai/v[\\d]/";
+
+ public static final int DEFAULT_INIT_NODE_DEPTH = 1000;
+
+ private String nodeId;
+ private String selfLink;
+
+ private boolean isRootNode;
+ private ConcurrentLinkedDeque<String> inboundNeighbors;
+ private ConcurrentLinkedDeque<String> outboundNeighbors;
+ private List<JsonNode> complexGroups;
+ private List<RelationshipList> relationshipLists;
+ private int nodeDepth;
+ private OperationResult opResult;
+
+
+ private boolean processingErrorOccurred;
+ private List<String> errorCauses;
+ private boolean selflinkRetrievalFailure;
+ private NodeProcessingState state;
+
+ private boolean processedNeighbors;
+
+ private boolean selfLinkPendingResolve;
+
+ /*
+ * I think we shouldn't be using this crutch flags. If these things are meant
+ * to represent the current state of the node, then they should be legitimate
+ * state transitions.
+ */
+
+ private boolean selfLinkDeterminationPending;
+
+ private AtomicBoolean selfLinkProcessed;
+
+ private OxmModelLoader oxmModelLoader;
+ private VisualizationConfig visualizationConfig;
+
+ private String entityType;
+ private String primaryKeyName;
+ private String primaryKeyValue;
+
+ private boolean nodeIssue;
+ private boolean ignoredByFilter;
+
+ private boolean resolvedSelfLink;
+
+ private Map<String, String> properties;
+ private ArrayList<String> queryParams;
+
+ private ObjectMapper mapper;
+
+ /**
+ * Instantiates a new active inventory node.
+ */
+ public ActiveInventoryNode() {
+ this(null);
+ }
+
+ /**
+ * Instantiates a new active inventory node.
+ *
+ * @param key the key
+ */
+ public ActiveInventoryNode(String key) {
+ this.nodeId = null;
+ this.entityType = null;
+ this.selfLink = null;
+ this.properties = new HashMap<String, String>();
+ this.processingErrorOccurred = false;
+ this.errorCauses = new ArrayList<String>();
+ this.selflinkRetrievalFailure = false;
+ this.nodeIssue = false;
+ this.state = NodeProcessingState.INIT;
+ this.selfLinkPendingResolve = false;
+ this.selfLinkDeterminationPending = false;
+
+ selfLinkProcessed = new AtomicBoolean(Boolean.FALSE);
+ oxmModelLoader = null;
+ visualizationConfig = null;
+
+ isRootNode = false;
+ inboundNeighbors = new ConcurrentLinkedDeque<String>();
+ outboundNeighbors = new ConcurrentLinkedDeque<String>();
+ complexGroups = new ArrayList<JsonNode>();
+ relationshipLists = new ArrayList<RelationshipList>();
+ nodeDepth = DEFAULT_INIT_NODE_DEPTH;
+ queryParams = new ArrayList<String>();
+
+ mapper = new ObjectMapper();
+
+ processedNeighbors = false;
+ resolvedSelfLink = false;
+
+
+ }
+
+ public void clearQueryParams() {
+ queryParams.clear();
+ }
+
+ public void addQueryParam(String queryParam) {
+ if ( queryParam!= null) {
+ if( !queryParams.contains(queryParam)) {
+ queryParams.add(queryParam);
+ }
+ }
+ }
+
+ public void addQueryParams(Collection<String> params) {
+
+ if (params != null & params.size() > 0) {
+
+ for (String param : params) {
+ addQueryParam(param);
+ }
+ }
+ }
+
+
+ public List<String> getQueryParams() {
+ return queryParams;
+ }
+
+ public void setSelfLinkDeterminationPending(boolean selfLinkDeterminationPending) {
+ this.selfLinkDeterminationPending = selfLinkDeterminationPending;
+ }
+
+ public boolean isSelfLinkDeterminationPending() {
+ return selfLinkDeterminationPending;
+ }
+
+ public NodeProcessingState getState() {
+ return state;
+ }
+
+ public List<JsonNode> getComplexGroups() {
+ return complexGroups;
+ }
+
+ public List<RelationshipList> getRelationshipLists() {
+ return relationshipLists;
+ }
+
+ public OperationResult getOpResult() {
+ return opResult;
+ }
+
+ public void setOpResult(OperationResult opResult) {
+ this.opResult = opResult;
+ }
+
+ public String getPrimaryKeyName() {
+ return primaryKeyName;
+ }
+
+ /**
+ * Gets the visualization config.
+ *
+ * @return the visualization config
+ */
+ public VisualizationConfig getvisualizationConfig() {
+ return visualizationConfig;
+ }
+
+ public int getNodeDepth() {
+ return nodeDepth;
+ }
+
+ public void setNodeDepth(int nodeDepth) {
+ this.nodeDepth = nodeDepth;
+ }
+
+ /**
+ * Sets the visualization config.
+ *
+ * @param visualizationConfig the new visualization config
+ */
+ public void setvisualizationConfig(VisualizationConfig visualizationConfig) {
+ this.visualizationConfig = visualizationConfig;
+ }
+
+ public OxmModelLoader getOxmModelLoader() {
+ return oxmModelLoader;
+ }
+
+ public void setPrimaryKeyName(String primaryKeyName) {
+ this.primaryKeyName = primaryKeyName;
+ }
+
+ public String getPrimaryKeyValue() {
+ return primaryKeyValue;
+ }
+
+ public void setPrimaryKeyValue(String primaryKeyValue) {
+ this.primaryKeyValue = primaryKeyValue;
+ }
+
+ public boolean isNodeIssue() {
+ return nodeIssue;
+ }
+
+ public boolean isIgnoredByFilter() {
+ return ignoredByFilter;
+ }
+
+ public void setIgnoredByFilter(boolean ignoredByFilter) {
+ this.ignoredByFilter = ignoredByFilter;
+ }
+
+ public void setNodeIssue(boolean nodeIssue) {
+ this.nodeIssue = nodeIssue;
+ }
+
+ /**
+ * Checks for processed neighbors.
+ *
+ * @return true, if successful
+ */
+ public boolean hasProcessedNeighbors() {
+ return processedNeighbors;
+ }
+
+ public void setProcessedNeighbors(boolean processedNeighbors) {
+ this.processedNeighbors = processedNeighbors;
+ }
+
+ /**
+ * Checks for resolved self link.
+ *
+ * @return true, if successful
+ */
+ public boolean hasResolvedSelfLink() {
+ return resolvedSelfLink;
+ }
+
+ public void setResolvedSelfLink(boolean resolvedSelfLink) {
+ this.resolvedSelfLink = resolvedSelfLink;
+ }
+
+ /**
+ * Checks for neighbors.
+ *
+ * @return true, if successful
+ */
+ public boolean hasNeighbors() {
+ return (inboundNeighbors.size() > 0 || outboundNeighbors.size() > 0);
+ }
+
+ /**
+ * Adds the inbound neighbor.
+ *
+ * @param nodeId the node id
+ */
+ public void addInboundNeighbor(String nodeId) {
+
+ if (nodeId == null) {
+ return;
+ }
+
+ if (!inboundNeighbors.contains(nodeId)) {
+ inboundNeighbors.add(nodeId);
+ }
+
+ }
+
+ /**
+ * Adds the outbound neighbor.
+ *
+ * @param nodeId the node id
+ */
+ public void addOutboundNeighbor(String nodeId) {
+
+ if (nodeId == null) {
+ return;
+ }
+
+ if (!outboundNeighbors.contains(nodeId)) {
+ outboundNeighbors.add(nodeId);
+ }
+
+ }
+
+ public boolean isAtMaxDepth() {
+ return (nodeDepth >= VisualizationConfig.getConfig().getMaxSelfLinkTraversalDepth());
+ }
+
+ public ConcurrentLinkedDeque<String> getInboundNeighbors() {
+ return inboundNeighbors;
+ }
+
+ public void setInboundNeighbors(ConcurrentLinkedDeque<String> inboundNeighbors) {
+ this.inboundNeighbors = inboundNeighbors;
+ }
+
+ public Collection<String> getOutboundNeighbors() {
+ List<String> result = new ArrayList<String>();
+
+ Iterator<String> neighborIterator = outboundNeighbors.iterator();
+
+ while (neighborIterator.hasNext()) {
+ result.add(neighborIterator.next());
+ }
+
+ return result;
+ }
+
+ /**
+ * Change depth.
+ *
+ * @param newDepth the new depth
+ * @return true, if successful
+ */
+ public boolean changeDepth(int newDepth) {
+
+ boolean nodeDepthWasChanged = false;
+
+ if (newDepth < nodeDepth) {
+ LOG.info(AaiUiMsgs.ACTIVE_INV_NODE_CHANGE_DEPTH, nodeId,
+ String.valueOf(this.nodeDepth), String.valueOf(newDepth));
+ this.nodeDepth = newDepth;
+ nodeDepthWasChanged = true;
+ }
+
+ return nodeDepthWasChanged;
+
+ }
+
+ public void setOutboundNeighbors(ConcurrentLinkedDeque<String> outboundNeighbors) {
+ this.outboundNeighbors = outboundNeighbors;
+ }
+
+ public boolean isRootNode() {
+ return isRootNode;
+ }
+
+ public void setRootNode(boolean isRootNode) {
+ this.isRootNode = isRootNode;
+ }
+
+ /**
+ * Change state.
+ *
+ * @param newState the new state
+ * @param action the action
+ */
+ public void changeState(NodeProcessingState newState, NodeProcessingAction action) {
+ /*
+ * NodeId may be null depending on the current node life-cycle state
+ */
+
+ if (getNodeId() != null) {
+ LOG.info(AaiUiMsgs.ACTIVE_INV_NODE_CHANGE_STATE, state.toString(), newState.toString(), action.toString());
+ } else {
+ LOG.info(AaiUiMsgs.ACTIVE_INV_NODE_CHANGE_STATE_NO_NODE_ID, state.toString(), newState.toString(), action.toString());
+ }
+ this.state = newState;
+ }
+
+ public boolean isSelfLinkPendingResolve() {
+ return selfLinkPendingResolve;
+ }
+
+ public void setSelfLinkPendingResolve(boolean selfLinkPendingResolve) {
+ this.selfLinkPendingResolve = selfLinkPendingResolve;
+ }
+
+ public boolean isSelflinkRetrievalFailure() {
+ return selflinkRetrievalFailure;
+ }
+
+ public void setSelflinkRetrievalFailure(boolean selflinkRetrievalFailure) {
+ this.selflinkRetrievalFailure = selflinkRetrievalFailure;
+ }
+
+ public void setOxmModelLoader(OxmModelLoader loader) {
+ this.oxmModelLoader = loader;
+ }
+
+ public boolean getSelfLinkProcessed() {
+ return selfLinkProcessed.get();
+ }
+
+ public void setSelfLinkProcessed(boolean selfLinkProcessed) {
+ this.selfLinkProcessed.set(selfLinkProcessed);
+ }
+
+ public boolean isDirectSelfLink() {
+ // https://aai-int1.test.att.com:8443/aai/v8/resources/id/2458124400
+ return isDirectSelfLink(this.selfLink);
+ }
+
+ /**
+ * Checks if is direct self link.
+ *
+ * @param link the link
+ * @return true, if is direct self link
+ */
+ public static boolean isDirectSelfLink(String link) {
+ // https://aai-int1.test.att.com:8443/aai/v8/resources/id/2458124400
+
+ if (link == null) {
+ return false;
+ }
+
+ return link.contains("/resources/id/");
+
+ }
+
+ public Map<String, String> getProperties() {
+ return properties;
+ }
+
+ /**
+ * Adds the error cause.
+ *
+ * @param error the error
+ */
+ public void addErrorCause(String error) {
+ if (!errorCauses.contains(error)) {
+ errorCauses.add(error);
+ }
+ }
+
+ /**
+ * Adds the property.
+ *
+ * @param key the key
+ * @param value the value
+ */
+ public void addProperty(String key, String value) {
+ properties.put(key, value);
+ }
+
+ public boolean isProcessingErrorOccurred() {
+ return processingErrorOccurred;
+ }
+
+ public void setProcessingErrorOccurred(boolean processingErrorOccurred) {
+ this.processingErrorOccurred = processingErrorOccurred;
+ }
+
+ public String getNodeId() {
+ return nodeId;
+ }
+
+ public void setNodeId(String nodeId) {
+ this.nodeId = nodeId;
+ }
+
+ public String getEntityType() {
+ return entityType;
+ }
+
+ public void setEntityType(String entityType) {
+ this.entityType = entityType;
+ }
+
+ public String getSelfLink() {
+ return selfLink;
+ }
+
+ /**
+ * Calculate edit attribute uri.
+ *
+ * @param link the link
+ * @return the string
+ */
+ public String calculateEditAttributeUri(String link) {
+ String uri = null;
+ Pattern pattern = Pattern.compile(URIRegexPattern);
+ Matcher matcher = pattern.matcher(link);
+ if (matcher.find()) {
+ uri = link.substring(matcher.end());
+ }
+ return uri;
+ }
+
+ /**
+ * Analyze self link relationship list.
+ *
+ * @param jsonResult the json result
+ * @return the relationship list
+ */
+ private RelationshipList analyzeSelfLinkRelationshipList(String jsonResult) {
+
+
+ RelationshipList relationshipList = null;
+
+ try {
+ relationshipList = mapper.readValue(jsonResult, RelationshipList.class);
+ } catch (Exception exc) {
+ LOG.error(AaiUiMsgs.SELF_LINK_RELATIONSHIP_LIST_ERROR, exc.toString());
+ }
+
+ return relationshipList;
+ }
+
+ /**
+ * Adds the relationship list.
+ *
+ * @param relationshipList the relationship list
+ */
+ public void addRelationshipList(RelationshipList relationshipList) {
+
+ if (!relationshipLists.contains(relationshipList)) {
+ relationshipLists.add(relationshipList);
+ }
+
+ }
+
+ /**
+ * Process pathed self link response.
+ *
+ * @param selfLinkJsonResponse the self link json response
+ * @param startNodeType the start node type
+ * @param startNodeResourceKey the start node resource key
+ */
+ public void processPathedSelfLinkResponse(String selfLinkJsonResponse, String startNodeType,
+ String startNodeResourceKey) {
+
+ if (selfLinkJsonResponse == null || selfLinkJsonResponse.length() == 0) {
+ LOG.error(AaiUiMsgs.SELF_LINK_NULL_EMPTY_RESPONSE);
+ return;
+ }
+
+ try {
+ JsonNode jsonNode = mapper.readValue(selfLinkJsonResponse, JsonNode.class);
+
+ Iterator<Entry<String, JsonNode>> fieldNames = jsonNode.fields();
+ Entry<String, JsonNode> field = null;
+
+ while (fieldNames.hasNext()) {
+
+ field = fieldNames.next();
+
+ /*
+ * Is there a way to tell if the field is an aggregate or an atomic value? This is where our
+ * flattening code needs to live
+ */
+
+ String fieldName = field.getKey();
+
+ if ("relationship-list".equals(fieldName)) {
+
+ /*
+ * Parse the relationship list like we were doing before, so we can determine whether or
+ * not to keep it or traverse it after we have performed the evaluative node depth logic.
+ */
+ RelationshipList relationshipList =
+ analyzeSelfLinkRelationshipList(field.getValue().toString());
+
+ if (relationshipList != null) {
+ this.relationshipLists.add(relationshipList);
+ } else {
+ LOG.info(AaiUiMsgs.NO_RELATIONSHIP_DISCOVERED, nodeId);
+ }
+ } else {
+ JsonNode nodeValue = field.getValue();
+
+ if (nodeValue != null && nodeValue.isValueNode()) {
+
+ /*
+ * before we blindly add the fieldName and value to our property set, let's do one more
+ * check to see if the field name is an entity type. If it is, then our complex
+ * attribute processing code will pick it up and process it instead, but this is
+ * probably more likely just for array node types, but we'll see.
+ */
+
+ if (oxmModelLoader.getEntityDescriptor(fieldName) == null) {
+ /*
+ * this is no an entity type as far as we can tell, so we can add it to our property
+ * set.
+ */
+
+ addProperty(fieldName, nodeValue.asText());
+
+ }
+
+ } else {
+
+ if (nodeValue.isArray()) {
+
+ /*
+ * make sure array entity-type collection is not an entityType before adding it to the
+ * property set. The expetation is that it will be added the visualization through a
+ * complex group or relationship.
+ */
+
+ if (oxmModelLoader.getEntityDescriptor(field.getKey()) == null) {
+ /*
+ * this is no an entity type as far as we can tell, so we can add it to our property
+ * set.
+ */
+
+ addProperty(field.getKey(), nodeValue.toString());
+
+ }
+
+ } else {
+
+ complexGroups.add(nodeValue);
+
+ }
+
+ }
+
+ }
+
+ }
+
+ } catch (IOException exc) {
+ LOG.error(AaiUiMsgs.JSON_CONVERSION_ERROR, "POJO", exc.getLocalizedMessage());
+ this.setProcessingErrorOccurred(true);
+ this.addErrorCause(
+ "An error occurred while converting JSON into POJO = " + exc.getLocalizedMessage());
+ }
+
+ }
+
+ public void setSelfLink(String selfLink) {
+ this.selfLink = selfLink;
+ }
+
+ /**
+ * Adds the complex group.
+ *
+ * @param complexGroup the complex group
+ */
+ public void addComplexGroup(JsonNode complexGroup) {
+
+ if (!complexGroups.contains(complexGroup)) {
+ complexGroups.add(complexGroup);
+ }
+
+ }
+
+ /**
+ * Gets the padding.
+ *
+ * @param level the level
+ * @param paddingString the padding string
+ * @return the padding
+ */
+ private static String getPadding(int level, String paddingString) {
+ StringBuilder sb = new StringBuilder(32);
+ for (int x = 0; x < level; x++) {
+ sb.append(paddingString);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Dump node tree.
+ *
+ * @param showProperties the show properties
+ * @return the string
+ */
+ public String dumpNodeTree(boolean showProperties) {
+ return dumpNodeTree(0, showProperties);
+ }
+
+ /**
+ * Dump node tree.
+ *
+ * @param level the level
+ * @param showProperties the show properties
+ * @return the string
+ */
+ private String dumpNodeTree(int level, boolean showProperties) {
+ StringBuilder sb = new StringBuilder(128);
+ String padding = getPadding(level, " ");
+
+ sb.append(padding + " -> " + getNodeId() + "]").append("\n");
+ sb.append(padding + " -> primaryKeyName = " + primaryKeyName + "]").append("\n");
+ sb.append(padding + " -> primaryKeyValue = " + primaryKeyValue + "]").append("\n");
+ sb.append(padding + " -> entityType = " + entityType + "]").append("\n");
+
+ if (showProperties) {
+ Set<Entry<String, String>> entries = properties.entrySet();
+ for (Entry<String, String> entry : entries) {
+ sb.append(
+ padding + " ----> " + String.format("[ %s => %s ]", entry.getKey(), entry.getValue()))
+ .append("\n");
+ }
+ }
+
+ sb.append(padding + " ----> " + String.format("[ selfLink => %s ]", getSelfLink()))
+ .append("\n");
+
+ sb.append("\n").append(padding + " ----> Inbound Neighbors:").append("\n");
+
+ for (String inboundNeighbor : inboundNeighbors) {
+ sb.append("\n").append(inboundNeighbor.toString());
+ }
+
+ sb.append(padding + " ----> Outbound Neighbors:").append("\n");
+ sb.append("\n").append(padding + " ----> Outbound Neighbors:").append("\n");
+
+ for (String outboundNeighbor : outboundNeighbors) {
+ sb.append("\n").append(outboundNeighbor.toString());
+ }
+
+ return sb.toString();
+
+ }
+
+ public String getProcessingErrorCauses() {
+
+ StringBuilder sb = new StringBuilder(128);
+
+ for (String c : this.errorCauses) {
+ sb.append(c).append("\n");
+ }
+
+ return sb.toString();
+ }
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/entity/D3VisualizationOutput.java b/src/main/java/org/openecomp/sparky/viewandinspect/entity/D3VisualizationOutput.java
new file mode 100644
index 0000000..c201408
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/entity/D3VisualizationOutput.java
@@ -0,0 +1,132 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.entity;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectWriter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The Class D3VisualizationOutput.
+ */
+public class D3VisualizationOutput {
+
+ public GraphMeta graphMeta;
+ public List<JsonNode> nodes;
+ public List<JsonNodeLink> links;
+ public InlineMessage inlineMessage;
+
+ /**
+ * Instantiates a new d 3 visualization output.
+ */
+ public D3VisualizationOutput() {
+ nodes = new ArrayList<JsonNode>();
+ links = new ArrayList<JsonNodeLink>();
+ inlineMessage = null;
+ }
+
+ public GraphMeta getGraphMeta() {
+ return graphMeta;
+ }
+
+ /**
+ * Peg counter.
+ *
+ * @param counterName the counter name
+ */
+ public void pegCounter(String counterName) {
+ graphMeta.pegCounter(counterName);
+ }
+
+ public void setGraphMeta(GraphMeta graphMeta) {
+ this.graphMeta = graphMeta;
+ }
+
+ /**
+ * Adds the nodes.
+ *
+ * @param nodes the nodes
+ */
+ public void addNodes(List<JsonNode> nodes) {
+ this.nodes.addAll(nodes);
+ }
+
+ /**
+ * Adds the links.
+ *
+ * @param links the links
+ */
+ public void addLinks(List<JsonNodeLink> links) {
+ this.links.addAll(links);
+ }
+
+ public InlineMessage getInlineMessage() {
+ return inlineMessage;
+ }
+
+ public void setInlineMessage(InlineMessage inlineMessage) {
+ this.inlineMessage = inlineMessage;
+ }
+
+ /**
+ * The main method.
+ *
+ * @param args the arguments
+ * @throws JsonProcessingException the json processing exception
+ */
+ public static final void main(String[] args) throws JsonProcessingException {
+
+ ActiveInventoryNode pserverAin = new ActiveInventoryNode();
+ pserverAin.setNodeId("pserver.76786asd87asgd");
+ JsonNode pserver = new JsonNode(pserverAin);
+
+ List<JsonNode> nodes = new ArrayList<JsonNode>();
+ nodes.add(pserver);
+
+ JsonNodeLink l1 = new JsonNodeLink();
+ l1.setSource(pserverAin.getNodeId());
+ l1.setTarget(pserverAin.getNodeId());
+ l1.setId(l1.getSource() + "_" + l1.getTarget());
+
+ List<JsonNodeLink> links = new ArrayList<JsonNodeLink>();
+ links.add(l1);
+
+ D3VisualizationOutput output = new D3VisualizationOutput();
+ output.addNodes(nodes);
+ output.addLinks(links);
+
+
+ ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
+ String json = ow.writeValueAsString(output);
+
+ System.out.println(json);
+
+ }
+
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/entity/EntityEntry.java b/src/main/java/org/openecomp/sparky/viewandinspect/entity/EntityEntry.java
new file mode 100644
index 0000000..0d5699d
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/entity/EntityEntry.java
@@ -0,0 +1,82 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.entity;
+
+/**
+ * The Class EntityEntry.
+ */
+public class EntityEntry {
+
+ private String entityType;
+
+ private String entityPrimaryKeyValue;
+
+ private String searchTags;
+
+ private String entityId;
+
+ public String getEntityId() {
+ return entityId;
+ }
+
+ public void setEntityId(String entityId) {
+ this.entityId = entityId;
+ }
+
+ public String getEntityType() {
+ return entityType;
+ }
+
+ public void setEntityType(String entityType) {
+ this.entityType = entityType;
+ }
+
+ public String getEntityPrimaryKeyValue() {
+ return entityPrimaryKeyValue;
+ }
+
+ public void setEntityPrimaryKeyValue(String entityPrimaryKeyValue) {
+ this.entityPrimaryKeyValue = entityPrimaryKeyValue;
+ }
+
+ public String getSearchTags() {
+ return searchTags;
+ }
+
+ public void setSearchTags(String searchTags) {
+ this.searchTags = searchTags;
+ }
+
+ @Override
+ public String toString() {
+ return "EntityEntry [" + (entityType != null ? "entityType=" + entityType + ", " : "")
+ + (entityPrimaryKeyValue != null ? "entityPrimaryKeyValue=" + entityPrimaryKeyValue + ", "
+ : "")
+ + (searchTags != null ? "searchTags=" + searchTags + ", " : "")
+ + (entityId != null ? "entityId=" + entityId : "") + "]";
+ }
+
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/entity/GraphMeta.java b/src/main/java/org/openecomp/sparky/viewandinspect/entity/GraphMeta.java
new file mode 100644
index 0000000..1409501
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/entity/GraphMeta.java
@@ -0,0 +1,148 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.entity;
+
+import org.openecomp.sparky.viewandinspect.EntityTypeAggregation;
+
+/**
+ * The Class GraphMeta.
+ */
+public class GraphMeta {
+
+ private com.fasterxml.jackson.databind.JsonNode aaiEntityNodeDescriptors;
+
+ private int numNodes;
+
+ private int numLinks;
+
+ private long renderTimeInMs;
+
+ private int numLinksResolvedSuccessfullyFromCache;
+
+ private int numLinksResolvedSuccessfullyFromServer;
+
+ private int numLinkResolveFailed;
+
+ private EntityTypeAggregation entitySummary;
+
+ /**
+ * Instantiates a new graph meta.
+ */
+ public GraphMeta() {
+ entitySummary = new EntityTypeAggregation();
+ }
+
+ public EntityTypeAggregation getEntitySummary() {
+ return entitySummary;
+ }
+
+ public void setEntitySummary(EntityTypeAggregation entitySummary) {
+ this.entitySummary = entitySummary;
+ }
+
+ public com.fasterxml.jackson.databind.JsonNode getAaiEntityNodeDescriptors() {
+ return aaiEntityNodeDescriptors;
+ }
+
+ public void setAaiEntityNodeDescriptors(
+ com.fasterxml.jackson.databind.JsonNode aaiEntityNodeDefinitions) {
+ 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;
+ }
+
+ public void setNumNodes(int numNodes) {
+ this.numNodes = numNodes;
+ }
+
+ public int getNumLinks() {
+ return numLinks;
+ }
+
+ public void setNumLinks(int numLinks) {
+ this.numLinks = numLinks;
+ }
+
+ public long getRenderTimeInMs() {
+ return renderTimeInMs;
+ }
+
+ public void setRenderTimeInMs(long renderTimeInMs) {
+ this.renderTimeInMs = renderTimeInMs;
+ }
+
+ /**
+ * Peg counter.
+ *
+ * @param counterName the counter name
+ */
+ public void pegCounter(String counterName) {
+ 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 : "") + "]";
+ }
+
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/entity/InlineMessage.java b/src/main/java/org/openecomp/sparky/viewandinspect/entity/InlineMessage.java
new file mode 100644
index 0000000..1a4dd58
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/entity/InlineMessage.java
@@ -0,0 +1,71 @@
+/**
+ * ============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.openecomp.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/src/main/java/org/openecomp/sparky/viewandinspect/entity/JsonNode.java b/src/main/java/org/openecomp/sparky/viewandinspect/entity/JsonNode.java
new file mode 100644
index 0000000..9db06f0
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/entity/JsonNode.java
@@ -0,0 +1,197 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.entity;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+/*
+ * We can use annotations to differentiate between intermediate data we use to build the node, and
+ * the data that we actually want to appear in the exported JSON.
+ */
+
+/*
+ * This is our current ( 14-June-2016 ) working schema that will remain organic until we get it just
+ * right.
+ *
+ * { "item-type": "customer", "item-name-key": "subscriber-name", “item-name-value” :
+ * “subscriber-name-123456789-aai847-data-01”, "item-properties": [{ "property-name":
+ * "subscriber-name", "property-value": "subscriber-name-123456789-aai847-data-01" }, {
+ * "property-name": "global-customer-id", "property-value":
+ * "global-customer-id-123456789-aai847-data-01" } ], "node-meta": { “color” : “#f2d2d2”,
+ * "isSearchTarget" : false, "nodeGroups" : "1,2,3,4" }, }
+ *
+ */
+
+
+/**
+ * The Class JsonNode.
+ */
+public class JsonNode {
+
+ private String id;
+ private String itemType;
+ private String itemNameKey;
+ private String itemNameValue;
+ private Map<String, String> itemProperties;
+ private NodeMeta nodeMeta;
+
+ @JsonIgnore
+ private boolean isRootNode;
+
+
+ @JsonIgnore
+ private String resourceKey;
+ @JsonIgnore
+ private Collection<String> inboundNeighbors;
+
+ @JsonIgnore
+ private Collection<String> outboundNeighbors;
+
+
+ @JsonIgnore
+ private static final Logger LOG = Logger.getLogger(JsonNode.class);
+
+ /**
+ * Instantiates a new json node.
+ *
+ * @param ain the ain
+ */
+ public JsonNode(ActiveInventoryNode ain) {
+ this.resourceKey = ain.getNodeId();
+ this.itemProperties = ain.getProperties();
+ this.setItemType(ain.getEntityType());
+ this.setItemNameKey(ain.getPrimaryKeyName());
+ this.setItemNameValue(ain.getPrimaryKeyValue());
+ this.setId(ain.getNodeId());
+ this.isRootNode = ain.isRootNode();
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("---");
+ LOG.debug("JsonNode constructor using AIN = " + ain.dumpNodeTree(true));
+ LOG.debug("---");
+ }
+
+ inboundNeighbors = ain.getInboundNeighbors();
+ outboundNeighbors = ain.getOutboundNeighbors();
+
+ nodeMeta = new NodeMeta();
+
+ nodeMeta.setNodeIssue(ain.isNodeIssue());
+ nodeMeta.setNodeDepth(ain.getNodeDepth());
+
+ nodeMeta.setNumInboundNeighbors(ain.getInboundNeighbors().size());
+ nodeMeta.setNumOutboundNeighbors(ain.getOutboundNeighbors().size());
+
+ nodeMeta.setAtMaxDepth(ain.isAtMaxDepth());
+ nodeMeta.setSelfLinkResolved(!ain.isSelflinkRetrievalFailure());
+ nodeMeta.setProcessingErrorOccurred(ain.isProcessingErrorOccurred());
+ nodeMeta.setHasNeighbors(
+ ain.getOutboundNeighbors().size() > 0 || ain.getInboundNeighbors().size() > 0);
+ nodeMeta.setProcessingState(ain.getState());
+
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getItemNameKey() {
+ return itemNameKey;
+ }
+
+ public String getItemNameValue() {
+ return itemNameValue;
+ }
+
+ public Map<String, String> getItemProperties() {
+ return itemProperties;
+ }
+
+ public String getItemType() {
+ return itemType;
+ }
+
+ public String getResourceKey() {
+ return resourceKey;
+ }
+
+ public void setItemNameKey(String itemNameKey) {
+ this.itemNameKey = itemNameKey;
+ }
+
+ public void setItemNameValue(String itemNameValue) {
+ this.itemNameValue = itemNameValue;
+ }
+
+ public void setItemProperties(HashMap<String, String> itemProperties) {
+ this.itemProperties = itemProperties;
+ }
+
+ public void setItemType(String itemType) {
+ this.itemType = itemType;
+ }
+
+ public void setResourceKey(String resourceKey) {
+ this.resourceKey = resourceKey;
+ }
+
+ public NodeMeta getNodeMeta() {
+ return nodeMeta;
+ }
+
+ public void setNodeMeta(NodeMeta nodeMeta) {
+ this.nodeMeta = nodeMeta;
+ }
+
+ public boolean isRootNode() {
+ return isRootNode;
+ }
+
+ @Override
+ public String toString() {
+ return "JsonNode [" + (id != null ? "id=" + id + ", " : "")
+ + (itemType != null ? "itemType=" + itemType + ", " : "")
+ + (itemNameKey != null ? "itemNameKey=" + itemNameKey + ", " : "")
+ + (itemNameValue != null ? "itemNameValue=" + itemNameValue + ", " : "")
+ + (itemProperties != null ? "itemProperties=" + itemProperties + ", " : "")
+ + (nodeMeta != null ? "nodeMeta=" + nodeMeta + ", " : "") + "isRootNode=" + isRootNode
+ + ", " + (resourceKey != null ? "resourceKey=" + resourceKey + ", " : "")
+ + (inboundNeighbors != null ? "inboundNeighbors=" + inboundNeighbors + ", " : "")
+ + (outboundNeighbors != null ? "outboundNeighbors=" + outboundNeighbors : "") + "]";
+ }
+
+
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/entity/JsonNodeLink.java b/src/main/java/org/openecomp/sparky/viewandinspect/entity/JsonNodeLink.java
new file mode 100644
index 0000000..f6be171
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/entity/JsonNodeLink.java
@@ -0,0 +1,76 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.entity;
+
+/*
+ * Expected JSON Output:
+ *
+ * { JsonNodeLink : { id : <value>, source : <value>, target : <value> } }
+ *
+ */
+
+/**
+ * The Class JsonNodeLink.
+ */
+public class JsonNodeLink {
+
+ protected String id;
+ protected String source;
+ protected String target;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getSource() {
+ return source;
+ }
+
+ public void setSource(String source) {
+ this.source = source;
+ }
+
+ public String getTarget() {
+ return target;
+ }
+
+ public void setTarget(String target) {
+ this.target = target;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "JsonNodeLink [id=" + id + ", source=" + source + ", target=" + target + "]";
+ }
+
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/entity/NodeDebug.java b/src/main/java/org/openecomp/sparky/viewandinspect/entity/NodeDebug.java
new file mode 100644
index 0000000..64f5333
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/entity/NodeDebug.java
@@ -0,0 +1,60 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.entity;
+
+/**
+ * The Class NodeDebug.
+ */
+public class NodeDebug {
+ private boolean maxTraversalDepthReached;
+ private boolean processingError;
+ private String processingErrorCauses;
+
+ public boolean isMaxTraversalDepthReached() {
+ return maxTraversalDepthReached;
+ }
+
+ public void setMaxTraversalDepthReached(boolean maxTraversalDepthReached) {
+ this.maxTraversalDepthReached = maxTraversalDepthReached;
+ }
+
+ public boolean isProcessingError() {
+ return processingError;
+ }
+
+ public void setProcessingError(boolean processingError) {
+ this.processingError = processingError;
+ }
+
+ public String getProcessingErrorCauses() {
+ return processingErrorCauses;
+ }
+
+ public void setProcessingErrorCauses(String processingErrorCauses) {
+ this.processingErrorCauses = processingErrorCauses;
+ }
+
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/entity/NodeMeta.java b/src/main/java/org/openecomp/sparky/viewandinspect/entity/NodeMeta.java
new file mode 100644
index 0000000..6df5e73
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/entity/NodeMeta.java
@@ -0,0 +1,212 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.entity;
+
+import org.openecomp.sparky.viewandinspect.config.VisualizationConfig;
+import org.openecomp.sparky.viewandinspect.enumeration.NodeProcessingState;
+
+/**
+ * The Class NodeMeta.
+ */
+public class NodeMeta {
+
+ private String className;
+
+ private boolean isEnrichableNode;
+ private boolean isSearchTarget;
+
+ private NodeDebug nodeDebug;
+ private boolean nodeIssue;
+ private boolean nodeValidated;
+ private long selfLinkResponseTimeInMs;
+ private long numInboundNeighbors;
+ private long numOutboundNeighbors;
+
+ private boolean atMaxDepth;
+ private boolean selfLinkResolved;
+ private boolean processingErrorOccurred;
+ private boolean neighborsProcessed;
+ private int nodeDepth;
+ private boolean hasNeighbors;
+
+ private NodeProcessingState processingState;
+
+ /**
+ * Instantiates a new node meta.
+ */
+ public NodeMeta() {
+ this.isSearchTarget = false;
+ this.isEnrichableNode = false;
+
+ if (VisualizationConfig.getConfig().isVisualizationDebugEnabled()) {
+ nodeDebug = new NodeDebug();
+ }
+ this.numInboundNeighbors = 0;
+ this.numOutboundNeighbors = 0;
+
+ this.selfLinkResponseTimeInMs = 0;
+
+ this.atMaxDepth = false;
+ this.selfLinkResolved = false;
+ this.processingErrorOccurred = false;
+ this.hasNeighbors = false;
+ this.neighborsProcessed = false;
+ this.nodeDepth = ActiveInventoryNode.DEFAULT_INIT_NODE_DEPTH;
+ this.processingState = NodeProcessingState.INIT;
+
+ }
+
+ public boolean isAtMaxDepth() {
+ return atMaxDepth;
+ }
+
+ public void setAtMaxDepth(boolean atMaxDepth) {
+ this.atMaxDepth = atMaxDepth;
+ }
+
+ public boolean isSelfLinkResolved() {
+ return selfLinkResolved;
+ }
+
+
+
+ public NodeProcessingState getProcessingState() {
+ return processingState;
+ }
+
+ public void setProcessingState(NodeProcessingState processingState) {
+ this.processingState = processingState;
+ }
+
+ public void setSelfLinkResolved(boolean selfLinkResolved) {
+ this.selfLinkResolved = selfLinkResolved;
+ }
+
+ public boolean isProcessingErrorOccurred() {
+ return processingErrorOccurred;
+ }
+
+ public void setProcessingErrorOccurred(boolean processingErrorOccurred) {
+ this.processingErrorOccurred = processingErrorOccurred;
+ }
+
+ public boolean isHasNeighbors() {
+ return hasNeighbors;
+ }
+
+ public void setHasNeighbors(boolean hasNeighbors) {
+ this.hasNeighbors = hasNeighbors;
+ }
+
+ public boolean isNeighborsProcessed() {
+ return neighborsProcessed;
+ }
+
+ public void setNeighborsProcessed(boolean neighborsProcessed) {
+ this.neighborsProcessed = neighborsProcessed;
+ }
+
+ public int getNodeDepth() {
+ return nodeDepth;
+ }
+
+ public void setNodeDepth(int nodeDepth) {
+ this.nodeDepth = nodeDepth;
+ }
+
+ public void setNodeDebug(NodeDebug nodeDebug) {
+ this.nodeDebug = nodeDebug;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ public long getNumInboundNeighbors() {
+ return numInboundNeighbors;
+ }
+
+ public void setNumInboundNeighbors(long numInboundNeighbors) {
+ this.numInboundNeighbors = numInboundNeighbors;
+ }
+
+ public long getNumOutboundNeighbors() {
+ return numOutboundNeighbors;
+ }
+
+ public void setNumOutboundNeighbors(long numOutboundNeighbors) {
+ this.numOutboundNeighbors = numOutboundNeighbors;
+ }
+
+ public NodeDebug getNodeDebug() {
+ return nodeDebug;
+ }
+
+ public long getSelfLinkResponseTimeInMs() {
+ return selfLinkResponseTimeInMs;
+ }
+
+ public boolean isEnrichableNode() {
+ return isEnrichableNode;
+ }
+
+ public boolean isNodeIssue() {
+ return nodeIssue;
+ }
+
+ public boolean isNodeValidated() {
+ return nodeValidated;
+ }
+
+ public boolean isSearchTarget() {
+ return isSearchTarget;
+ }
+
+ public void setClassName(String className) {
+ this.className = className;
+ }
+
+ public void setEnrichableNode(boolean isEnrichableNode) {
+ this.isEnrichableNode = isEnrichableNode;
+ }
+
+ public void setNodeIssue(boolean nodeIssue) {
+ this.nodeIssue = nodeIssue;
+ }
+
+ public void setNodeValidated(boolean nodeValidated) {
+ this.nodeValidated = nodeValidated;
+ }
+
+ public void setSearchTarget(boolean isSearchTarget) {
+ this.isSearchTarget = isSearchTarget;
+ }
+
+ public void setSelfLinkResponseTimeInMs(long selfLinkResponseTimeInMs) {
+ this.selfLinkResponseTimeInMs = selfLinkResponseTimeInMs;
+ }
+
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/entity/NodeProcessingTransaction.java b/src/main/java/org/openecomp/sparky/viewandinspect/entity/NodeProcessingTransaction.java
new file mode 100644
index 0000000..f881f06
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/entity/NodeProcessingTransaction.java
@@ -0,0 +1,103 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.entity;
+
+import org.openecomp.sparky.dal.rest.OperationResult;
+
+/**
+ * The Class NodeProcessingTransaction.
+ */
+public class NodeProcessingTransaction {
+
+ private ActiveInventoryNode processingNode;
+ private OperationResult opResult;
+ private String selfLinkWithModifiers;
+ private String requestParameters;
+
+ /**
+ * Instantiates a new node processing transaction.
+ */
+ public NodeProcessingTransaction() {}
+
+ public String getRequestParameters() {
+ return requestParameters;
+ }
+
+ public void setRequestParameters(String requestParameters) {
+ this.requestParameters = requestParameters;
+ }
+
+ public String getSelfLinkWithModifiers() {
+
+ if (processingNode == null) {
+ return null;
+ }
+
+ return processingNode.getSelfLink() + requestParameters;
+ }
+
+ public ActiveInventoryNode getProcessingNode() {
+ return processingNode;
+ }
+
+ public void setProcessingNode(ActiveInventoryNode processingNode) {
+ this.processingNode = processingNode;
+ }
+
+ public OperationResult getOpResult() {
+ return opResult;
+ }
+
+ public void setOpResult(OperationResult opResult) {
+ this.opResult = opResult;
+ }
+
+ /**
+ * Processing error occurred.
+ *
+ * @return true, if successful
+ */
+ public boolean processingErrorOccurred() {
+ if (opResult == null) {
+ return true;
+ }
+
+ return !opResult.wasSuccessful();
+
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "NodeProcessingTransaction ["
+ + (processingNode != null ? "processingNode=" + processingNode + ", " : "")
+ + (opResult != null ? "opResult=" + opResult + ", " : "") + "processorErrorOccurred="
+ + processingErrorOccurred() + "]";
+ }
+
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/entity/QueryParams.java b/src/main/java/org/openecomp/sparky/viewandinspect/entity/QueryParams.java
new file mode 100644
index 0000000..b3592c3
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/entity/QueryParams.java
@@ -0,0 +1,58 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.entity;
+
+/**
+ * The Class QueryParams.
+ */
+public class QueryParams {
+
+ private String searchTargetPrimaryKeyValues;
+ private String searchTargetNodeId;
+
+ /**
+ * Instantiates a new query params.
+ */
+ public QueryParams() {
+
+ }
+
+ public String getSearchTargetPrimaryKeyValues() {
+ return searchTargetPrimaryKeyValues;
+ }
+
+ public void setSearchTargetPrimaryKeyValues(String searchTargetPrimaryKeyValues) {
+ this.searchTargetPrimaryKeyValues = searchTargetPrimaryKeyValues;
+ }
+
+ public String getSearchTargetNodeId() {
+ return searchTargetNodeId;
+ }
+
+ public void setSearchTargetNodeId(String searchTargetNodeId) {
+ this.searchTargetNodeId = searchTargetNodeId;
+ }
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/entity/QueryRequest.java b/src/main/java/org/openecomp/sparky/viewandinspect/entity/QueryRequest.java
new file mode 100644
index 0000000..34c34ef
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/entity/QueryRequest.java
@@ -0,0 +1,48 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.entity;
+
+/**
+ * The Class QueryRequest.
+ */
+public class QueryRequest {
+
+ private String hashId;
+
+ public String getHashId() {
+ return hashId;
+ }
+
+ public void setHashId(String hashId) {
+ this.hashId = hashId;
+ }
+
+ @Override
+ public String toString() {
+ return "QueryRequest [hashId=" + hashId + "]";
+ }
+
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/entity/QuerySearchEntity.java b/src/main/java/org/openecomp/sparky/viewandinspect/entity/QuerySearchEntity.java
new file mode 100644
index 0000000..71b775c
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/entity/QuerySearchEntity.java
@@ -0,0 +1,75 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.entity;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+/**
+ * The Class ViewAndInspectSearchRequest.
+ */
+public class QuerySearchEntity {
+
+ private static final String DEFAULT_MAX_RESULTS = "10";
+ public String maxResults;
+
+ public String queryStr;
+
+ /**
+ * Instantiates a new view and inspect search request.
+ */
+ public QuerySearchEntity() {
+ maxResults = DEFAULT_MAX_RESULTS;
+ queryStr = null;
+ }
+
+ public String getMaxResults() {
+ return maxResults;
+ }
+
+ public void setMaxResults(String maxResults) {
+ this.maxResults = maxResults;
+ }
+
+ public String getQueryStr() {
+ return queryStr;
+ }
+
+ public void setQueryStr(String queryStr) {
+ this.queryStr = queryStr;
+ }
+
+ @JsonIgnore
+ public String[] getSearchTerms() {
+
+ if (queryStr == null) {
+ return null;
+ }
+
+ return queryStr.split(" ");
+
+ }
+
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/entity/RelatedToProperty.java b/src/main/java/org/openecomp/sparky/viewandinspect/entity/RelatedToProperty.java
new file mode 100644
index 0000000..a4c72b0
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/entity/RelatedToProperty.java
@@ -0,0 +1,65 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.entity;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * The Class RelatedToProperty.
+ */
+public class RelatedToProperty {
+ protected String propertyKey;
+ protected String propertyValue;
+
+ @JsonProperty("property-key")
+ public String getPropertyKey() {
+ return propertyKey;
+ }
+
+ public void setPropertyKey(String propertyKey) {
+ this.propertyKey = propertyKey;
+ }
+
+ @JsonProperty("property-value")
+ public String getPropertyValue() {
+ return propertyValue;
+ }
+
+ public void setPropertyValue(String propertyValue) {
+ this.propertyValue = propertyValue;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "RelatedToProperty [propertyKey=" + propertyKey + ", propertyValue=" + propertyValue
+ + "]";
+ }
+
+
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/entity/Relationship.java b/src/main/java/org/openecomp/sparky/viewandinspect/entity/Relationship.java
new file mode 100644
index 0000000..e82ef3a
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/entity/Relationship.java
@@ -0,0 +1,92 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.entity;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.Arrays;
+
+/**
+ * The Class Relationship.
+ */
+public class Relationship {
+
+ protected String relatedTo;
+ protected String relatedLink;
+ protected RelationshipData[] relationshipData;
+ protected RelatedToProperty[] relatedToProperty;
+
+ public String getRelatedTo() {
+ return relatedTo;
+ }
+
+ @JsonProperty("related-to")
+ public void setRelatedTo(String relatedTo) {
+ this.relatedTo = relatedTo;
+ }
+
+ public String getRelatedLink() {
+ return relatedLink;
+ }
+
+ @JsonProperty("related-link")
+ public void setRelatedLink(String relatedLink) {
+ this.relatedLink = relatedLink;
+ }
+
+ public RelationshipData[] getRelationshipData() {
+ return relationshipData;
+ }
+
+ @JsonProperty("relationship-data")
+ public void setRelationshipData(RelationshipData[] relationshipData) {
+ this.relationshipData = relationshipData;
+ }
+
+
+
+ public RelatedToProperty[] getRelatedToProperty() {
+ return relatedToProperty;
+ }
+
+ @JsonProperty("related-to-property")
+ public void setRelatedToProperty(RelatedToProperty[] relatedToProperty) {
+ this.relatedToProperty = relatedToProperty;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "Relationship [relatedTo=" + relatedTo + ", relatedLink=" + relatedLink
+ + ", relationshipData=" + Arrays.toString(relationshipData) + ", relatedToProperty="
+ + Arrays.toString(relatedToProperty) + "]";
+ }
+
+
+
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/entity/RelationshipData.java b/src/main/java/org/openecomp/sparky/viewandinspect/entity/RelationshipData.java
new file mode 100644
index 0000000..d290fef
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/entity/RelationshipData.java
@@ -0,0 +1,64 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.entity;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * The Class RelationshipData.
+ */
+public class RelationshipData {
+ protected String relationshipKey;
+ protected String relationshipValue;
+
+ @JsonProperty("relationship-key")
+ public String getRelationshipKey() {
+ return relationshipKey;
+ }
+
+ public void setRelationshipKey(String relationshipKey) {
+ this.relationshipKey = relationshipKey;
+ }
+
+ @JsonProperty("relationship-value")
+ public String getRelationshipValue() {
+ return relationshipValue;
+ }
+
+ public void setRelationshipValue(String relationshipValue) {
+ this.relationshipValue = relationshipValue;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "RelationshipData [relationshipKey=" + relationshipKey + ", relationshipValue="
+ + relationshipValue + "]";
+ }
+
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/entity/RelationshipDirectionality.java b/src/main/java/org/openecomp/sparky/viewandinspect/entity/RelationshipDirectionality.java
new file mode 100644
index 0000000..3c273dc
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/entity/RelationshipDirectionality.java
@@ -0,0 +1,43 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.entity;
+
+/**
+ * This enumeration is intended to be used to help us discriminate neighbor relationships for the
+ * purpose of visualization and conceptualization to model in/out relationships between
+ * ActiveInventoryNodes.
+ * Possible visualization behaviors could be the following: - IN ( draw a line with 1 arrow ) - OUT
+ * ( draw a line with 1 arrow ) - BOTH ( draw a line with 2 arrows, or 2 lines with 1 arrow each ) -
+ * UNKNOWN ( draw a line with no arrows )
+ * The UNKNOWN case is what we have at the moment where we have a collection neighbors with no
+ * knowledge of relationship directionality.
+ *
+ * @author davea
+ *
+ */
+public enum RelationshipDirectionality {
+ IN, OUT, BOTH, UNKNOWN
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/entity/RelationshipList.java b/src/main/java/org/openecomp/sparky/viewandinspect/entity/RelationshipList.java
new file mode 100644
index 0000000..257b68c
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/entity/RelationshipList.java
@@ -0,0 +1,58 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.entity;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.Arrays;
+
+/**
+ * The Class RelationshipList.
+ */
+public class RelationshipList {
+
+ protected Relationship[] relationship;
+
+ public Relationship[] getRelationshipList() {
+ return relationship;
+ }
+
+ @JsonProperty("relationship")
+ public void setRelationshipList(Relationship[] relationship) {
+ this.relationship = relationship;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "RelationshipList [relationshipList=" + Arrays.toString(relationship) + "]";
+ }
+
+
+
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/entity/SearchResponse.java b/src/main/java/org/openecomp/sparky/viewandinspect/entity/SearchResponse.java
new file mode 100644
index 0000000..8cc8d9b
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/entity/SearchResponse.java
@@ -0,0 +1,93 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.entity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openecomp.sparky.suggestivesearch.SuggestionEntity;
+
+/**
+ * The Class SearchResponse.
+ */
+public class SearchResponse {
+
+ private long processingTimeInMs;
+ private int totalFound;
+
+ private List<SuggestionEntity> suggestions;
+
+ /**
+ * Instantiates a new search response.
+ */
+ public SearchResponse() {
+ this.suggestions = new ArrayList<SuggestionEntity>();
+ this.processingTimeInMs = 0;
+ this.totalFound = 0;
+ }
+
+ public long getProcessingTimeInMs() {
+ return processingTimeInMs;
+ }
+
+ public void setProcessingTimeInMs(long processingTimeInMs) {
+ this.processingTimeInMs = processingTimeInMs;
+ }
+
+ public int getTotalFound() {
+ return totalFound;
+ }
+
+ public void setTotalFound(int totalFound) {
+ this.totalFound = totalFound;
+ }
+
+ public List<SuggestionEntity> getSuggestions() {
+ return suggestions;
+ }
+
+ public void setSuggestions(List<SuggestionEntity> suggestions) {
+ this.suggestions = suggestions;
+ }
+ /**
+ * Adds the entity entry.
+ *
+ * @param suggestionEntry that will be converted to JSON
+ */
+ public void addSuggestion(SuggestionEntity suggestionEntity){
+ suggestions.add(suggestionEntity);
+ }
+
+ /**
+ * Increments the total number of hits for this SearchResponse by
+ * the value passed in.
+ *
+ * @param additionalCount - Count to increment the total found
+ */
+ public void addToTotalFound(int additionalCount) {
+ totalFound += additionalCount;
+ }
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/entity/SelfLinkDeterminationTransaction.java b/src/main/java/org/openecomp/sparky/viewandinspect/entity/SelfLinkDeterminationTransaction.java
new file mode 100644
index 0000000..fbe6325
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/entity/SelfLinkDeterminationTransaction.java
@@ -0,0 +1,82 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.entity;
+
+import org.openecomp.sparky.dal.rest.OperationResult;
+
+public class SelfLinkDeterminationTransaction {
+
+ private String parentNodeId;
+ private ActiveInventoryNode newNode;
+ private String queryString;
+ private String entityUrl;
+ private OperationResult opResult;
+
+
+
+ public String getParentNodeId() {
+ return parentNodeId;
+ }
+
+ public void setParentNodeId(String parentNodeId) {
+ this.parentNodeId = parentNodeId;
+ }
+
+ public ActiveInventoryNode getNewNode() {
+ return newNode;
+ }
+
+ public void setNewNode(ActiveInventoryNode newNode) {
+ this.newNode = newNode;
+ }
+
+ public OperationResult getOpResult() {
+ return opResult;
+ }
+
+ public void setOpResult(OperationResult opResult) {
+ this.opResult = opResult;
+ }
+
+ public String getQueryString() {
+ return queryString;
+ }
+
+ public void setQueryString(String queryString) {
+ this.queryString = queryString;
+ }
+
+ public String getEntityUrl() {
+ return entityUrl;
+ }
+
+ public void setEntityUrl(String entityUrl) {
+ this.entityUrl = entityUrl;
+ }
+
+
+
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/entity/Violations.java b/src/main/java/org/openecomp/sparky/viewandinspect/entity/Violations.java
new file mode 100644
index 0000000..a921782
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/entity/Violations.java
@@ -0,0 +1,114 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.entity;
+
+import com.att.aft.dme2.internal.jackson.annotate.JsonProperty;
+
+/**
+ * The Class Violations.
+ */
+public class Violations {
+
+ private String severity;
+
+ private String category;
+
+ private String type;
+
+ private String timestamp;
+
+ private String details;
+
+ @JsonProperty("error-message")
+ private String errorMessage;
+
+ /**
+ * Instantiates a new violations.
+ *
+ * @param severity the severity
+ * @param category the category
+ * @param type the type
+ * @param timestamp the timestamp
+ * @param errorMessage the error message
+ */
+ public Violations(String severity, String category, String type, String timestamp,
+ String errorMessage) {
+ this.severity = severity;
+ this.category = category;
+ this.type = type;
+ this.timestamp = timestamp;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getSeverity() {
+ return severity;
+ }
+
+ public void setSeverity(String severity) {
+ this.severity = severity;
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ public void setCategory(String category) {
+ this.category = category;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+
+ public String getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(String timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ /*
+ * public Map<String, Object> getDetails() { return details; }
+ *
+ * public void setDetails(Map<String, Object> details) { this.details = details; }
+ */
+
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+
+ public void setErrorMessage(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+
+
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/enumeration/NodeProcessingAction.java b/src/main/java/org/openecomp/sparky/viewandinspect/enumeration/NodeProcessingAction.java
new file mode 100644
index 0000000..7c9befa
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/enumeration/NodeProcessingAction.java
@@ -0,0 +1,36 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.enumeration;
+
+/**
+ * The Enum NodeProcessingAction.
+ */
+public enum NodeProcessingAction {
+ SELF_LINK_SET, NEW_NODE_PROCESSED, SELF_LINK_RESOLVE_ERROR, SELF_LINK_DETERMINATION_ERROR,
+ SELF_LINK_RESOLVE_OK, SELF_LINK_RESPONSE_PARSE_ERROR, SELF_LINK_RESPONSE_PARSE_OK,
+ NEIGHBORS_PROCESSED_ERROR, NEIGHBORS_PROCESSED_OK, COMPLEX_ATTRIBUTE_GROUP_PARSE_ERROR,
+ COMPLEX_ATTRIBUTE_GROUP_PARSE_OK, NODE_IDENTITY_ERROR, UNEXPECTED_STATE_TRANSITION
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/enumeration/NodeProcessingState.java b/src/main/java/org/openecomp/sparky/viewandinspect/enumeration/NodeProcessingState.java
new file mode 100644
index 0000000..344f8df
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/enumeration/NodeProcessingState.java
@@ -0,0 +1,33 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.enumeration;
+
+/**
+ * The Enum NodeProcessingState.
+ */
+public enum NodeProcessingState {
+ INIT, SELF_LINK_UNRESOLVED, SELF_LINK_RESPONSE_UNPROCESSED, NEIGHBORS_UNPROCESSED, READY, ERROR
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/services/SearchServiceWrapper.java b/src/main/java/org/openecomp/sparky/viewandinspect/services/SearchServiceWrapper.java
new file mode 100644
index 0000000..41f0eff
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/services/SearchServiceWrapper.java
@@ -0,0 +1,809 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.services;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.openecomp.cl.api.Logger;
+import org.openecomp.cl.eelf.LoggerFactory;
+import org.openecomp.sparky.config.oxm.OxmEntityDescriptor;
+import org.openecomp.sparky.config.oxm.OxmModelLoader;
+import org.openecomp.sparky.dal.elasticsearch.HashQueryResponse;
+import org.openecomp.sparky.dal.elasticsearch.SearchAdapter;
+import org.openecomp.sparky.dal.rest.OperationResult;
+import org.openecomp.sparky.dal.sas.config.SearchServiceConfig;
+import org.openecomp.sparky.logging.AaiUiMsgs;
+import org.openecomp.sparky.search.VnfSearchService;
+import org.openecomp.sparky.search.config.SuggestionConfig;
+import org.openecomp.sparky.suggestivesearch.SuggestionEntity;
+import org.openecomp.sparky.util.NodeUtils;
+import org.openecomp.sparky.viewandinspect.entity.QuerySearchEntity;
+import org.openecomp.sparky.viewandinspect.entity.SearchResponse;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+
+/**
+ * The Class SearchServlet.
+ */
+
+public class SearchServiceWrapper {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final Logger LOG = LoggerFactory.getInstance().getLogger(SearchServiceWrapper.class);
+
+ private SearchServiceConfig sasConfig = null;
+ private SuggestionConfig suggestionConfig = null;
+ private SearchAdapter search = null;
+ private ObjectMapper mapper;
+ private OxmModelLoader oxmModelLoader;
+ private VnfSearchService vnfSearch = null;
+
+ private static final String SEARCH_STRING = "search";
+ private static final String COUNT_STRING = "count";
+ private static final String QUERY_SEARCH = SEARCH_STRING + "/querysearch";
+ private static final String SUMMARY_BY_ENTITY_TYPE_API = SEARCH_STRING + "/summarybyentitytype";
+ private static final String SUMMARY_BY_ENTITY_TYPE_COUNT_API =
+ SUMMARY_BY_ENTITY_TYPE_API + "/" + COUNT_STRING;
+
+ private static final String VALUE_ANYKEY = "anyKey";
+ private static final String VALUE_QUERY = "query";
+
+ private static final String KEY_HASH_ID = "hashId";
+ private static final String KEY_GROUP_BY = "groupby";
+ private static final String KEY_SEARCH_RESULT = "searchResult";
+ private static final String KEY_HITS = "hits";
+ private static final String KEY_PAYLOAD = "payload";
+ private static final String KEY_DOCUMENT = "document";
+ private static final String KEY_CONTENT = "content";
+ private static final String KEY_SEARCH_TAG_IDS = "searchTagIDs";
+ private static final String KEY_SEARCH_TAGS = "searchTags";
+ private static final String KEY_LINK = "link";
+ private static final String KEY_ENTITY_TYPE = "entityType";
+
+ private static final String VI_SUGGESTION_ROUTE = "viewInspect"; // TODO -> Read route from
+ // suggestive-search.properties
+ // instead of hard coding
+
+ private static final String VIUI_SEARCH_TEMPLATE =
+ "{ " + "\"results-start\": 0," + "\"results-size\": %d," + "\"queries\": [{" + "\"must\": {"
+ + "\"match\": {" + "\"field\": \"entityType searchTags crossEntityReferenceValues\","
+ + "\"value\": \"%s\"," + "\"operator\": \"and\", "
+ + "\"analyzer\": \"whitespace_analyzer\"" + "}" + "}" + "}]" + "}";
+
+ /**
+ * Instantiates a new search service wrapper
+ */
+ public SearchServiceWrapper() {
+ this.mapper = new ObjectMapper();
+ vnfSearch = new VnfSearchService();
+
+ try {
+ if (sasConfig == null) {
+ sasConfig = SearchServiceConfig.getConfig();
+ }
+
+ if (suggestionConfig == null) {
+ suggestionConfig = SuggestionConfig.getConfig();
+ }
+
+ if (search == null) {
+ search = new SearchAdapter();
+ }
+
+ if (oxmModelLoader == null) {
+ oxmModelLoader = OxmModelLoader.getInstance();
+
+ if (OxmModelLoader.getInstance().getSearchableEntityDescriptors().isEmpty()) {
+ LOG.error(AaiUiMsgs.ENTITY_NOT_FOUND_IN_OXM, "searchable entity");
+ }
+ }
+ } catch (Exception exc) {
+ new ServletException(
+ "Caught an exception while getting an instance of servlet configuration from SearchServlet.", exc);
+ }
+ }
+
+ public void doGet(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ doPost(request, response);
+ }
+
+ public void setSasConfig(SearchServiceConfig sasConfig) {
+ this.sasConfig = sasConfig;
+ }
+
+ public SearchServiceConfig getSasConfig() {
+ return sasConfig;
+ }
+
+ public void setSuggestionConfig(SuggestionConfig suggestionConfig) {
+ this.suggestionConfig = suggestionConfig;
+ }
+
+ public void setSearch(SearchAdapter search) {
+ this.search = search;
+ }
+
+ public SuggestionConfig getSuggestionConfig() {
+ return suggestionConfig;
+ }
+
+ public SearchAdapter getSearch() {
+ return search;
+ }
+
+ public void setOxmModelLoader(OxmModelLoader oxmModelLoader) {
+ this.oxmModelLoader = oxmModelLoader;
+ }
+
+ public OxmModelLoader getOxmModelLoader() {
+ return oxmModelLoader;
+ }
+
+ public VnfSearchService getVnfSearch() {
+ return vnfSearch;
+ }
+
+ public void setVnfSearch(VnfSearchService vnfSearch) {
+ this.vnfSearch = vnfSearch;
+ }
+
+ /**
+ * Get Full URL for search
+ *
+ * @param api the api
+ * @param indexName
+ * @return the full url
+ */
+ private String getSasFullUrl(String indexName, String type, String ipAddress, String port,
+ String version) {
+
+ return String.format("https://%s:%s/services/search-data-service/%s/search/indexes/%s/%s",
+ ipAddress, port, version, indexName, type);
+ }
+
+ /**
+ * Handle search service do query.
+ *
+ * @param app the app
+ * @param request the request
+ * @param response the response
+ * @throws Exception the exception
+ */
+
+ protected JSONObject getRequestParamsFromHeader(HttpServletRequest request) {
+ StringBuffer br = new StringBuffer();
+ String line = null;
+ try {
+ BufferedReader reader = request.getReader();
+ while ((line = reader.readLine()) != null) {
+ br.append(line);
+ }
+ } catch (Exception exc) {
+ LOG.error(AaiUiMsgs.ERROR_READING_HTTP_REQ_PARAMS);
+ }
+
+ String output = br.toString();
+
+ return new JSONObject(output);
+ }
+
+ protected void handleSummaryByEntityTypeCount(HttpServletRequest request,
+ HttpServletResponse response) throws Exception {
+ JSONObject parameters = getRequestParamsFromHeader(request);
+ String hashId = null;
+ if (parameters.has(KEY_HASH_ID)){
+ hashId = parameters.get(KEY_HASH_ID).toString();
+ } else {
+ vnfSearch.setZeroCountResponse(response);
+ LOG.error(AaiUiMsgs.ERROR_HASH_NOT_FOUND);
+ return;
+ }
+ HashQueryResponse hashQueryResponse = getResponseForQueryByHash(hashId, response);
+ Map<String, String> hashQueryResponsePayloadParams = new HashMap<String, String>();
+ if (hashQueryResponse.getJsonPayload() != null) {
+ hashQueryResponsePayloadParams = getPayloadParams(hashQueryResponse.getJsonPayload());
+ vnfSearch.getEntityCountResults(response, hashQueryResponsePayloadParams);
+ } else {
+ vnfSearch.setZeroCountResponse(response);
+ LOG.error(AaiUiMsgs.ERROR_INVALID_HASH, hashId);
+ }
+ }
+
+ protected Map<String, String> getPayloadParams(String parameters) {
+ Map<String, String> payloadParams = new HashMap<String, String>();
+ try {
+ JSONObject json = new JSONObject(parameters);
+ JSONObject payload = json.getJSONObject(KEY_PAYLOAD);
+ if (payload.length() > 0) {
+ for (String key : JSONObject.getNames(payload)) {
+ payloadParams.put(key, payload.getString(key));
+ }
+ }
+ } catch (JSONException exc) {
+ LOG.error(AaiUiMsgs.ERROR_PARSING_PARAMS, exc);
+ }
+ return payloadParams;
+ }
+
+ protected HashQueryResponse getResponseForQueryByHash(String hashId, HttpServletResponse response){
+ return vnfSearch.getJSONPayloadFromHash(hashId);
+ }
+
+ protected void handleSummaryByEntityType(HttpServletRequest request, HttpServletResponse response)
+ throws Exception {
+ JSONObject parameters = getRequestParamsFromHeader(request);
+ String hashId = null;
+ if (parameters.has(KEY_HASH_ID)){
+ hashId = parameters.get(KEY_HASH_ID).toString();
+ } else {
+ vnfSearch.setZeroCountResponse(response);
+ LOG.error(AaiUiMsgs.ERROR_HASH_NOT_FOUND);
+ return;
+ }
+ HashQueryResponse hashQueryResponse = getResponseForQueryByHash(hashId, response);
+ Map<String, String> hashQueryResponsePayloadParams = new HashMap<String, String>();
+ if (hashQueryResponse.getJsonPayload() != null) {
+ hashQueryResponsePayloadParams = getPayloadParams(hashQueryResponse.getJsonPayload());
+ if (parameters.has(KEY_GROUP_BY)){
+ String groupByKey = parameters.getString(KEY_GROUP_BY);
+ vnfSearch.getSummaryByEntityType(response, hashQueryResponsePayloadParams, groupByKey);
+ }
+ } else {
+ LOG.error(AaiUiMsgs.ERROR_INVALID_HASH, hashId);
+ vnfSearch.setEmptyAggResponse(response);
+ }
+ }
+
+ /**
+ * Gets the value from node.
+ *
+ * @param node the node
+ * @param fieldName the field name
+ * @return the value from node
+ */
+ private String getValueFromNode(JsonNode node, String fieldName) {
+
+ if (node == null || fieldName == null) {
+ return null;
+ }
+
+ JsonNode valueNode = node.get(fieldName);
+
+ if (valueNode != null) {
+ return valueNode.asText();
+ }
+
+ return null;
+
+ }
+
+ /**
+ * Builds the search response.
+ *
+ * @param operationResult the operation result
+ * @param queryStr the query str
+ * @return TODO
+ * @return the search response
+ */
+ private List<SuggestionEntity> generateSuggestionsForSearchResponse(String operationResult,
+ String queryStr) {
+
+
+ if (operationResult == null || operationResult.length() == 0) {
+ return null;
+ }
+
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode rootNode = null;
+ List<SuggestionEntity> suggestionEntityList = new ArrayList<SuggestionEntity>();
+ try {
+ rootNode = mapper.readTree(operationResult);
+
+ JsonNode hitsNode = rootNode.get(KEY_SEARCH_RESULT);
+
+
+ // Check if there are hits that are coming back
+ if (hitsNode.has(KEY_HITS)) {
+ ArrayNode hitsArray = (ArrayNode) hitsNode.get(KEY_HITS);
+
+ /*
+ * next we iterate over the values in the hit array elements
+ */
+
+ Iterator<JsonNode> nodeIterator = hitsArray.elements();
+ JsonNode entityNode = null;
+ SuggestionEntity suggestionEntity = null;
+ JsonNode sourceNode = null;
+ while (nodeIterator.hasNext()) {
+ entityNode = nodeIterator.next();
+ sourceNode = entityNode.get(KEY_DOCUMENT).get(KEY_CONTENT);
+
+ // do the point transformation as we build the response?
+ suggestionEntity = new SuggestionEntity();
+ suggestionEntity.setRoute(VI_SUGGESTION_ROUTE);
+
+ /*
+ * This is where we probably want to annotate the search tags because we also have access
+ * to the seachTagIds
+ */
+
+ String searchTagIds = getValueFromNode(sourceNode, KEY_SEARCH_TAG_IDS);
+ String searchTags = getValueFromNode(sourceNode, KEY_SEARCH_TAGS);
+ String link = getValueFromNode(sourceNode, KEY_LINK);
+ String entityType = getValueFromNode(sourceNode, KEY_ENTITY_TYPE);
+ if (link != null) {
+ suggestionEntity.setHashId(NodeUtils.generateUniqueShaDigest(link));
+ }
+
+ try {
+ suggestionEntity
+ .setText(annotateSearchTags(searchTags, searchTagIds, entityType, queryStr));
+ } catch (Exception exc) {
+ LOG.error(AaiUiMsgs.SEARCH_TAG_ANNOTATION_ERROR, searchTags.toString(),
+ exc.getLocalizedMessage());
+ // at least send back the un-annotated search tags
+ suggestionEntity.setText(searchTags);
+ }
+
+ if (searchTags != null) {
+ suggestionEntityList.add(suggestionEntity);
+ }
+
+ }
+ }
+ } catch (IOException exc) {
+ LOG.warn(AaiUiMsgs.SEARCH_RESPONSE_BUILDING_EXCEPTION, exc.getLocalizedMessage());
+ }
+ return suggestionEntityList;
+ }
+
+ /*
+ */
+
+ /**
+ * Query terms match search tag.
+ *
+ * @param queryTerms the query terms
+ * @param searchTag the search tag
+ * @return true, if successful @return.
+ */
+ private boolean queryTermsMatchSearchTag(String[] queryTerms, String searchTag) {
+
+ if (queryTerms == null || queryTerms.length == 0 || searchTag == null) {
+ return false;
+ }
+
+ for (String queryTerm : queryTerms) {
+ if (searchTag.toLowerCase().contains(queryTerm.toLowerCase())) {
+ return true;
+ }
+ }
+
+ return false;
+
+ }
+
+ /**
+ * The current format of an UI-dropdown-item is like: "search-terms entityType att1=attr1_val".
+ * Example, for pserver: search-terms pserver hostname=djmAG-72060,
+ * pserver-name2=example-pserver-name2-val-17254, pserver-id=example-pserver-id-val-17254,
+ * ipv4-oam-address=example-ipv4-oam-address-val-17254 SearchController.js parses the above
+ * format. So if you are modifying the parsing below, please update SearchController.js as well.
+ *
+ * @param searchTags the search tags
+ * @param searchTagIds the search tag ids
+ * @param entityType the entity type
+ * @param queryStr the query str
+ * @return the string
+ */
+
+ private String annotateSearchTags(String searchTags, String searchTagIds, String entityType,
+ String queryStr) {
+
+ if (searchTags == null || searchTagIds == null) {
+ String valueOfSearchTags = String.valueOf(searchTags);
+ String valueOfSearchTagIds = String.valueOf(searchTagIds);
+
+ LOG.error(AaiUiMsgs.SEARCH_TAG_ANNOTATION_ERROR, "See error",
+ "Search tags = " + valueOfSearchTags + " and Seach tag IDs = " + valueOfSearchTagIds);
+ return searchTags;
+ }
+
+ if (entityType == null) {
+ LOG.error(AaiUiMsgs.SEARCH_TAG_ANNOTATION_ERROR, searchTags.toString(), "EntityType is null");
+ return searchTags;
+ }
+
+ if (queryStr == null) {
+ LOG.error(AaiUiMsgs.SEARCH_TAG_ANNOTATION_ERROR, searchTags.toString(),
+ "Query string is null");
+ return searchTags;
+ }
+
+ /*
+ * The ElasticSearch analyzer has already applied the lowercase filter, so we don't have to
+ * covert them again
+ */
+ String[] searchTagsArray = searchTags.split(";");
+ String[] searchTagIdsArray = searchTagIds.split(";");
+
+ // specifically apply lower case to the the query terms to make matching
+ // simpler
+ String[] queryTerms = queryStr.toLowerCase().split(" ");
+
+ OxmEntityDescriptor desc = oxmModelLoader.getSearchableEntityDescriptors().get(entityType);
+
+ if (desc == null) {
+ LOG.error(AaiUiMsgs.ENTITY_NOT_FOUND_IN_OXM, entityType.toString());
+ return searchTags;
+ }
+
+ String primaryKeyName = NodeUtils.concatArray(desc.getPrimaryKeyAttributeName(), "/");
+ String primaryKeyValue = null;
+
+ /*
+ * For each used attribute, get the fieldName for the attribute index and transform the search
+ * tag into t1,t2,t3 => h1=t1, h2=t2, h3=t3;
+ */
+ StringBuilder searchTagsBuilder = new StringBuilder(128);
+ searchTagsBuilder.append(entityType);
+
+ String primaryKeyConjunctionValue = null;
+ boolean queryTermsMatchedSearchTags = false;
+
+ if (searchTagsArray.length == searchTagIdsArray.length) {
+ for (int i = 0; i < searchTagsArray.length; i++) {
+ String searchTagAttributeId = searchTagIdsArray[i];
+ String searchTagAttributeValue = searchTagsArray[i];
+
+ // Find the concat conjunction
+ Map<String, String> pairConjunctionList = suggestionConfig.getPairingList();
+
+ String suggConjunction = null;
+ if (pairConjunctionList.get(searchTagAttributeId) != null) {
+ suggConjunction = pairConjunctionList.get(searchTagAttributeId);
+ } else {
+ suggConjunction = suggestionConfig.getDefaultPairingValue();
+ }
+
+ if (primaryKeyName.equals(searchTagAttributeId)) {
+ primaryKeyValue = searchTagAttributeValue;
+ primaryKeyConjunctionValue = suggConjunction;
+ }
+
+ if (queryTermsMatchSearchTag(queryTerms, searchTagAttributeValue)) {
+ searchTagsBuilder.append(" " + suggConjunction + " " + searchTagAttributeValue);
+ queryTermsMatchedSearchTags = true;
+ }
+ }
+ } else {
+ String errorMessage = "Search tags length did not match search tag ID length for entity type " + entityType;
+ LOG.error(AaiUiMsgs.ENTITY_SYNC_SEARCH_TAG_ANNOTATION_FAILED, errorMessage);
+ }
+
+ /*
+ * if none of the user query terms matched the index entity search tags then we should still tag
+ * the matched entity with a conjunction set to at least it's entity primary key value to
+ * discriminate between the entities of the same type in the search results displayed in the UI
+ * search bar results
+ */
+
+ if (!queryTermsMatchedSearchTags) {
+
+ if (primaryKeyValue != null && primaryKeyConjunctionValue != null) {
+ searchTagsBuilder.append(" " + primaryKeyConjunctionValue + " " + primaryKeyValue);
+ } else {
+ LOG.error(AaiUiMsgs.SEARCH_TAG_ANNOTATION_ERROR, "See error",
+ "Could not annotate user query terms " + queryStr
+ + " from available entity search tags = " + searchTags);
+ return searchTags;
+ }
+
+ }
+
+ return searchTagsBuilder.toString();
+
+ }
+
+
+ /**
+ * @param queryStr - space separate query search terms
+ * @return - query string with stop-words removed
+ */
+ private String stripStopWordsFromQuery(String queryStr) {
+
+ if (queryStr == null) {
+ return queryStr;
+ }
+
+ Collection<String> stopWords = suggestionConfig.getStopWords();
+ ArrayList<String> queryTerms = new ArrayList<String>(Arrays.asList(queryStr.toLowerCase().split(" ")));
+
+ queryTerms.removeAll(stopWords);
+
+ return String.join(" ", queryTerms);
+ }
+
+ /*
+ * Expected query:
+ *
+ * POST /search/viuiSearch/
+ *
+ * { "maxResults" : "10", "searchStr" : "<search bar text>" }
+ */
+
+ /**
+ * Handle view and inspect search.
+ *
+ * @param request the request
+ * @param maxResults Max number of results to return
+ * @param response the response
+ * @return
+ * @throws IOException Signals that an I/O exception has occurred.
+ */
+ protected List<SuggestionEntity> performViewAndInspectQuerySearch(
+ QuerySearchEntity querySearchEntity, int maxResults) throws IOException {
+ List<SuggestionEntity> suggestionEntityList = new ArrayList<SuggestionEntity>();
+
+ /*
+ * Based on the configured stop words, we need to strip any matched stop-words ( case
+ * insensitively ) from the query string, before hitting elastic to prevent the words from being
+ * used against the elastic view-and-inspect index. Another alternative to this approach would
+ * be to define stop words on the elastic search index configuration for the
+ * entity-search-index, but but that may be more complicated / more risky than just a simple bug
+ * fix, but it's something we should think about for the future.
+ */
+
+ try {
+ final String queryStringWithoutStopWords =
+ stripStopWordsFromQuery(querySearchEntity.getQueryStr());
+
+ final String fullUrlStr = getSasFullUrl(sasConfig.getIndexName(), VALUE_QUERY,
+ sasConfig.getIpAddress(), sasConfig.getHttpPort(), sasConfig.getVersion());
+
+ String postBody =
+ String.format(VIUI_SEARCH_TEMPLATE, maxResults, queryStringWithoutStopWords);
+
+ OperationResult opResult = search.doPost(fullUrlStr, postBody, "application/json");
+ if (opResult.getResultCode() == 200) {
+ suggestionEntityList = generateSuggestionsForSearchResponse(opResult.getResult(),
+ querySearchEntity.getQueryStr());
+ }
+ } catch (Exception exc) {
+ LOG.error(AaiUiMsgs.SEARCH_SERVLET_ERROR,
+ "View and inspect query failed with error = " + exc.getMessage());
+ }
+ return suggestionEntityList;
+ }
+
+ protected List<SuggestionEntity> performVnfQuerySearch(QuerySearchEntity querySearchEntity,
+ int resultCountLimit) throws Exception {
+ return vnfSearch.getSuggestionsResults(querySearchEntity, resultCountLimit);
+ }
+
+ protected void handleQuerySearch(HttpServletRequest request, HttpServletResponse response)
+ throws IOException {
+ String payload = NodeUtils.getBody(request);
+ if (payload == null || payload.isEmpty()) {
+ handleSearchServletErrors("Unable to parse payload", null, response);
+ } else {
+ QuerySearchEntity querySearchEntity = mapper.readValue(payload, QuerySearchEntity.class);
+ int maxResultsPerSearch = Integer.valueOf(querySearchEntity.getMaxResults());
+ try {
+ SearchResponse searchResponse = new SearchResponse();
+ List<SuggestionEntity> viewAndInspectsuggestionEntityList =
+ new ArrayList<SuggestionEntity>();
+ List<SuggestionEntity> vnfSuggestionEntityList = new ArrayList<SuggestionEntity>();
+ long processTime = System.currentTimeMillis();
+ for (String searchService : suggestionConfig.getSearchIndexToSearchService().values()) {
+ if (searchService.equals(SearchServiceWrapper.class.getSimpleName())) {
+ viewAndInspectsuggestionEntityList =
+ performViewAndInspectQuerySearch(querySearchEntity, maxResultsPerSearch);
+ } else if (searchService.equals(VnfSearchService.class.getSimpleName())) {
+ vnfSuggestionEntityList = performVnfQuerySearch(querySearchEntity, maxResultsPerSearch);
+ }
+ }
+
+ int totalAdded = 0;
+ for (int i = 0; i < maxResultsPerSearch; i++) {
+ if (i < viewAndInspectsuggestionEntityList.size() && totalAdded < maxResultsPerSearch) {
+ searchResponse.addSuggestion(viewAndInspectsuggestionEntityList.get(i));
+ totalAdded++;
+ }
+ if (i < vnfSuggestionEntityList.size() && totalAdded < maxResultsPerSearch) {
+ searchResponse.addSuggestion(vnfSuggestionEntityList.get(i));
+ totalAdded++;
+ }
+ if (totalAdded >= maxResultsPerSearch) {
+ break;
+ }
+ }
+ searchResponse.addToTotalFound(totalAdded);
+ String searchResponseJson = NodeUtils.convertObjectToJson(searchResponse, true);
+
+ processTime = System.currentTimeMillis() - processTime;
+ searchResponse.setProcessingTimeInMs(processTime);
+ setServletResponse(response, searchResponseJson);
+ } catch (Exception exc) {
+ LOG.error(AaiUiMsgs.SEARCH_SERVLET_ERROR,
+ "Query search failed with error = " + exc.getMessage());
+ }
+ }
+ }
+
+ public void doPost(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+
+ String api = null;
+ try {
+
+ // set default response
+ response.setStatus(200);
+
+ if (request.getRequestURI().contains(QUERY_SEARCH)) {
+ api = QUERY_SEARCH;
+ handleQuerySearch(request, response);
+ return;
+ } else if (request.getRequestURI().contains(SUMMARY_BY_ENTITY_TYPE_COUNT_API)) {
+ api = SUMMARY_BY_ENTITY_TYPE_COUNT_API;
+ handleSummaryByEntityTypeCount(request, response);
+ return;
+ } else if (request.getRequestURI().contains(SUMMARY_BY_ENTITY_TYPE_API)) {
+ api = SUMMARY_BY_ENTITY_TYPE_API;
+ handleSummaryByEntityType(request, response);
+ return;
+ } else {
+
+ final String errorMessage = "Ignored request-uri = " + request.getRequestURI();
+ LOG.error(AaiUiMsgs.SEARCH_SERVLET_ERROR, errorMessage);
+ response.setStatus(404);
+ response.setContentType("application/json");
+ PrintWriter out = response.getWriter();
+ out.println(generateJsonErrorResponse(errorMessage));
+ out.close();
+
+
+ }
+ } catch (JSONException je){
+ handleSearchServletErrors("Caught an exception while parsing json in processing for " + api, je,
+ response);
+ } catch (Exception e1) {
+ handleSearchServletErrors("Caught an exception while communicating with elasticsearch", e1,
+ response);
+ }
+ }
+
+ /**
+ * Generate json error response.
+ *
+ * @param message the message
+ * @return the string
+ */
+ /*
+ * This is the manual approach, however we could also create an object container for the error
+ * then use the Jackson ObjectWrite to dump the object to json instead. If it gets any more
+ * complicated we could do that approach so we don't have to manually trip over the JSON
+ * formatting.
+ */
+ protected String generateJsonErrorResponse(String message) {
+ return String.format("{ \"errorMessage\" : %s }", message);
+ }
+
+ /**
+ * Handle search servlet errors.
+ *
+ * @param errorMsg the error msg
+ * @param exc the exc
+ * @param response the response
+ * @throws IOException Signals that an I/O exception has occurred.
+ */
+ public void handleSearchServletErrors(String errorMsg, Exception exc,
+ HttpServletResponse response) throws IOException {
+
+ String errorLogMsg =
+ (exc == null ? errorMsg : errorMsg + ". Error:" + exc.getLocalizedMessage());
+
+ LOG.error(AaiUiMsgs.SEARCH_SERVLET_ERROR, errorLogMsg);
+
+ response.setContentType("application/json");
+ PrintWriter out = response.getWriter();
+ out.println(generateJsonErrorResponse(errorMsg));
+ out.close();
+ }
+
+
+ /**
+ * Execute query.
+ *
+ * @param response the response
+ * @param requestUrl the request url
+ * @param requestJsonPayload the request json payload
+ * @throws Exception the exception
+ */
+ public void executeQuery(HttpServletResponse response, String requestUrl,
+ String requestJsonPayload) throws Exception {
+
+ OperationResult opResult = search.doPost(requestUrl, requestJsonPayload, "application/json");
+
+ if (opResult != null) {
+
+ response.setStatus(opResult.getResultCode());
+ String finalOutput = opResult.getResult();
+
+ // example: failed to populate drop-down items from formatOutputJson()
+ if (finalOutput != null) {
+ response.setContentType("application/json");
+ PrintWriter out = response.getWriter();
+ out.println(finalOutput);
+ out.close();
+ }
+
+ } else {
+ response.setStatus(500);
+ }
+
+ }
+
+ /**
+ * Sets the servlet response.
+ *
+ * @param response the response
+ * @param postPayload the post payload
+ *
+ * @throws IOException Signals that an I/O exception has occurred.
+ */
+ private void setServletResponse(HttpServletResponse response, String postPayload)
+ throws IOException {
+
+ if (postPayload != null) {
+ response.setContentType("application/json");
+ PrintWriter out = response.getWriter();
+ out.println(postPayload);
+ out.close();
+ }
+ }
+
+
+
+} \ No newline at end of file
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/services/VisualizationContext.java b/src/main/java/org/openecomp/sparky/viewandinspect/services/VisualizationContext.java
new file mode 100644
index 0000000..c5adfd4
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/services/VisualizationContext.java
@@ -0,0 +1,1649 @@
+/**
+ * ============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.openecomp.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.openecomp.cl.api.Logger;
+import org.openecomp.cl.eelf.LoggerFactory;
+import org.openecomp.sparky.config.oxm.OxmEntityDescriptor;
+import org.openecomp.sparky.config.oxm.OxmModelLoader;
+import org.openecomp.sparky.dal.aai.ActiveInventoryDataProvider;
+import org.openecomp.sparky.dal.aai.config.ActiveInventoryConfig;
+import org.openecomp.sparky.dal.rest.OperationResult;
+import org.openecomp.sparky.logging.AaiUiMsgs;
+import org.openecomp.sparky.synchronizer.entity.SearchableEntity;
+import org.openecomp.sparky.util.NodeUtils;
+import org.openecomp.sparky.viewandinspect.config.TierSupportUiConstants;
+import org.openecomp.sparky.viewandinspect.config.VisualizationConfig;
+import org.openecomp.sparky.viewandinspect.entity.ActiveInventoryNode;
+import org.openecomp.sparky.viewandinspect.entity.InlineMessage;
+import org.openecomp.sparky.viewandinspect.entity.NodeProcessingTransaction;
+import org.openecomp.sparky.viewandinspect.entity.QueryParams;
+import org.openecomp.sparky.viewandinspect.entity.Relationship;
+import org.openecomp.sparky.viewandinspect.entity.RelationshipData;
+import org.openecomp.sparky.viewandinspect.entity.RelationshipList;
+import org.openecomp.sparky.viewandinspect.entity.SelfLinkDeterminationTransaction;
+import org.openecomp.sparky.viewandinspect.enumeration.NodeProcessingAction;
+import org.openecomp.sparky.viewandinspect.enumeration.NodeProcessingState;
+import org.openecomp.sparky.viewandinspect.task.PerformNodeSelfLinkProcessingTask;
+import org.openecomp.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 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(
+ VisualizationContext.class);
+ private final ActiveInventoryDataProvider aaiProvider;
+
+ private int maxSelfLinkTraversalDepth;
+ private AtomicInteger numLinksDiscovered;
+ private AtomicInteger numSuccessfulLinkResolveFromCache;
+ private AtomicInteger numSuccessfulLinkResolveFromFromServer;
+ private AtomicInteger numFailedLinkResolve;
+ private AtomicInteger aaiWorkOnHand;
+
+ private ActiveInventoryConfig aaiConfig;
+ private VisualizationConfig visualizationConfig;
+ private List<String> shallowEntities;
+
+ private AtomicInteger totalLinksRetrieved;
+
+ private final long contextId;
+ private final String contextIdStr;
+
+ private OxmModelLoader loader;
+ private ObjectMapper mapper;
+ private InlineMessage inlineMessage = null;
+
+ private ExecutorService aaiExecutorService;
+
+ /*
+ * 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<String, ActiveInventoryNode> nodeCache;
+
+ /**
+ * Instantiates a new self link node collector.
+ *
+ * @param loader the loader
+ * @throws Exception the exception
+ */
+ public VisualizationContext(long contextId, ActiveInventoryDataProvider aaiDataProvider,
+ ExecutorService aaiExecutorService, OxmModelLoader loader) throws Exception {
+
+ this.contextId = contextId;
+ this.contextIdStr = "[Context-Id=" + contextId + "]";
+ this.aaiProvider = aaiDataProvider;
+ this.aaiExecutorService = aaiExecutorService;
+ this.loader = loader;
+
+ this.nodeCache = new ConcurrentHashMap<String, ActiveInventoryNode>();
+ 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.aaiConfig = ActiveInventoryConfig.getConfig();
+ this.visualizationConfig = VisualizationConfig.getConfig();
+ this.shallowEntities = aaiConfig.getAaiRestConfig().getShallowEntities();
+
+ this.maxSelfLinkTraversalDepth = visualizationConfig.getMaxSelfLinkTraversalDepth();
+
+ this.mapper = new ObjectMapper();
+ mapper.setSerializationInclusion(Include.NON_EMPTY);
+ mapper.setPropertyNamingStrategy(new PropertyNamingStrategy.KebabCaseStrategy());
+ }
+
+ 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<String> extractQueryParamsFromSelfLink(String link) {
+
+ List<String> queryParams = new ArrayList<String>();
+
+ if (link == null) {
+ LOG.error(AaiUiMsgs.QUERY_PARAM_EXTRACTION_ERROR, "self link is null");
+ return queryParams;
+ }
+
+ Map<String, OxmEntityDescriptor> entityDescriptors = loader.getEntityDescriptors();
+
+ try {
+
+ URIBuilder urlBuilder = new URIBuilder(link);
+ String urlPath = urlBuilder.getPath();
+
+ OxmEntityDescriptor descriptor = null;
+ String[] urlPathElements = urlPath.split("/");
+ List<String> 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.getPrimaryKeyAttributeName();
+
+ /*
+ * 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<Entry<String, JsonNode>> entityArrays = attributeGroup.fields();
+ Entry<String, JsonNode> 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<JsonNode> 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:
+ *
+ * <li>In the ActiveInventoryNode, on construction maintain a collection of queryParams
+ * that is added to for the purpose of discovering parent->child hierarchies.
+ *
+ * <li>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.
+ *
+ * <li>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();
+ 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 =
+ aaiProvider.getGenericQueryForSelfLink(entityType, newNode.getQueryParams());
+
+ /**
+ * <li>get the self-link
+ * <li>add it to the new node
+ * <li>generate node id
+ * <li>add node to node cache
+ * <li>add node id to parent outbound links list
+ * <li>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, aaiProvider),
+ aaiExecutorService).whenComplete((nodeTxn, error) -> {
+ aaiWorkOnHand.decrementAndGet();
+ 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.isResolvedLinkFailure()) {
+ numFailedLinkResolve.incrementAndGet();
+ }
+
+ if (opResult.isResolvedLinkFromCache()) {
+ numSuccessfulLinkResolveFromCache.incrementAndGet();
+ }
+
+ if (opResult.isResolvedLinkFromServer()) {
+ numSuccessfulLinkResolveFromFromServer.incrementAndGet();
+ }
+
+ /*
+ * extract the self-link from the operational result.
+ */
+
+ Collection<JsonNode> entityLinks = new ArrayList<JsonNode>();
+ 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();
+
+ newChildNode.setSelfLink(selfLink);
+ newChildNode.setNodeId(NodeUtils.generateUniqueShaDigest(selfLink));
+
+ String uri = NodeUtils.calculateEditAttributeUri(selfLink);
+ if (uri != null) {
+ newChildNode.addProperty(TierSupportUiConstants.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);
+
+ }
+
+ }
+
+ });
+
+ }
+
+ 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<Entry<String, JsonNode>> fieldNames = jsonNode.fields();
+ Entry<String, JsonNode> 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 && nodeValue.isValueNode()) {
+
+ if (loader.getEntityDescriptor(fieldName) == null) {
+
+ /*
+ * entity property name is not an entity, thus we can add this property name and value
+ * to our property set
+ */
+
+ ain.addProperty(fieldName, nodeValue.asText());
+
+ }
+
+ } else {
+
+ if (nodeValue.isArray()) {
+
+ if (loader.getEntityDescriptor(fieldName) == null) {
+
+ /*
+ * entity property name is not an entity, thus we can add this property name and value
+ * to our property set
+ */
+
+ ain.addProperty(field.getKey(), nodeValue.toString());
+
+ }
+
+ } else {
+
+ ain.addComplexGroup(nodeValue);
+
+ }
+
+ }
+ }
+
+ }
+
+ String uri = NodeUtils.calculateEditAttributeUri(ain.getSelfLink());
+ if (uri != null) {
+ ain.addProperty(TierSupportUiConstants.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);
+
+ }
+
+ /**
+ * 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 (shallowEntities.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, aaiProvider),
+ aaiExecutorService).whenComplete((nodeTxn, error) -> {
+ aaiWorkOnHand.decrementAndGet();
+ 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.isResolvedLinkFailure()) {
+ numFailedLinkResolve.incrementAndGet();
+ }
+
+ if (opResult.isResolvedLinkFromCache()) {
+ numSuccessfulLinkResolveFromCache.incrementAndGet();
+ }
+
+ if (opResult.isResolvedLinkFromServer()) {
+ 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);
+
+ }
+ }
+
+ });
+
+ }
+
+ }
+
+
+ /**
+ * 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 boolean findAndMarkRootNode(QueryParams queryParams) {
+
+ 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());
+ return true;
+ }
+ }
+
+ return false;
+
+ }
+
+ /**
+ * Process current node states.
+ *
+ * @param rootNodeDiscovered the root node discovered
+ */
+ private void processCurrentNodeStates(boolean rootNodeDiscovered) {
+ /*
+ * Force an evaluation of node depths before determining if we should limit state-based
+ * traversal or processing.
+ */
+ if (rootNodeDiscovered) {
+ evaluateNodeDepths();
+ }
+
+ 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 (!rootNodeDiscovered || cacheNode.getNodeDepth() < VisualizationConfig.getConfig()
+ .getMaxSelfLinkTraversalDepth()) {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(AaiUiMsgs.DEBUG_GENERIC,
+ "SLNC::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<Entry<String, JsonNode>> fields = attributeGroup.fields();
+ Entry<String, JsonNode> 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 (loader.getEntityDescriptor(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<String, ActiveInventoryNode> 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<String> 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<String> pkeyNames =
+ loader.getEntityDescriptor(ain.getEntityType()).getPrimaryKeyAttributeName();
+
+ 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;
+ }
+
+ OxmModelLoader modelLoader = OxmModelLoader.getInstance();
+
+ Relationship[] relationshipArray = relationshipList.getRelationshipList();
+ OxmEntityDescriptor descriptor = null;
+ String repairedSelfLink = null;
+
+ if (relationshipArray != null) {
+
+ ActiveInventoryNode newNode = null;
+
+ for (Relationship r : relationshipArray) {
+
+ repairedSelfLink = aaiConfig.repairSelfLink(r.getRelatedLink());
+
+ String nodeId = NodeUtils.generateUniqueShaDigest(repairedSelfLink);
+
+ if (nodeId == null) {
+
+ LOG.error(AaiUiMsgs.SKIPPING_RELATIONSHIP, r.toString());
+ processingNode.changeState(NodeProcessingState.ERROR,
+ NodeProcessingAction.NODE_IDENTITY_ERROR);
+ return false;
+ }
+
+ newNode = new ActiveInventoryNode();
+
+ String entityType = r.getRelatedTo();
+
+ if (r.getRelationshipData() != null) {
+ for (RelationshipData rd : r.getRelationshipData()) {
+ newNode.addQueryParam(rd.getRelationshipKey() + ":" + rd.getRelationshipValue());
+ }
+ }
+
+ descriptor = modelLoader.getEntityDescriptor(r.getRelatedTo());
+
+ newNode.setNodeId(nodeId);
+ newNode.setEntityType(entityType);
+ newNode.setSelfLink(repairedSelfLink);
+
+ processingNode.addOutboundNeighbor(nodeId);
+
+ if (descriptor != null) {
+
+ List<String> pkeyNames = descriptor.getPrimaryKeyAttributeName();
+
+ 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();
+
+ 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 (queryParams.getSearchTargetNodeId().equals(newNode.getNodeId())) {
+ newNode.setNodeDepth(0);
+ newNode.setRootNode(true);
+ LOG.info(AaiUiMsgs.ROOT_NODE_DISCOVERED, queryParams.getSearchTargetNodeId());
+ }
+
+ newNode.setSelfLink(searchTargetEntity.getLink());
+
+ nodeCache.putIfAbsent(newNode.getNodeId(), newNode);
+ }
+
+ /**
+ * Checks for out standing work.
+ *
+ * @return true, if successful
+ */
+ private boolean hasOutStandingWork() {
+
+ int numNodesWithPendingStates = 0;
+
+ /*
+ * Force an evaluation of node depths before determining if we should limit state-based
+ * traversal or processing.
+ */
+
+ 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() < VisualizationConfig.getConfig().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));
+
+ return (numNodesWithPendingStates > 0);
+
+ }
+
+ /**
+ * Process self links.
+ *
+ * @param skeletonNode the skeleton node
+ * @param queryParams the query params
+ */
+ 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;
+ }
+
+ processSearchableEntity(searchtargetEntity, queryParams);
+
+ long startTimeInMs = System.currentTimeMillis();
+
+ /*
+ * wait until all transactions are complete or guard-timer expires.
+ */
+
+ long totalResolveTime = 0;
+ boolean hasOutstandingWork = hasOutStandingWork();
+ boolean outstandingWorkGuardTimerFired = false;
+ long maxGuardTimeInMs = 5000;
+ long guardTimeInMs = 0;
+ boolean foundRootNode = false;
+
+
+ /*
+ * TODO: Put a count-down-latch in place of the while loop, but if we do that then
+ * we'll need to decouple the visualization processing from the main thread so it can continue to process while
+ * the main thread is waiting on for count-down-latch gate to open. This may also be easier once we move to the
+ * VisualizationService + VisualizationContext ideas.
+ */
+
+
+ while (hasOutstandingWork || !outstandingWorkGuardTimerFired) {
+
+ if (!foundRootNode) {
+ foundRootNode = findAndMarkRootNode(queryParams);
+ }
+
+ processCurrentNodeStates(foundRootNode);
+
+ verifyOutboundNeighbors();
+
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException exc) {
+ LOG.error(AaiUiMsgs.PROCESSING_LOOP_INTERUPTED, exc.getMessage());
+ return;
+ }
+
+ totalResolveTime = (System.currentTimeMillis() - startTimeInMs);
+
+ if (!hasOutstandingWork) {
+
+ guardTimeInMs += 500;
+
+ if (guardTimeInMs > maxGuardTimeInMs) {
+ outstandingWorkGuardTimerFired = true;
+ }
+ } else {
+ guardTimeInMs = 0;
+ }
+
+ hasOutstandingWork = hasOutStandingWork();
+
+ }
+
+ 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 (VisualizationConfig.getConfig().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 = loader.getEntityDescriptor(entityType);
+
+ if (descriptor == null) {
+ LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE, "oxm entity"
+ + " descriptor for entityType = " + entityType);
+ return null;
+ }
+
+ List<String> pkeyNames = descriptor.getPrimaryKeyAttributeName();
+
+ 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/src/main/java/org/openecomp/sparky/viewandinspect/services/VisualizationService.java b/src/main/java/org/openecomp/sparky/viewandinspect/services/VisualizationService.java
new file mode 100644
index 0000000..aed7cd2
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/services/VisualizationService.java
@@ -0,0 +1,387 @@
+/**
+ * ============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.openecomp.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.openecomp.cl.api.Logger;
+import org.openecomp.cl.eelf.LoggerFactory;
+import org.openecomp.sparky.config.oxm.OxmModelLoader;
+import org.openecomp.sparky.dal.aai.ActiveInventoryAdapter;
+import org.openecomp.sparky.dal.aai.ActiveInventoryDataProvider;
+import org.openecomp.sparky.dal.aai.config.ActiveInventoryConfig;
+import org.openecomp.sparky.dal.aai.config.ActiveInventoryRestConfig;
+import org.openecomp.sparky.dal.cache.EntityCache;
+import org.openecomp.sparky.dal.cache.PersistentEntityCache;
+import org.openecomp.sparky.dal.elasticsearch.ElasticSearchAdapter;
+import org.openecomp.sparky.dal.elasticsearch.ElasticSearchDataProvider;
+import org.openecomp.sparky.dal.elasticsearch.config.ElasticSearchConfig;
+import org.openecomp.sparky.dal.rest.OperationResult;
+import org.openecomp.sparky.dal.rest.RestClientBuilder;
+import org.openecomp.sparky.dal.rest.RestfulDataAccessor;
+import org.openecomp.sparky.logging.AaiUiMsgs;
+import org.openecomp.sparky.synchronizer.entity.SearchableEntity;
+import org.openecomp.sparky.util.NodeUtils;
+import org.openecomp.sparky.viewandinspect.config.VisualizationConfig;
+import org.openecomp.sparky.viewandinspect.entity.ActiveInventoryNode;
+import org.openecomp.sparky.viewandinspect.entity.D3VisualizationOutput;
+import org.openecomp.sparky.viewandinspect.entity.GraphMeta;
+import org.openecomp.sparky.viewandinspect.entity.QueryParams;
+import org.openecomp.sparky.viewandinspect.entity.QueryRequest;
+
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class VisualizationService {
+
+ private static final Logger LOG =
+ LoggerFactory.getInstance().getLogger(VisualizationService.class);
+
+ private OxmModelLoader loader;
+ private ObjectMapper mapper = new ObjectMapper();
+
+ private final ActiveInventoryDataProvider aaiProvider;
+ private final ActiveInventoryRestConfig aaiRestConfig;
+ private final ElasticSearchDataProvider esProvider;
+ private final ElasticSearchConfig esConfig;
+ private final ExecutorService aaiExecutorService;
+
+ private ConcurrentHashMap<Long, VisualizationContext> contextMap;
+ private final SecureRandom secureRandom;
+
+ private ActiveInventoryConfig aaiConfig;
+ private VisualizationConfig visualizationConfig;
+
+ public VisualizationService(OxmModelLoader loader) throws Exception {
+ this.loader = loader;
+
+ aaiRestConfig = ActiveInventoryConfig.getConfig().getAaiRestConfig();
+
+ EntityCache cache = null;
+ secureRandom = new SecureRandom();
+
+ ActiveInventoryAdapter aaiAdapter = new ActiveInventoryAdapter(new RestClientBuilder());
+
+ if (aaiRestConfig.isCacheEnabled()) {
+ cache = new PersistentEntityCache(aaiRestConfig.getStorageFolderOverride(),
+ aaiRestConfig.getNumCacheWorkers());
+
+ aaiAdapter.setCacheEnabled(true);
+ aaiAdapter.setEntityCache(cache);
+ }
+
+ this.aaiProvider = aaiAdapter;
+
+ RestClientBuilder esClientBuilder = new RestClientBuilder();
+ esClientBuilder.setUseHttps(false);
+ RestfulDataAccessor nonCachingRestProvider = new RestfulDataAccessor(esClientBuilder);
+ this.esConfig = ElasticSearchConfig.getConfig();
+ this.esProvider = new ElasticSearchAdapter(nonCachingRestProvider, this.esConfig);
+
+ this.mapper = new ObjectMapper();
+ mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+
+ this.contextMap = new ConcurrentHashMap<Long, VisualizationContext>();
+ this.visualizationConfig = VisualizationConfig.getConfig();
+ this.aaiConfig = ActiveInventoryConfig.getConfig();
+ this.aaiExecutorService = NodeUtils.createNamedExecutor("SLNC-WORKER",
+ aaiConfig.getAaiRestConfig().getNumResolverWorkers(), LOG);
+ }
+
+ public OxmModelLoader getLoader() {
+ return loader;
+ }
+
+ public void setLoader(OxmModelLoader loader) {
+ this.loader = loader;
+ }
+
+ /**
+ * Analyze query request body.
+ *
+ * @param queryRequestJson the query request json
+ * @return the query request
+ */
+
+ public QueryRequest analyzeQueryRequestBody(String queryRequestJson) {
+
+
+ LOG.debug(AaiUiMsgs.DEBUG_GENERIC,
+ "analyzeQueryRequestBody()," + " queryRequestJson = " + queryRequestJson);
+
+ ObjectMapper nonEmptyMapper = new ObjectMapper();
+ nonEmptyMapper.setSerializationInclusion(Include.NON_EMPTY);
+
+ QueryRequest queryBody = null;
+
+ try {
+ queryBody = nonEmptyMapper.readValue(queryRequestJson, QueryRequest.class);
+ } catch (Exception exc) {
+ LOG.error(AaiUiMsgs.EXCEPTION_CAUGHT, "Analyzing query request body.",
+ exc.getLocalizedMessage());
+ }
+
+ return queryBody;
+
+ }
+
+ /**
+ * Log optime.
+ *
+ * @param method the method
+ * @param opStartTimeInMs the op start time in ms
+ */
+ private void logOptime(String method, long opStartTimeInMs) {
+ LOG.info(AaiUiMsgs.OPERATION_TIME, method,
+ String.valueOf(System.currentTimeMillis() - opStartTimeInMs));
+ }
+
+ private SearchableEntity extractSearchableEntityFromElasticEntity(OperationResult operationResult) {
+ if (operationResult == null || !operationResult.wasSuccessful()) {
+ // error, return empty collection
+ return null;
+ }
+
+ SearchableEntity sourceEntity = null;
+ if (operationResult.wasSuccessful()) {
+
+ try {
+ JsonNode elasticValue = mapper.readValue(operationResult.getResult(), JsonNode.class);
+
+ if (elasticValue != null) {
+ JsonNode sourceField = elasticValue.get("_source");
+
+ if (sourceField != null) {
+ sourceEntity = new SearchableEntity();
+
+ String entityType = NodeUtils.extractFieldValueFromObject(sourceField, "entityType");
+ sourceEntity.setEntityType(entityType);
+ String entityPrimaryKeyValue = NodeUtils.extractFieldValueFromObject(sourceField, "entityPrimaryKeyValue");
+ sourceEntity.setEntityPrimaryKeyValue(entityPrimaryKeyValue);
+ String link = NodeUtils.extractFieldValueFromObject(sourceField, "link");
+ sourceEntity.setLink(link);
+ String lastmodTimestamp = NodeUtils.extractFieldValueFromObject(sourceField, "lastmodTimestamp");
+ sourceEntity.setEntityTimeStamp(lastmodTimestamp);
+ }
+ }
+ } catch (IOException ioe) {
+ LOG.error(AaiUiMsgs.JSON_CONVERSION_ERROR, "a json node ", ioe.getLocalizedMessage());
+ }
+ }
+ return sourceEntity;
+ }
+
+ /**
+ * Builds the visualization using generic query.
+ *
+ * @param queryRequest the query request
+ * @return the operation result
+ */
+ public OperationResult buildVisualizationUsingGenericQuery(QueryRequest queryRequest) {
+
+ OperationResult returnValue = new OperationResult();
+ OperationResult dataCollectionResult = null;
+ QueryParams queryParams = null;
+ SearchableEntity sourceEntity = null;
+
+ try {
+
+ /*
+ * Here is where we need to make a dip to elastic-search for the self-link by entity-id (link
+ * hash).
+ */
+ dataCollectionResult = esProvider.retrieveEntityById(queryRequest.getHashId());
+ sourceEntity = extractSearchableEntityFromElasticEntity(dataCollectionResult);
+
+ if (sourceEntity != null) {
+ sourceEntity.generateId();
+ }
+
+ queryParams = new QueryParams();
+ queryParams.setSearchTargetNodeId(queryRequest.getHashId());
+
+ } catch (Exception e1) {
+ LOG.error(AaiUiMsgs.FAILED_TO_GET_NODES_QUERY_RESULT, e1.getLocalizedMessage());
+ dataCollectionResult = new OperationResult(500, "Failed to get nodes-query result from AAI");
+ }
+
+ if (dataCollectionResult.getResultCode() == 200) {
+
+ String d3OutputJsonOutput = null;
+
+ try {
+
+ d3OutputJsonOutput = getVisualizationOutputBasedonGenericQuery(sourceEntity, queryParams);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(AaiUiMsgs.DEBUG_GENERIC,
+ "Generated D3" + " output as json = " + d3OutputJsonOutput);
+ }
+
+ if (d3OutputJsonOutput != null) {
+ returnValue.setResultCode(200);
+ returnValue.setResult(d3OutputJsonOutput);
+ } else {
+ returnValue.setResult(500, "Failed to generate D3 graph visualization");
+ }
+
+ } catch (Exception exc) {
+ returnValue.setResult(500,
+ "Failed to generate D3 graph visualization, due to a servlet exception.");
+ LOG.error(AaiUiMsgs.ERROR_D3_GRAPH_VISUALIZATION, exc.getLocalizedMessage());
+ }
+ } else {
+ returnValue.setResult(dataCollectionResult.getResultCode(), dataCollectionResult.getResult());
+ }
+
+ return returnValue;
+
+ }
+
+ /**
+ * Gets the visualization output basedon generic query.
+ *
+ * @param searchtargetEntity entity that will be used to start visualization flow
+ * @param queryParams the query params
+ * @return the visualization output basedon generic query
+ * @throws ServletException the servlet exception
+ */
+ private String getVisualizationOutputBasedonGenericQuery(SearchableEntity searchtargetEntity,
+ QueryParams queryParams) throws ServletException {
+
+ long opStartTimeInMs = System.currentTimeMillis();
+
+ VisualizationTransformer transformer = null;
+ try {
+ transformer = new VisualizationTransformer();
+ } catch (Exception exc) {
+ throw new ServletException(
+ "Failed to create VisualizationTransformer instance because of execption", exc);
+ }
+
+ VisualizationContext visContext = null;
+ long contextId = secureRandom.nextLong();
+ try {
+ visContext = new VisualizationContext(contextId, aaiProvider, aaiExecutorService, loader);
+ contextMap.putIfAbsent(contextId, visContext);
+ } catch (Exception e1) {
+ LOG.error(AaiUiMsgs.EXCEPTION_CAUGHT,
+ "While building Visualization Context, " + e1.getLocalizedMessage());
+ throw new ServletException(e1);
+ }
+
+ String jsonResponse = null;
+
+ long startTimeInMs = System.currentTimeMillis();
+
+ visContext.processSelfLinks(searchtargetEntity, queryParams);
+ contextMap.remove(contextId);
+
+ logOptime("collectSelfLinkNodes()", startTimeInMs);
+
+ /*
+ * Flatten the graphs into a set of Graph and Link nodes. In this method I want the node graph
+ * resulting from the edge-tag-query to be represented first, and then we'll layer in
+ * relationship data.
+ */
+ long overlayDataStartTimeInMs = System.currentTimeMillis();
+
+ Map<String, ActiveInventoryNode> cachedNodeMap = visContext.getNodeCache();
+
+ if (LOG.isDebugEnabled()) {
+
+ StringBuilder sb = new StringBuilder(128);
+
+ sb.append("\nCached Node Map:\n");
+ for (String k : cachedNodeMap.keySet()) {
+ sb.append("\n----");
+ sb.append("\n").append(cachedNodeMap.get(k).dumpNodeTree(true));
+ }
+
+ LOG.debug(AaiUiMsgs.DEBUG_GENERIC, sb.toString());
+ }
+
+ transformer.buildFlatNodeArrayFromGraphCollection(cachedNodeMap);
+ transformer.buildLinksFromGraphCollection(cachedNodeMap);
+
+ /*
+ * - Apply configuration-driven styling
+ * - Build the final transformation response object
+ * - Use information we have to populate the GraphMeta object
+ */
+
+ transformer.addSearchTargetAttributesToRootNode();
+
+ GraphMeta graphMeta = new GraphMeta();
+
+ D3VisualizationOutput output = null;
+ try {
+ output = transformer
+ .generateVisualizationOutput((System.currentTimeMillis() - opStartTimeInMs), graphMeta);
+ } catch (JsonProcessingException exc) {
+ throw new ServletException("Caught an exception while generation visualization output", exc);
+ } catch (IOException exc) {
+ LOG.error(AaiUiMsgs.FAILURE_TO_PROCESS_REQUEST, exc.getLocalizedMessage());
+ }
+
+ output.setInlineMessage(visContext.getInlineMessage());
+ output.getGraphMeta().setNumLinkResolveFailed(visContext.getNumFailedLinkResolve());
+ output.getGraphMeta().setNumLinksResolvedSuccessfullyFromCache(
+ visContext.getNumSuccessfulLinkResolveFromCache());
+ output.getGraphMeta().setNumLinksResolvedSuccessfullyFromServer(
+ visContext.getNumSuccessfulLinkResolveFromFromServer());
+
+ try {
+ jsonResponse = transformer.convertVisualizationOutputToJson(output);
+ } catch (JsonProcessingException jpe) {
+ throw new ServletException(
+ "Caught an exception while converting visualization output to json", jpe);
+ }
+
+ logOptime("[build flat node array, add relationship data, search target,"
+ + " color scheme, and generate visualization output]", overlayDataStartTimeInMs);
+
+ logOptime("doFilter()", opStartTimeInMs);
+
+ return jsonResponse;
+
+ }
+
+ public void shutdown() {
+ aaiProvider.shutdown();
+ aaiExecutorService.shutdown();
+ esProvider.shutdown();
+ }
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/services/VisualizationTransformer.java b/src/main/java/org/openecomp/sparky/viewandinspect/services/VisualizationTransformer.java
new file mode 100644
index 0000000..a6803a6
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/services/VisualizationTransformer.java
@@ -0,0 +1,320 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.services;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import org.openecomp.cl.api.Logger;
+import org.openecomp.cl.eelf.LoggerFactory;
+import org.openecomp.sparky.dal.aai.config.ActiveInventoryConfig;
+import org.openecomp.sparky.logging.AaiUiMsgs;
+import org.openecomp.sparky.util.ConfigHelper;
+import org.openecomp.sparky.viewandinspect.config.VisualizationConfig;
+import org.openecomp.sparky.viewandinspect.entity.ActiveInventoryNode;
+import org.openecomp.sparky.viewandinspect.entity.D3VisualizationOutput;
+import org.openecomp.sparky.viewandinspect.entity.GraphMeta;
+import org.openecomp.sparky.viewandinspect.entity.JsonNode;
+import org.openecomp.sparky.viewandinspect.entity.JsonNodeLink;
+import org.openecomp.sparky.viewandinspect.entity.NodeDebug;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectWriter;
+
+/**
+ * The idea here is to receive a collection of graphs and then fold them together (or not) based on
+ * configuration. The first goal will be to fold all like-resources together, but the choice of
+ * folding could/should be configurable, and will simply change the degree of link based nodes when
+ * we generate the Node-Array and Link-Array output.
+ *
+ * @author DAVEA
+ *
+ */
+
+public class VisualizationTransformer {
+
+ private static final Logger LOG = LoggerFactory.getInstance().getLogger(
+ VisualizationTransformer.class);
+
+ List<JsonNode> flatNodeArray = new ArrayList<JsonNode>();
+ Set<String> enrichableUriPrefixes = null;
+
+ /*
+ * Maybe this isn't a string but Json-Model objects that we will convert to final string
+ * representation when we dump the node-array and link-array collections the post-data blob in the
+ * HttpServletResponse.
+ */
+
+ List<JsonNodeLink> linkArrayOutput = new ArrayList<JsonNodeLink>();
+
+
+
+ private VisualizationConfig visualizationConfig;
+
+
+ /**
+ * Instantiates a new visualization transformer.
+ *
+ * @throws Exception the exception
+ */
+ public VisualizationTransformer() throws Exception {
+ visualizationConfig = VisualizationConfig.getConfig();
+
+ }
+
+
+ /**
+ * Log optime.
+ *
+ * @param method the method
+ * @param startTimeInMs the start time in ms
+ */
+ private void logOptime(String method, long startTimeInMs) {
+ LOG.info(AaiUiMsgs.OPERATION_TIME, method,
+ String.valueOf((System.currentTimeMillis() - startTimeInMs)));
+ }
+
+ /**
+ * Adds the search target attributes to root node.
+ */
+ public void addSearchTargetAttributesToRootNode() {
+
+ for (JsonNode n : flatNodeArray) {
+ if (n.isRootNode()) {
+ n.getNodeMeta().setSearchTarget(true);
+ n.getNodeMeta().setClassName(visualizationConfig.getSelectedSearchedNodeClassName());
+ }
+
+ }
+
+ }
+
+ /**
+ * Generate visualization output.
+ *
+ * @param preProcessingOpTimeInMs the pre processing op time in ms
+ * @param graphMeta the graph meta
+ * @return the d 3 visualization output
+ * @throws JsonProcessingException the json processing exception
+ * @throws IOException Signals that an I/O exception has occurred.
+ */
+
+ public D3VisualizationOutput generateVisualizationOutput(long preProcessingOpTimeInMs,
+ GraphMeta graphMeta) throws JsonProcessingException, IOException {
+
+ long opStartTimeInMs = System.currentTimeMillis();
+
+ /*
+ * iterate over the flat collection, and only add the graph nodes to the graph node collection
+ */
+
+ D3VisualizationOutput output = new D3VisualizationOutput();
+
+ output.setGraphMeta(graphMeta);
+
+ for (JsonNode n : flatNodeArray) {
+ if ( n.getItemType()!= null) {
+ output.pegCounter(n.getItemType());
+ }
+ }
+
+ output.addNodes(flatNodeArray);
+ output.addLinks(linkArrayOutput);
+
+ int numNodes = flatNodeArray.size();
+ int numLinks = linkArrayOutput.size();
+
+ LOG.info(AaiUiMsgs.VISUALIZATION_GRAPH_OUTPUT, String.valueOf(numNodes),
+ String.valueOf(numLinks));
+
+ if (numLinks < (numNodes - 1)) {
+ LOG.warn(AaiUiMsgs.DANGLING_NODE_WARNING, String.valueOf(numLinks),
+ String.valueOf(numNodes));
+ }
+
+ ObjectMapper mapper = new ObjectMapper();
+
+ final String fileContent = ConfigHelper.getFileContents(
+ System.getProperty("AJSC_HOME") + visualizationConfig.getAaiEntityNodeDescriptors());
+ com.fasterxml.jackson.databind.JsonNode aaiEntityNodeDefinitions = mapper.readTree(fileContent);
+ graphMeta.setAaiEntityNodeDescriptors(aaiEntityNodeDefinitions);
+
+ graphMeta.setNumLinks(linkArrayOutput.size());
+ graphMeta.setNumNodes(flatNodeArray.size());
+ graphMeta.setRenderTimeInMs(preProcessingOpTimeInMs);
+
+ output.setGraphMeta(graphMeta);
+
+ logOptime("generateVisualizationOutput()", opStartTimeInMs);
+
+ return output;
+ }
+
+ /**
+ * Convert visualization output to json.
+ *
+ * @param output the output
+ * @return the string
+ * @throws JsonProcessingException the json processing exception
+ */
+ public String convertVisualizationOutputToJson(D3VisualizationOutput output)
+ throws JsonProcessingException {
+
+ if (output == null) {
+ return null;
+ }
+
+ ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
+
+ return ow.writeValueAsString(output);
+
+ }
+
+ /**
+ * Builds the links from graph collection.
+ *
+ * @param nodeMap the node map
+ */
+ public void buildLinksFromGraphCollection(Map<String, ActiveInventoryNode> nodeMap) {
+
+ for (ActiveInventoryNode ain : nodeMap.values()) {
+
+ /*
+ * This one is a little bit different, when we iterate over the collection we only want to
+ * draw the links for node that are less than the max traversal depth. We want to only draw
+ * links at a depth of n-1 because we are basing the links on the outbound neighbors from the
+ * current node.
+ */
+
+ if (ain.getNodeDepth() < VisualizationConfig.getConfig().getMaxSelfLinkTraversalDepth()) {
+
+ Collection<String> outboundNeighbors = ain.getOutboundNeighbors();
+
+ for (String outboundNeighbor : outboundNeighbors) {
+
+ JsonNodeLink nodeLink = new JsonNodeLink();
+
+ nodeLink.setId(UUID.randomUUID().toString());
+ nodeLink.setSource(ain.getNodeId());
+ nodeLink.setTarget(outboundNeighbor);
+
+ linkArrayOutput.add(nodeLink);
+
+ }
+
+ Collection<String> inboundNeighbors = ain.getInboundNeighbors();
+
+ for (String inboundNeighbor : inboundNeighbors) {
+
+ JsonNodeLink nodeLink = new JsonNodeLink();
+
+ nodeLink.setId(UUID.randomUUID().toString());
+ nodeLink.setSource(ain.getNodeId());
+ nodeLink.setTarget(inboundNeighbor);
+
+ linkArrayOutput.add(nodeLink);
+
+ }
+
+
+ } else {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(AaiUiMsgs.DEBUG_GENERIC, "buildLinks(),"
+ + " Filtering node = " + ain.getNodeId() + " @ depth = "
+ + ain.getNodeDepth());
+ }
+
+ }
+ }
+
+ }
+
+ /**
+ * Builds the flat node array from graph collection.
+ *
+ * @param nodeMap the node map
+ */
+ /*
+ * Recursive function to walk multi-graph nodes and children to build a folded resource target
+ * graph.
+ */
+ public void buildFlatNodeArrayFromGraphCollection(Map<String, ActiveInventoryNode> nodeMap) {
+
+ for (ActiveInventoryNode n : nodeMap.values()) {
+
+ if (n.getNodeDepth() <= VisualizationConfig.getConfig().getMaxSelfLinkTraversalDepth()) {
+
+ JsonNode jsonNode = new JsonNode(n);
+
+ if (this.isUriEnrichable(n.getSelfLink())) {
+ jsonNode.getNodeMeta().setEnrichableNode(true);
+ }
+
+ jsonNode.getNodeMeta().setClassName(visualizationConfig.getGeneralNodeClassName());
+
+ if (VisualizationConfig.getConfig().isVisualizationDebugEnabled()) {
+
+ NodeDebug nodeDebug = jsonNode.getNodeMeta().getNodeDebug();
+
+ if (nodeDebug != null) {
+ nodeDebug.setProcessingError(n.isProcessingErrorOccurred());
+ nodeDebug.setProcessingErrorCauses(n.getProcessingErrorCauses());
+ }
+ }
+ flatNodeArray.add(jsonNode);
+ } else {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(AaiUiMsgs.DEBUG_GENERIC,
+ "Filtering node from visualization: " + n.getNodeId() + " @ depth = "
+ + n.getNodeDepth());
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks if is uri enrichable.
+ *
+ * @param uri the uri
+ * @return true, if is uri enrichable
+ */
+ private boolean isUriEnrichable(String uri) {
+ if (enrichableUriPrefixes != null) {
+ for (String prefix : enrichableUriPrefixes) {
+ if (uri.contains(prefix)) { // AAI-4089
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/servlet/SearchServlet.java b/src/main/java/org/openecomp/sparky/viewandinspect/servlet/SearchServlet.java
new file mode 100644
index 0000000..c011e9c
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/servlet/SearchServlet.java
@@ -0,0 +1,194 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.servlet;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.openecomp.cl.api.Logger;
+import org.openecomp.cl.eelf.LoggerFactory;
+import org.openecomp.sparky.config.oxm.OxmModelLoader;
+import org.openecomp.sparky.dal.elasticsearch.SearchAdapter;
+import org.openecomp.sparky.dal.rest.OperationResult;
+import org.openecomp.sparky.dal.sas.config.SearchServiceConfig;
+import org.openecomp.sparky.logging.AaiUiMsgs;
+import org.openecomp.sparky.search.VnfSearchService;
+import org.openecomp.sparky.search.config.SuggestionConfig;
+import org.openecomp.sparky.util.NodeUtils;
+import org.openecomp.sparky.viewandinspect.services.SearchServiceWrapper;
+
+import org.openecomp.cl.mdc.MdcContext;
+
+/**
+ * The Class SearchServlet.
+ */
+
+public class SearchServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final Logger LOG = LoggerFactory.getInstance().getLogger(SearchServlet.class);
+
+ private SearchServiceWrapper searchWrapper = null;
+
+ private static final String KEY_PAYLOAD = "payload";
+
+ /**
+ * Instantiates a new search servlet.
+ */
+ public SearchServlet() {
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest,
+ * javax.servlet.http.HttpServletResponse)
+ */
+ @Override
+ public void doGet(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ doPost(request, response);
+ }
+
+ public void destroy() {
+ // TODO Auto-generated method stub
+ super.destroy();
+ }
+
+ public void init() throws ServletException {
+ super.init();
+ searchWrapper = new SearchServiceWrapper();
+ }
+
+ protected Map<String, String> getPayloadParams(JSONObject parameters) {
+ Map<String, String> payloadParams = new HashMap<String, String>();
+ try {
+ JSONObject payload = parameters.getJSONObject(KEY_PAYLOAD);
+ if (payload.length() > 0) {
+ for (String key : JSONObject.getNames(payload)) {
+ payloadParams.put(key, payload.getString(key));
+ }
+ }
+ } catch (JSONException exc) {
+ LOG.error(AaiUiMsgs.ERROR_PARSING_PARAMS, exc);
+ }
+ return payloadParams;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest,
+ * javax.servlet.http.HttpServletResponse)
+ */
+ @Override
+ public void doPost(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ String txnID = request.getHeader("X-TransactionId");
+ if (txnID == null) {
+ txnID = NodeUtils.getRandomTxnId();
+ }
+
+ String partnerName = request.getHeader("X-FromAppId");
+ if (partnerName == null) {
+ partnerName = "Browser";
+ }
+ MdcContext.initialize(txnID, "AAI_UI", "", partnerName, request.getRemoteAddr());
+ searchWrapper.doPost(request, response);
+ }
+
+ /**
+ * Generate json error response.
+ *
+ * @param message the message
+ * @return the string
+ */
+ /*
+ * This is the manual approach, however we could also create an object container for the error
+ * then use the Jackson ObjectWrite to dump the object to json instead. If it gets any more
+ * complicated we could do that approach so we don't have to manually trip over the JSON
+ * formatting.
+ */
+ protected String generateJsonErrorResponse(String message) {
+ return String.format("{ \"errorMessage\" : %s }", message);
+ }
+
+ /**
+ * Handle search servlet errors.
+ *
+ * @param errorMsg the error msg
+ * @param exc the exc
+ * @param response the response
+ * @throws IOException Signals that an I/O exception has occurred.
+ */
+ public void handleSearchServletErrors(String errorMsg, Exception exc,
+ HttpServletResponse response) throws IOException {
+
+ String errorLogMsg =
+ (exc == null ? errorMsg : errorMsg + ". Error:" + exc.getLocalizedMessage());
+
+ LOG.error(AaiUiMsgs.SEARCH_SERVLET_ERROR, errorLogMsg);
+
+ response.setContentType("application/json");
+ PrintWriter out = response.getWriter();
+ out.println(generateJsonErrorResponse(errorMsg));
+ out.close();
+ }
+
+
+ /**
+ * Sets the servlet response.
+ *
+ * @param response the response
+ * @param postPayload the post payload
+ *
+ * @throws IOException Signals that an I/O exception has occurred.
+ */
+ private void setServletResponse(HttpServletResponse response, String postPayload)
+ throws IOException {
+
+ if (postPayload != null) {
+ response.setContentType("application/json");
+ PrintWriter out = response.getWriter();
+ out.println(postPayload);
+ out.close();
+ }
+ }
+
+
+
+
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/servlet/VisualizationServlet.java b/src/main/java/org/openecomp/sparky/viewandinspect/servlet/VisualizationServlet.java
new file mode 100644
index 0000000..b64d0e8
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/servlet/VisualizationServlet.java
@@ -0,0 +1,203 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.servlet;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.io.IOUtils;
+import org.openecomp.cl.api.Logger;
+import org.openecomp.cl.eelf.LoggerFactory;
+import org.openecomp.sparky.config.oxm.OxmModelLoader;
+import org.openecomp.sparky.dal.rest.OperationResult;
+import org.openecomp.sparky.dal.servlet.ResettableStreamHttpServletRequest;
+import org.openecomp.sparky.logging.AaiUiMsgs;
+import org.openecomp.sparky.util.NodeUtils;
+import org.openecomp.sparky.viewandinspect.entity.QueryRequest;
+import org.openecomp.sparky.viewandinspect.services.VisualizationService;
+
+import org.openecomp.cl.mdc.MdcContext;
+
+/**
+ * A dedicated servlet for handling Front-End Visualization Requests and performing feats of magic
+ * to execute the right model/type/config driven queries to build the D3 visualization output JSON
+ * back to the FE.
+ *
+ * @author DAVEA
+ *
+ */
+public class VisualizationServlet extends HttpServlet {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 4678831934652478571L;
+ private static final Logger LOG =
+ LoggerFactory.getInstance().getLogger(VisualizationServlet.class);
+ private static final String VISUALIZATION_API_ENDPOINT = "prepareVisualization";
+ private final VisualizationService visualizationService;
+ /**
+ * Instantiates a new visualization servlet.
+ *
+ * @throws Exception the exception
+ */
+ public VisualizationServlet() throws Exception {
+ this.visualizationService = new VisualizationService(OxmModelLoader.getInstance());
+ }
+
+ /**
+ * Inits the.
+ *
+ * @param filterConfig the filter config
+ * @throws ServletException the servlet exception
+ */
+ public void init(FilterConfig filterConfig) throws ServletException {
+ LOG.debug(AaiUiMsgs.DEBUG_GENERIC, "init()");
+ }
+
+ /**
+ * Gets the request body.
+ *
+ * @param request the request
+ * @return the request body
+ */
+ private String getRequestBody(HttpServletRequest request) {
+
+ ResettableStreamHttpServletRequest requestWrapper =
+ new ResettableStreamHttpServletRequest(request);
+
+ String body = null;
+ try {
+ body = IOUtils.toString(requestWrapper.getRequestBody());
+ } catch (IOException exc) {
+ LOG.error(AaiUiMsgs.EXCEPTION_CAUGHT, "Trying to get body from request",
+ exc.getLocalizedMessage());
+ }
+
+ return body;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest,
+ * javax.servlet.http.HttpServletResponse)
+ */
+ @Override
+ protected void doGet(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ doPost(request, response);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest,
+ * javax.servlet.http.HttpServletResponse)
+ */
+ @Override
+ protected void doPost(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ String txnID = request.getHeader("X-TransactionId");
+ if (txnID == null) {
+ txnID = NodeUtils.getRandomTxnId();
+ }
+
+ String partnerName = request.getHeader("X-FromAppId");
+ if (partnerName == null) {
+ partnerName = "Browser";
+ }
+
+ MdcContext.initialize(txnID, "AAI-UI", "", partnerName, request.getRemoteAddr());
+
+ String postRequestBody = getRequestBody(request);
+
+ String requestUri = request.getRequestURI();
+ OperationResult operationResult = null;
+
+ /*
+ * For now we only have a single API call but there could be more in the future
+ */
+ if (requestUri.endsWith(VISUALIZATION_API_ENDPOINT)) {
+
+ /*
+ * Work our magic and determine the best way to interrogate AAI to get the stuff we are
+ * interested in. Perhaps it should be an edge-tag-query or perhaps it is a straight up
+ * derived self-link query.
+ */
+
+ /*
+ * Map request body to an interpreted API PoJo object
+ */
+ QueryRequest queryRequest = visualizationService.analyzeQueryRequestBody(postRequestBody);
+
+ if (queryRequest != null) {
+ operationResult = visualizationService.buildVisualizationUsingGenericQuery(queryRequest);
+ } else {
+ LOG.error(AaiUiMsgs.FAILED_TO_ANALYZE,
+ String.format("Failed to analyze post request query body = '%s'", postRequestBody));
+
+ operationResult = new OperationResult();
+ operationResult.setResult(500,
+ String.format("Failed to analyze post request query body = '%s'", postRequestBody));
+
+ }
+
+ } else {
+ // unhandled type
+ LOG.error(AaiUiMsgs.UNKNOWN_SERVER_ERROR, "Unhandled requestUri - " + requestUri);
+ operationResult = new OperationResult();
+ operationResult.setResult(500, "Unknown Server Error: Unhandled requestUri = " + requestUri);
+ }
+
+ PrintWriter out = response.getWriter();
+ response.addHeader("Content-Type", "application/xml");
+
+ response.setStatus(operationResult.getResultCode());
+
+ if (operationResult.getResultCode() == 200) {
+ response.setContentLength(operationResult.getResult().length());
+ out.print(operationResult.getResult());
+ out.print("\n");
+ } else {
+ response.setContentLength(operationResult.getResult().length());
+ out.print(operationResult.getResult());
+ out.print("\n");
+ }
+ }
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ visualizationService.shutdown();
+ }
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/task/CollectNodeSelfLinkTask.java b/src/main/java/org/openecomp/sparky/viewandinspect/task/CollectNodeSelfLinkTask.java
new file mode 100644
index 0000000..6c482e9
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/task/CollectNodeSelfLinkTask.java
@@ -0,0 +1,60 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.task;
+
+import java.util.function.Supplier;
+
+import org.openecomp.sparky.dal.aai.ActiveInventoryDataProvider;
+import org.openecomp.sparky.dal.rest.OperationResult;
+
+/**
+ * The Class CollectNodeSelfLinkTask.
+ */
+public class CollectNodeSelfLinkTask implements Supplier<OperationResult> {
+
+ private String selfLink;
+ private ActiveInventoryDataProvider aaiProvider;
+
+ /**
+ * Instantiates a new collect node self link task.
+ *
+ * @param selfLink the self link
+ * @param aaiProvider the aai provider
+ */
+ public CollectNodeSelfLinkTask(String selfLink, ActiveInventoryDataProvider aaiProvider) {
+ this.selfLink = selfLink;
+ this.aaiProvider = aaiProvider;
+ }
+
+ /* (non-Javadoc)
+ * @see java.util.function.Supplier#get()
+ */
+ @Override
+ public OperationResult get() {
+ return aaiProvider.queryActiveInventoryWithRetries(selfLink, "application/json", 5);
+ }
+
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/task/PerformNodeSelfLinkProcessingTask.java b/src/main/java/org/openecomp/sparky/viewandinspect/task/PerformNodeSelfLinkProcessingTask.java
new file mode 100644
index 0000000..b7fe3a5
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/task/PerformNodeSelfLinkProcessingTask.java
@@ -0,0 +1,104 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.task;
+
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.openecomp.cl.api.Logger;
+import org.openecomp.cl.eelf.LoggerFactory;
+import org.openecomp.sparky.dal.aai.ActiveInventoryDataProvider;
+import org.openecomp.sparky.dal.aai.config.ActiveInventoryConfig;
+import org.openecomp.sparky.dal.rest.OperationResult;
+import org.openecomp.sparky.logging.AaiUiMsgs;
+import org.openecomp.sparky.viewandinspect.entity.NodeProcessingTransaction;
+import org.slf4j.MDC;
+
+/**
+ * The Class PerformNodeSelfLinkProcessingTask.
+ */
+public class PerformNodeSelfLinkProcessingTask implements Supplier<NodeProcessingTransaction> {
+
+ private static final Logger logger =
+ LoggerFactory.getInstance().getLogger(PerformNodeSelfLinkProcessingTask.class);
+
+ private NodeProcessingTransaction txn;
+ private ActiveInventoryDataProvider aaiProvider;
+ private Map<String, String> contextMap;
+
+ /**
+ * Instantiates a new perform node self link processing task.
+ *
+ * @param txn the txn
+ * @param requestParameters the request parameters
+ * @param aaiProvider the aai provider
+ */
+ public PerformNodeSelfLinkProcessingTask(NodeProcessingTransaction txn, String requestParameters,
+ ActiveInventoryDataProvider aaiProvider) {
+ this.aaiProvider = aaiProvider;
+ this.txn = txn;
+ this.contextMap = MDC.getCopyOfContextMap();
+ }
+
+ /* (non-Javadoc)
+ * @see java.util.function.Supplier#get()
+ */
+ @Override
+ public NodeProcessingTransaction get() {
+ MDC.setContextMap(contextMap);
+ String link = txn.getSelfLinkWithModifiers();
+
+ if (link == null) {
+ OperationResult opResult = new OperationResult();
+ opResult.setResult(500, "Aborting self-link processing because self link is null");
+ txn.setOpResult(opResult);
+ return txn;
+ }
+
+ if (logger.isDebugEnabled()) {
+ logger.debug(AaiUiMsgs.DEBUG_GENERIC, "Collecting " + link);
+ }
+
+ OperationResult opResult = null;
+ try {
+ opResult = aaiProvider.queryActiveInventoryWithRetries(link, "application/json",
+ ActiveInventoryConfig.getConfig().getAaiRestConfig().getNumRequestRetries());
+ } catch (Exception exc) {
+ opResult = new OperationResult();
+ opResult.setResult(500, "Querying AAI with retry failed due to an exception.");
+ logger.error(AaiUiMsgs.ERROR_AAI_QUERY_WITH_RETRY, exc.getMessage());
+ }
+
+ if (logger.isDebugEnabled()) {
+ logger.debug(AaiUiMsgs.DEBUG_GENERIC, "Operation result = " + opResult.toString());
+ }
+
+ txn.setOpResult(opResult);
+ return txn;
+
+ }
+
+}
diff --git a/src/main/java/org/openecomp/sparky/viewandinspect/task/PerformSelfLinkDeterminationTask.java b/src/main/java/org/openecomp/sparky/viewandinspect/task/PerformSelfLinkDeterminationTask.java
new file mode 100644
index 0000000..948c5cb
--- /dev/null
+++ b/src/main/java/org/openecomp/sparky/viewandinspect/task/PerformSelfLinkDeterminationTask.java
@@ -0,0 +1,96 @@
+/**
+ * ============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.openecomp.sparky.viewandinspect.task;
+
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.openecomp.sparky.dal.aai.ActiveInventoryDataProvider;
+import org.openecomp.sparky.dal.aai.config.ActiveInventoryConfig;
+import org.openecomp.sparky.dal.rest.OperationResult;
+import org.openecomp.sparky.logging.AaiUiMsgs;
+import org.openecomp.sparky.viewandinspect.entity.SelfLinkDeterminationTransaction;
+import org.openecomp.cl.api.Logger;
+import org.openecomp.cl.eelf.LoggerFactory;
+import org.slf4j.MDC;
+
+public class PerformSelfLinkDeterminationTask implements Supplier<SelfLinkDeterminationTransaction> {
+
+ private static final Logger logger =
+ LoggerFactory.getInstance().getLogger(PerformSelfLinkDeterminationTask.class);
+
+ private SelfLinkDeterminationTransaction txn;
+ private ActiveInventoryDataProvider aaiProvider;
+ private Map<String, String> contextMap;
+
+
+ /**
+ * Instantiates a new perform node self link processing task.
+ *
+ * @param txn the txn
+ * @param requestParameters the request parameters
+ * @param aaiProvider the aai provider
+ */
+ public PerformSelfLinkDeterminationTask(SelfLinkDeterminationTransaction txn, String requestParameters,
+ ActiveInventoryDataProvider aaiProvider) {
+
+ this.aaiProvider = aaiProvider;
+ this.txn = txn;
+ this.contextMap = MDC.getCopyOfContextMap();
+ }
+
+ /* (non-Javadoc)
+ * @see java.util.function.Supplier#get()
+ */
+ @Override
+ public SelfLinkDeterminationTransaction get() {
+ MDC.setContextMap(contextMap);
+ if (txn.getQueryString() == null) {
+ OperationResult opResult = new OperationResult();
+ opResult.setResult(500, "Aborting self-link determination because self link query is null.");
+ txn.setOpResult(opResult);
+ return txn;
+ }
+
+ OperationResult opResult = null;
+ try {
+ opResult = aaiProvider.queryActiveInventoryWithRetries(txn.getQueryString(), "application/json",
+ ActiveInventoryConfig.getConfig().getAaiRestConfig().getNumRequestRetries());
+ } catch (Exception exc) {
+ opResult = new OperationResult();
+ opResult.setResult(500, "Querying AAI with retry failed due to an exception.");
+ logger.error(AaiUiMsgs.ERROR_AAI_QUERY_WITH_RETRY, exc.getMessage());
+ }
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Operation result = " + opResult.toString());
+ }
+
+ txn.setOpResult(opResult);
+ return txn;
+
+ }
+
+} \ No newline at end of file