From e5da3da7d7e0430a8d8d5c7c4a031c3ba557e247 Mon Sep 17 00:00:00 2001 From: da490c Date: Thu, 8 Mar 2018 12:54:22 -0500 Subject: [PATCH] Integrate Sparky-BE with Gizmo Issue-ID: AAI-847 Change-Id: I8c61e681c4f427f6e567dd8498b6f5f7baa19c85 Signed-off-by: da490c --- .../java/org/onap/aai/sparky/dal/GizmoAdapter.java | 334 +++++++ .../sparky/sync/task/PerformGizmoRetrieval.java | 93 ++ .../java/org/onap/aai/sparky/util/NodeUtils.java | 104 ++- .../config/VisualizationConfigs.java | 47 +- .../viewandinspect/entity/ActiveInventoryNode.java | 40 + .../sparky/viewandinspect/entity/GizmoEntity.java | 96 ++ .../entity/GizmoRelationshipEntity.java | 101 +++ .../entity/GizmoRelationshipHint.java | 75 ++ .../services/BaseGizmoVisualizationContext.java | 988 +++++++++++++++++++++ .../services/BaseVisualizationService.java | 23 +- .../PerformGizmoNodeSelfLinkProcessingTask.java | 126 +++ .../config/oxm/OxmEntityContainerLookup.java | 99 +++ .../sparky/synchronizer/GizmoEntitySummarizer.java | 251 ++++++ .../sparky/util/OxmModelAndProcessorHelper.java | 225 ++--- .../BaseVisualizationServiceTest.java | 11 +- .../sync/ViewInspectGizmoEntitySynchronizer.java | 792 +++++++++++++++++ .../sync/ViewInspectGizmoSyncController.java | 106 +++ 17 files changed, 3371 insertions(+), 140 deletions(-) create mode 100644 src/main/java/org/onap/aai/sparky/dal/GizmoAdapter.java create mode 100644 src/main/java/org/onap/aai/sparky/sync/task/PerformGizmoRetrieval.java create mode 100644 src/main/java/org/onap/aai/sparky/viewandinspect/entity/GizmoEntity.java create mode 100644 src/main/java/org/onap/aai/sparky/viewandinspect/entity/GizmoRelationshipEntity.java create mode 100644 src/main/java/org/onap/aai/sparky/viewandinspect/entity/GizmoRelationshipHint.java create mode 100644 src/main/java/org/onap/aai/sparky/viewandinspect/services/BaseGizmoVisualizationContext.java create mode 100644 src/main/java/org/onap/aai/sparky/viewandinspect/task/PerformGizmoNodeSelfLinkProcessingTask.java create mode 100644 src/test/java/org/onap/aai/sparky/config/oxm/OxmEntityContainerLookup.java create mode 100644 src/test/java/org/onap/aai/sparky/synchronizer/GizmoEntitySummarizer.java create mode 100644 src/test/java/org/onap/aai/sparky/viewandinspect/sync/ViewInspectGizmoEntitySynchronizer.java create mode 100644 src/test/java/org/onap/aai/sparky/viewandinspect/sync/ViewInspectGizmoSyncController.java diff --git a/src/main/java/org/onap/aai/sparky/dal/GizmoAdapter.java b/src/main/java/org/onap/aai/sparky/dal/GizmoAdapter.java new file mode 100644 index 0000000..99a967c --- /dev/null +++ b/src/main/java/org/onap/aai/sparky/dal/GizmoAdapter.java @@ -0,0 +1,334 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017 Amdocs + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.sparky.dal; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriBuilder; + +import org.onap.aai.cl.api.Logger; +import org.onap.aai.cl.eelf.LoggerFactory; +import org.onap.aai.restclient.client.OperationResult; +import org.onap.aai.restclient.client.RestClient; +import org.onap.aai.restclient.enums.RestAuthenticationMode; +import org.onap.aai.sparky.config.oxm.OxmModelLoader; +import org.onap.aai.sparky.dal.exception.ElasticSearchOperationException; +import org.onap.aai.sparky.dal.rest.RestClientConstructionException; +import org.onap.aai.sparky.dal.rest.RestClientFactory; +import org.onap.aai.sparky.dal.rest.config.RestEndpointConfig; +import org.onap.aai.sparky.logging.AaiUiMsgs; +import org.onap.aai.sparky.util.NodeUtils; + +/** + * The Class GizmoAdapter. + */ + +public class GizmoAdapter { + + private static final Logger LOG = LoggerFactory.getInstance().getLogger(GizmoAdapter.class); + + private static final String HEADER_TRANS_ID = "X-TransactionId"; + private static final String HEADER_FROM_APP_ID = "X-FromAppId"; + private static final String HEADER_AUTHORIZATION = "Authorization"; + + private static final String HTTP_SCHEME = "http"; + private static final String HTTPS_SCHEME = "https"; + + private static final String TRANSACTION_ID_PREFIX = "txnId-"; + private static final String UI_APP_NAME = "AAI-UI"; + + private OxmModelLoader oxmModelLoader; + + private RestEndpointConfig endpointConfig; + + private RestClient restClient; + + private String inventoryBasePath; + private String relationshipsBasePath; + + /** + * Instantiates a new active inventory adapter. + * + * @throws RestClientConstructionException + * + */ + + public GizmoAdapter(OxmModelLoader oxmModelLoader, RestEndpointConfig endpointConfig) + throws ElasticSearchOperationException, IOException, RestClientConstructionException { + + this.oxmModelLoader = oxmModelLoader; + this.endpointConfig = endpointConfig; + this.restClient = RestClientFactory.buildClient(endpointConfig); + + } + + public String getRelationshipsBasePath() { + return relationshipsBasePath; + } + + public void setRelationshipsBasePath(String relationshipsBasePath) { + this.relationshipsBasePath = relationshipsBasePath; + } + + public String getInventoryBasePath() { + return inventoryBasePath; + } + + public void setInventoryBasePath(String inventoryBasePath) { + this.inventoryBasePath = inventoryBasePath; + } + + public String getFullInventoryUrl(String resourceUrl) throws Exception { + final String host = endpointConfig.getEndpointIpAddress(); + final String port = endpointConfig.getEndpointServerPort(); + final String basePath = getInventoryBasePath(); + return String.format("https://%s:%s%s%s", host, port, basePath, resourceUrl); + } + + public String addServerDetailsToUrl(String resourceUrl) throws Exception { + final String host = endpointConfig.getEndpointIpAddress(); + final String port = endpointConfig.getEndpointServerPort(); + return String.format("https://%s:%s/%s", host, port, resourceUrl); + } + + public String getFullRelationshipUrl(String resourceUrl) throws Exception { + final String host = endpointConfig.getEndpointIpAddress(); + final String port = endpointConfig.getEndpointServerPort(); + final String basePath = getRelationshipsBasePath(); + return String.format("https://%s:%s%s%s", host, port, basePath, resourceUrl); + } + + protected Map> getMessageHeaders() { + + Map> headers = new HashMap>(); + + headers.putIfAbsent(HEADER_FROM_APP_ID, new ArrayList()); + headers.get(HEADER_FROM_APP_ID).add(UI_APP_NAME); + + headers.putIfAbsent(HEADER_TRANS_ID, new ArrayList()); + headers.get(HEADER_TRANS_ID).add(TRANSACTION_ID_PREFIX + NodeUtils.getRandomTxnId()); + + if (endpointConfig.getRestAuthenticationMode() == RestAuthenticationMode.SSL_BASIC) { + + headers.putIfAbsent(HEADER_AUTHORIZATION, new ArrayList()); + headers.get(HEADER_AUTHORIZATION).add(getBasicAuthenticationCredentials()); + + } + + return headers; + } + + protected String getBasicAuthenticationCredentials() { + String usernameAndPassword = String.join(":", endpointConfig.getBasicAuthUserName(), + endpointConfig.getBasicAuthPassword()); + return "Basic " + java.util.Base64.getEncoder().encodeToString(usernameAndPassword.getBytes()); + } + + /** + * Our retry conditions should be very specific. + * + * @param r + * the r + * @return true, if successful + */ + private boolean shouldRetryRequest(OperationResult r) { + + if (r == null) { + return true; + } + + int rc = r.getResultCode(); + + if (rc == 200) { + return false; + } + + if (rc == 404) { + return false; + } + + return true; + + } + + /** + * Query active inventory. + * + * @param url + * the url + * @param acceptContentType + * the accept content type + * @return the operation result + */ + OperationResult queryGizmo(String url, String acceptContentType) { + + return restClient.get(url, getMessageHeaders(), MediaType.APPLICATION_JSON_TYPE); + + } + + public RestEndpointConfig getEndpointConfig() { + return endpointConfig; + } + + public void setEndpointConfig(RestEndpointConfig endpointConfig) { + this.endpointConfig = endpointConfig; + } + + public OperationResult queryGizmoWithRetries(String url, String responseType, int numRetries) { + + OperationResult result = null; + + for (int retryCount = 0; retryCount < numRetries; retryCount++) { + + LOG.debug(AaiUiMsgs.QUERY_AAI_RETRY_SEQ, url, String.valueOf(retryCount + 1)); + + result = queryGizmo(url, responseType); + + /** + * Record number of times we have attempted the request to later + * summarize how many times we are generally retrying over thousands + * of messages in a sync. + * + * If the number of retries is surprisingly high, then we need to + * understand why that is as the number of retries is also causing a + * heavier load on AAI beyond the throttling controls we already + * have in place in term of the transaction rate controller and + * number of parallelized threads per task processor. + */ + + result.setNumRetries(retryCount); + + if (!shouldRetryRequest(result)) { + + result.setFromCache(false); + LOG.debug(AaiUiMsgs.QUERY_AAI_RETRY_DONE_SEQ, url, String.valueOf(retryCount + 1)); + + return result; + } + + try { + /* + * Sleep between re-tries to be nice to the target system. + */ + Thread.sleep(50); + } catch (InterruptedException exc) { + LOG.error(AaiUiMsgs.QUERY_AAI_WAIT_INTERRUPTION, exc.getLocalizedMessage()); + break; + } + LOG.error(AaiUiMsgs.QUERY_AAI_RETRY_FAILURE_WITH_SEQ, url, String.valueOf(retryCount + 1)); + + } + + LOG.info(AaiUiMsgs.QUERY_AAI_RETRY_MAXED_OUT, url); + + return result; + + } + + /** + * This method adds a scheme, host and port (if missing) to the passed-in + * URI. If these parts of the URI are already present, they will not be + * duplicated. + * + * @param selflink + * The URI to repair + * @param queryParams + * The query parameters as a single string + * @return The corrected URI (i.e. includes a scheme/host/port) + */ + + private String repairGizmoSelfLink(String baseUrlPath, String selfLink, String queryParams) { + + if (selfLink == null) { + return selfLink; + } + + if (selfLink.startsWith("http") || selfLink.startsWith("https")) { + return selfLink; + } + + UriBuilder builder = UriBuilder.fromPath(baseUrlPath + "/" + selfLink) + .host(endpointConfig.getEndpointIpAddress()) + .port(Integer.parseInt(endpointConfig.getEndpointServerPort())); + + switch (endpointConfig.getRestAuthenticationMode()) { + + case SSL_BASIC: + case SSL_CERT: { + builder.scheme(HTTPS_SCHEME); + break; + } + + default: { + builder.scheme(HTTP_SCHEME); + } + } + + boolean includeQueryParams = ((null != queryParams) && (!"".equals(queryParams))); + + /* + * builder.build().toString() will encode special characters to hexadecimal pairs prefixed with + * a '%' so we're adding the query parameters separately, in their UTF-8 representations, so + * that characters such as '?', '&', etc. remain intact as needed by the synchronizer + */ + return (builder.build().toString() + (includeQueryParams ? queryParams : "")); + + } + + public String repairRelationshipSelfLink(String selflink, String queryParams) { + return repairGizmoSelfLink(relationshipsBasePath, selflink, queryParams); + } + + public String repairInventorySelfLink(String selflink, String queryParams) { + return repairGizmoSelfLink(inventoryBasePath, selflink, queryParams); + } + + public OperationResult getSelfLinksByEntityType(String entityType) throws Exception { + + if (entityType == null) { + throw new NullPointerException("Failed to getSelfLinksByEntityType() because entityType is null"); + } + + String link = getFullInventoryUrl(entityType); + + return queryGizmoWithRetries(link, "application/json", endpointConfig.getNumRequestRetries()); + + } + + public static String extractResourcePath(String selflink) { + try { + return new URI(selflink).getRawPath(); + } catch (URISyntaxException uriSyntaxException) { + LOG.error(AaiUiMsgs.ERROR_EXTRACTING_RESOURCE_PATH_FROM_LINK, uriSyntaxException.getMessage()); + return selflink; + } + } + +} diff --git a/src/main/java/org/onap/aai/sparky/sync/task/PerformGizmoRetrieval.java b/src/main/java/org/onap/aai/sparky/sync/task/PerformGizmoRetrieval.java new file mode 100644 index 0000000..374163f --- /dev/null +++ b/src/main/java/org/onap/aai/sparky/sync/task/PerformGizmoRetrieval.java @@ -0,0 +1,93 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017 Amdocs + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.sparky.sync.task; + +import java.util.Map; +import java.util.function.Supplier; + +import org.onap.aai.cl.api.Logger; +import org.onap.aai.cl.eelf.LoggerFactory; +import org.onap.aai.restclient.client.OperationResult; +import org.onap.aai.sparky.dal.GizmoAdapter; +import org.onap.aai.sparky.dal.NetworkTransaction; +import org.onap.aai.sparky.logging.AaiUiMsgs; +import org.slf4j.MDC; + +/* + * Consider abstraction the tasks into common elemnts, because most of them repeat a generic call + * flow pattern + */ + +/** + * The Class PerformActiveInventoryRetrieval. + */ +public class PerformGizmoRetrieval implements Supplier { + + private static Logger logger = LoggerFactory.getInstance().getLogger(PerformGizmoRetrieval.class); + + private NetworkTransaction txn; + private GizmoAdapter gizmoAdapter; + private Map contextMap; + + /** + * Instantiates a new perform active inventory retrieval. + * + * @param txn the txn + * @param aaiProvider the aai provider + */ + public PerformGizmoRetrieval(NetworkTransaction txn, + GizmoAdapter gizmoAdapter) { + this.txn = txn; + this.gizmoAdapter = gizmoAdapter; + this.contextMap = MDC.getCopyOfContextMap(); + } + + /* (non-Javadoc) + * @see java.util.function.Supplier#get() + */ + @Override + public NetworkTransaction get() { + + txn.setTaskAgeInMs(); + + long startTimeInMs = System.currentTimeMillis(); + MDC.setContextMap(contextMap); + OperationResult result = null; + try { + result = gizmoAdapter.queryGizmoWithRetries(txn.getLink(), "application/json", 5); + } catch (Exception exc) { + logger.error(AaiUiMsgs.ERROR_GENERIC,"Failure to resolve self link from AAI. Error = " + exc.getMessage()); + result = new OperationResult(500, + "Caught an exception while trying to resolve link = " + exc.getMessage()); + } finally { + txn.setOperationResult(result); + txn.setOpTimeInMs(System.currentTimeMillis() - startTimeInMs); + } + + return txn; + } + + protected void setContextMap(Map contextMap) { + this.contextMap = contextMap; + } +} diff --git a/src/main/java/org/onap/aai/sparky/util/NodeUtils.java b/src/main/java/org/onap/aai/sparky/util/NodeUtils.java index 7657d7e..dd8e882 100644 --- a/src/main/java/org/onap/aai/sparky/util/NodeUtils.java +++ b/src/main/java/org/onap/aai/sparky/util/NodeUtils.java @@ -73,7 +73,9 @@ public class NodeUtils { private static SecureRandom sRandom = new SecureRandom(); private static final Pattern AAI_VERSION_PREFIX = Pattern.compile("/aai/v[0-9]+/(.*)"); - + private static final Pattern GIZMO_VERSION_PREFIX = Pattern.compile("[/]*services/inventory/v[0-9]+/(.*)"); + private static final Pattern GIZMO_RELATIONSHIP_VERSION_PREFIX = Pattern.compile("services/inventory/relationships/v[0-9]+/(.*)"); + public static synchronized String getRandomTxnId(){ byte bytes[] = new byte[6]; @@ -119,6 +121,53 @@ public class NodeUtils { return null; } + + public static String extractRawGizmoPathWithoutVersion(String resourceLink) { + + try { + + String rawPath = new URI(resourceLink).getRawPath(); + + Matcher m = GIZMO_VERSION_PREFIX.matcher(rawPath); + + if (m.matches()) { + + if ( m.groupCount() >= 1) { + return m.group(1); + } + + } + } catch (Exception e) { + } + + return null; + + } + + public static String extractRawGizmoRelationshipPathWithoutVersion(String resourceLink) { + + try { + + String rawPath = new URI(resourceLink).getRawPath(); + + Matcher m = GIZMO_RELATIONSHIP_VERSION_PREFIX.matcher(rawPath); + + if (m.matches()) { + + if ( m.groupCount() >= 1) { + return m.group(1); + } + + } + } catch (Exception e) { + } + + return null; + + } + + + /** * Checks if is numeric. @@ -163,12 +212,7 @@ public class NodeUtils { return Executors.newScheduledThreadPool(numWorkers + 1, namedThreadFactory); } - /** - * Calculate edit attribute uri. - * - * @param link the link - * @return the string - */ + public static String calculateEditAttributeUri(String link) { String uri = null; @@ -183,6 +227,7 @@ public class NodeUtils { return uri; } + /** * Generate unique sha digest. * @@ -612,6 +657,51 @@ public class NodeUtils { } } + } + + public static String extractObjectValueByKey(JsonNode node, String searchKey) { + + if (node == null) { + return null; + } + + if (node.isObject()) { + Iterator> nodeIterator = node.fields(); + + while (nodeIterator.hasNext()) { + Map.Entry entry = nodeIterator.next(); + if (!entry.getValue().isValueNode()) { + return extractObjectValueByKey(entry.getValue(), searchKey); + } + + String name = entry.getKey(); + if (name.equalsIgnoreCase(searchKey)) { + + JsonNode entryNode = entry.getValue(); + + if (entryNode.isArray()) { + + Iterator arrayItemsIterator = entryNode.elements(); + while (arrayItemsIterator.hasNext()) { + return arrayItemsIterator.next().asText(); + } + + } else { + return entry.getValue().asText(); + } + + + } + } + } else if (node.isArray()) { + Iterator arrayItemsIterator = node.elements(); + while (arrayItemsIterator.hasNext()) { + return extractObjectValueByKey(arrayItemsIterator.next(), searchKey); + } + + } + + return null; } diff --git a/src/main/java/org/onap/aai/sparky/viewandinspect/config/VisualizationConfigs.java b/src/main/java/org/onap/aai/sparky/viewandinspect/config/VisualizationConfigs.java index a0be371..aff24bc 100644 --- a/src/main/java/org/onap/aai/sparky/viewandinspect/config/VisualizationConfigs.java +++ b/src/main/java/org/onap/aai/sparky/viewandinspect/config/VisualizationConfigs.java @@ -46,11 +46,15 @@ public class VisualizationConfigs { private boolean makeAllNeighborsBidirectional; private ArrayList shallowEntities; + + private boolean gizmoEnabled; /** * Instantiates a new visualization config. */ - public VisualizationConfigs() {} + public VisualizationConfigs() { + this.gizmoEnabled = false; + } public ArrayList getShallowEntities() { return shallowEntities; @@ -129,23 +133,28 @@ public class VisualizationConfigs { this.numOfThreadsToFetchNodeIntegrity = numOfThreadsToFetchNodeIntegrity; } - - - @Override - public String toString() { - return "VisualizationConfigs [maxSelfLinkTraversalDepth=" + maxSelfLinkTraversalDepth - + ", visualizationDebugEnabled=" + visualizationDebugEnabled + ", " - + (aaiEntityNodeDescriptors != null - ? "aaiEntityNodeDescriptors=" + aaiEntityNodeDescriptors + ", " : "") - + (generalNodeClassName != null ? "generalNodeClassName=" + generalNodeClassName + ", " - : "") - + (searchNodeClassName != null ? "searchNodeClassName=" + searchNodeClassName + ", " : "") - + (selectedSearchedNodeClassName != null - ? "selectedSearchedNodeClassName=" + selectedSearchedNodeClassName + ", " : "") - + "numOfThreadsToFetchNodeIntegrity=" + numOfThreadsToFetchNodeIntegrity - + ", makeAllNeighborsBidirectional=" + makeAllNeighborsBidirectional + "]"; - } - - + public boolean isGizmoEnabled() { + return gizmoEnabled; + } + + public void setGizmoEnabled(boolean gizmoEnabled) { + this.gizmoEnabled = gizmoEnabled; + } + + @Override + public String toString() { + return "VisualizationConfigs [maxSelfLinkTraversalDepth=" + maxSelfLinkTraversalDepth + + ", visualizationDebugEnabled=" + visualizationDebugEnabled + ", " + + (aaiEntityNodeDescriptors != null ? "aaiEntityNodeDescriptors=" + aaiEntityNodeDescriptors + ", " + : "") + + (generalNodeClassName != null ? "generalNodeClassName=" + generalNodeClassName + ", " : "") + + (searchNodeClassName != null ? "searchNodeClassName=" + searchNodeClassName + ", " : "") + + (selectedSearchedNodeClassName != null + ? "selectedSearchedNodeClassName=" + selectedSearchedNodeClassName + ", " : "") + + "numOfThreadsToFetchNodeIntegrity=" + numOfThreadsToFetchNodeIntegrity + + ", makeAllNeighborsBidirectional=" + makeAllNeighborsBidirectional + ", " + + (shallowEntities != null ? "shallowEntities=" + shallowEntities + ", " : "") + "gizmoEnabled=" + + gizmoEnabled + "]"; + } } diff --git a/src/main/java/org/onap/aai/sparky/viewandinspect/entity/ActiveInventoryNode.java b/src/main/java/org/onap/aai/sparky/viewandinspect/entity/ActiveInventoryNode.java index c5d9b4a..e90d469 100644 --- a/src/main/java/org/onap/aai/sparky/viewandinspect/entity/ActiveInventoryNode.java +++ b/src/main/java/org/onap/aai/sparky/viewandinspect/entity/ActiveInventoryNode.java @@ -66,6 +66,10 @@ public class ActiveInventoryNode { private boolean isRootNode; private ConcurrentLinkedDeque inboundNeighbors; private ConcurrentLinkedDeque outboundNeighbors; + + private ConcurrentLinkedDeque inboundNeighborSelfLinks; + private ConcurrentLinkedDeque outboundNeighborSelfLinks; + private List complexGroups; private List relationshipLists; private int nodeDepth; @@ -139,6 +143,10 @@ public class ActiveInventoryNode { isRootNode = false; inboundNeighbors = new ConcurrentLinkedDeque(); outboundNeighbors = new ConcurrentLinkedDeque(); + + inboundNeighborSelfLinks = new ConcurrentLinkedDeque(); + outboundNeighborSelfLinks = new ConcurrentLinkedDeque(); + complexGroups = new ArrayList(); relationshipLists = new ArrayList(); nodeDepth = DEFAULT_INIT_NODE_DEPTH; @@ -164,6 +172,38 @@ public class ActiveInventoryNode { } } + public void addInboundSelfLink(String link) { + + if (link == null) { + return; + } + + if (!inboundNeighborSelfLinks.contains(link)) { + inboundNeighborSelfLinks.add(link); + } + + } + + public void addOutboundSelfLink(String link) { + + if (link == null) { + return; + } + + if (!outboundNeighborSelfLinks.contains(link)) { + outboundNeighborSelfLinks.add(link); + } + + } + + public Collection getInboundNeighborSelfLinks() { + return inboundNeighborSelfLinks; + } + + public Collection getOutboundNeighborSelfLinks() { + return outboundNeighborSelfLinks; + } + public void addQueryParams(Collection params) { if (params != null & params.size() > 0) { diff --git a/src/main/java/org/onap/aai/sparky/viewandinspect/entity/GizmoEntity.java b/src/main/java/org/onap/aai/sparky/viewandinspect/entity/GizmoEntity.java new file mode 100644 index 0000000..211efa5 --- /dev/null +++ b/src/main/java/org/onap/aai/sparky/viewandinspect/entity/GizmoEntity.java @@ -0,0 +1,96 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017 Amdocs + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.sparky.viewandinspect.entity; + +import java.util.Arrays; +import java.util.Map; + +public class GizmoEntity { + + private String id; + private String type; + private String url; + private Map properties; + private GizmoRelationshipHint[] in; + private GizmoRelationshipHint[] out; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + + public GizmoRelationshipHint[] getIn() { + return in; + } + + public void setIn(GizmoRelationshipHint[] in) { + this.in = in; + } + + public GizmoRelationshipHint[] getOut() { + return out; + } + + public void setOut(GizmoRelationshipHint[] out) { + this.out = out; + } + + @Override + public String toString() { + return "GizmoEntity [" + (id != null ? "id=" + id + ", " : "") + + (type != null ? "type=" + type + ", " : "") + (url != null ? "url=" + url + ", " : "") + + (properties != null ? "properties=" + properties + ", " : "") + + (in != null ? "in=" + Arrays.toString(in) + ", " : "") + + (out != null ? "out=" + Arrays.toString(out) : "") + "]"; + } + + + +} diff --git a/src/main/java/org/onap/aai/sparky/viewandinspect/entity/GizmoRelationshipEntity.java b/src/main/java/org/onap/aai/sparky/viewandinspect/entity/GizmoRelationshipEntity.java new file mode 100644 index 0000000..a566d69 --- /dev/null +++ b/src/main/java/org/onap/aai/sparky/viewandinspect/entity/GizmoRelationshipEntity.java @@ -0,0 +1,101 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017 Amdocs + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.sparky.viewandinspect.entity; + +import java.util.Map; + +public class GizmoRelationshipEntity { + + /* + * {"id":"oe4ur-3a0-27th-fu8","type":"has","url": + * "services/inventory/relationships/v8/has/oe4ur-3a0-27th-fu8","source": + * "services/inventory/v8/generic-vnf/4248","target": + * "services/inventory/v8/vserver/20528", + * "properties":{"is-parent":"true","multiplicity":"many","has-del-target": + * "true","uses-resource": "true"}} + */ + + private String id; + private String type; + private String url; + private String source; + private String target; + private Map properties; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + 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; + } + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + + @Override + public String toString() { + return "GizmoRelationshipEntity [" + (id != null ? "id=" + id + ", " : "") + + (type != null ? "type=" + type + ", " : "") + (url != null ? "url=" + url + ", " : "") + + (source != null ? "source=" + source + ", " : "") + (target != null ? "target=" + target + ", " : "") + + (properties != null ? "properties=" + properties : "") + "]"; + } + +} diff --git a/src/main/java/org/onap/aai/sparky/viewandinspect/entity/GizmoRelationshipHint.java b/src/main/java/org/onap/aai/sparky/viewandinspect/entity/GizmoRelationshipHint.java new file mode 100644 index 0000000..f28ba5b --- /dev/null +++ b/src/main/java/org/onap/aai/sparky/viewandinspect/entity/GizmoRelationshipHint.java @@ -0,0 +1,75 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017 Amdocs + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.sparky.viewandinspect.entity; + +public class GizmoRelationshipHint { + + private String id; + private String type; + private String url; + private String source; + private String target; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + 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; + } + + + + } diff --git a/src/main/java/org/onap/aai/sparky/viewandinspect/services/BaseGizmoVisualizationContext.java b/src/main/java/org/onap/aai/sparky/viewandinspect/services/BaseGizmoVisualizationContext.java new file mode 100644 index 0000000..8963ff7 --- /dev/null +++ b/src/main/java/org/onap/aai/sparky/viewandinspect/services/BaseGizmoVisualizationContext.java @@ -0,0 +1,988 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017 Amdocs + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.sparky.viewandinspect.services; + +import static java.util.concurrent.CompletableFuture.supplyAsync; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.AtomicInteger; + +import org.onap.aai.cl.api.Logger; +import org.onap.aai.cl.eelf.LoggerFactory; +import org.onap.aai.restclient.client.OperationResult; +import org.onap.aai.sparky.config.oxm.OxmEntityDescriptor; +import org.onap.aai.sparky.config.oxm.OxmEntityLookup; +import org.onap.aai.sparky.dal.GizmoAdapter; +import org.onap.aai.sparky.logging.AaiUiMsgs; +import org.onap.aai.sparky.sync.entity.SearchableEntity; +import org.onap.aai.sparky.util.NodeUtils; +import org.onap.aai.sparky.viewandinspect.config.SparkyConstants; +import org.onap.aai.sparky.viewandinspect.config.VisualizationConfigs; +import org.onap.aai.sparky.viewandinspect.entity.ActiveInventoryNode; +import org.onap.aai.sparky.viewandinspect.entity.GizmoEntity; +import org.onap.aai.sparky.viewandinspect.entity.GizmoRelationshipEntity; +import org.onap.aai.sparky.viewandinspect.entity.GizmoRelationshipHint; +import org.onap.aai.sparky.viewandinspect.entity.InlineMessage; +import org.onap.aai.sparky.viewandinspect.entity.NodeProcessingTransaction; +import org.onap.aai.sparky.viewandinspect.entity.QueryParams; +import org.onap.aai.sparky.viewandinspect.enumeration.NodeProcessingAction; +import org.onap.aai.sparky.viewandinspect.enumeration.NodeProcessingState; +import org.onap.aai.sparky.viewandinspect.task.PerformGizmoNodeSelfLinkProcessingTask; + +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.PropertyNamingStrategy; + +/** + * The Class SelfLinkNodeCollector. + */ +public class BaseGizmoVisualizationContext implements VisualizationContext { + + private static final int MAX_DEPTH_EVALUATION_ATTEMPTS = 100; + + private static final Logger LOG = + LoggerFactory.getInstance().getLogger(BaseGizmoVisualizationContext.class); + + private final GizmoAdapter gizmoAdapter; + + private AtomicInteger numLinksDiscovered; + private AtomicInteger numSuccessfulLinkResolveFromCache; + private AtomicInteger numSuccessfulLinkResolveFromFromServer; + private AtomicInteger numFailedLinkResolve; + private AtomicInteger aaiWorkOnHand; + + private VisualizationConfigs visualizationConfigs; + + private AtomicInteger totalLinksRetrieved; + + private final long contextId; + private final String contextIdStr; + private long lastProcessStatesSummaryLogInMs = -1; + + + private ObjectMapper mapper; + private InlineMessage inlineMessage = null; + + private ExecutorService graphExecutorService; + private OxmEntityLookup oxmEntityLookup; + private boolean rootNodeFound; + + /* + * The node cache is intended to be a flat structure indexed by a primary key to avoid needlessly + * re-requesting the same self-links over-and-over again, to speed up the overall render time and + * more importantly to reduce the network cost of determining information we already have. + */ + private ConcurrentHashMap nodeCache; + + /** + * Instantiates a new self link node collector. + * + * @param loader the loader + * @throws Exception the exception + */ + public BaseGizmoVisualizationContext(long contextId, GizmoAdapter gizmoAdapter, + ExecutorService graphExecutorService, VisualizationConfigs visualizationConfigs, + OxmEntityLookup oxmEntityLookup) throws Exception { + + this.contextId = contextId; + this.contextIdStr = "[Context-Id=" + contextId + "]"; + this.gizmoAdapter = gizmoAdapter; + this.graphExecutorService = graphExecutorService; + this.visualizationConfigs = visualizationConfigs; + this.oxmEntityLookup = oxmEntityLookup; + + this.nodeCache = new ConcurrentHashMap(); + this.numLinksDiscovered = new AtomicInteger(0); + this.totalLinksRetrieved = new AtomicInteger(0); + this.numSuccessfulLinkResolveFromCache = new AtomicInteger(0); + this.numSuccessfulLinkResolveFromFromServer = new AtomicInteger(0); + this.numFailedLinkResolve = new AtomicInteger(0); + this.aaiWorkOnHand = new AtomicInteger(0); + + this.mapper = new ObjectMapper(); + mapper.setSerializationInclusion(Include.NON_EMPTY); + mapper.setPropertyNamingStrategy(new PropertyNamingStrategy.KebabCaseStrategy()); + this.rootNodeFound = false; + } + + protected boolean isRootNodeFound() { + return rootNodeFound; + } + + protected void setRootNodeFound(boolean rootNodeFound) { + this.rootNodeFound = rootNodeFound; + } + + public long getContextId() { + return contextId; + } + + public GizmoAdapter getGizmoAdapter() { + return gizmoAdapter; + } + + /** + * Process self link response. + * + * @param nodeId the node id + */ + private void processSelfLinkResponse(String nodeId) { + + if (nodeId == null) { + LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, + "Cannot process self link" + " response because nodeId is null"); + return; + } + + ActiveInventoryNode ain = nodeCache.get(nodeId); + + if (ain == null) { + LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, + "Cannot process self link response" + " because can't find node for id = " + nodeId); + return; + } + + GizmoEntity gizmoEntity = null; + + try { + gizmoEntity = mapper.readValue(ain.getOpResult().getResult(), GizmoEntity.class); + } catch (Exception exc) { + exc.printStackTrace(); + LOG.error(AaiUiMsgs.SELF_LINK_JSON_PARSE_ERROR, "Failed to marshal json" + + " response str into JsonNode with error, " + exc.getLocalizedMessage()); + ain.changeState(NodeProcessingState.ERROR, + NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_ERROR); + return; + } + + if (gizmoEntity == null) { + + LOG.error(AaiUiMsgs.SELF_LINK_JSON_PARSE_ERROR, + "Failed to parse json node str." + " Parse resulted a null value."); + ain.changeState(NodeProcessingState.ERROR, + NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_ERROR); + return; + } + + /* + * Now that we have the gizmo entity we can populate the AIN node with it, as well as the + * relationships + */ + + ain.setEntityType(gizmoEntity.getType()); + + ain.setPrimaryKeyName(getEntityTypePrimaryKeyName(gizmoEntity.getType())); + + OxmEntityDescriptor descriptor = oxmEntityLookup.getEntityDescriptors().get(gizmoEntity); + + if (descriptor != null) { + ain.setPrimaryKeyValue(getPrimaryKeyValues(gizmoEntity.getProperties(), + descriptor.getPrimaryKeyAttributeNames())); + } else { + LOG.error(AaiUiMsgs.ERROR_GENERIC, "Could not determine oxm descriptor for entity type = " + gizmoEntity.getType()); + } + + gizmoEntity.getProperties().forEach((key, value) -> { + ain.getProperties().put(key, value); + }); + + // add edit attributes link + if (ain.getSelfLink() != null) { + ain.addProperty(SparkyConstants.URI_ATTR_NAME, ain.getSelfLink()); + } + + + + /* + * Only discover neighbors if our depth is less than the Max-Traversal-Depth + */ + + if (ain.getNodeDepth() < this.visualizationConfigs.getMaxSelfLinkTraversalDepth()) { + + /* + * I think the next thing to do is: + * + * 1. Calculate the source / target node id 2. Add the nodeId to the incoming / outgoing links + * collection 3. Add the node to the node cache for processing + */ + + String resourceLink = null; + String relationshipNodeId = null; + ActiveInventoryNode relationshipNode = null; + + for (GizmoRelationshipHint inRelationship : gizmoEntity.getIn()) { + + if (inRelationship.getSource() != null) { + + resourceLink = NodeUtils.extractRawGizmoPathWithoutVersion(inRelationship.getSource()); + relationshipNodeId = NodeUtils.generateUniqueShaDigest(resourceLink); + + if (!nodeCache.containsKey(relationshipNodeId)) { + + relationshipNode = new ActiveInventoryNode(visualizationConfigs, oxmEntityLookup); + relationshipNode.setNodeId(relationshipNodeId); + relationshipNode.setSelfLink(resourceLink); + relationshipNode.changeState(NodeProcessingState.SELF_LINK_UNRESOLVED, + NodeProcessingAction.NEW_NODE_PROCESSED); + + ain.addInboundNeighbor(relationshipNodeId); + + addNode(relationshipNode); + + } + } + + } + + for (GizmoRelationshipHint outRelationship : gizmoEntity.getOut()) { + + if (outRelationship.getTarget() != null) { + + resourceLink = NodeUtils.extractRawGizmoPathWithoutVersion(outRelationship.getTarget()); + relationshipNodeId = NodeUtils.generateUniqueShaDigest(resourceLink); + + if (!nodeCache.containsKey(relationshipNodeId)) { + + relationshipNode = new ActiveInventoryNode(visualizationConfigs, oxmEntityLookup); + relationshipNode.setNodeId(relationshipNodeId); + relationshipNode.setSelfLink(resourceLink); + relationshipNode.changeState(NodeProcessingState.SELF_LINK_UNRESOLVED, + NodeProcessingAction.NEW_NODE_PROCESSED); + + ain.addOutboundNeighbor(relationshipNodeId); + + addNode(relationshipNode); + + } + } + + } + } + + ain.changeState(NodeProcessingState.READY, NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_OK); + + } + + /** + * Perform self link resolve. + * + * @param nodeId the node id + */ + private void performSelfLinkResolve(String nodeId) { + + if (nodeId == null) { + LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, + "Resolve of self-link" + " has been skipped because provided nodeId is null"); + return; + } + + ActiveInventoryNode ain = nodeCache.get(nodeId); + + if (ain == null) { + LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, "Failed to find node with id, " + nodeId + + ", from node cache. Resolve self-link method has been skipped."); + return; + } + + if (!ain.isSelfLinkPendingResolve()) { + + ain.setSelfLinkPendingResolve(true); + + // kick off async self-link resolution + + if (LOG.isDebugEnabled()) { + LOG.debug(AaiUiMsgs.DEBUG_GENERIC, + "About to process node in SELF_LINK_UNPROCESSED State, link = " + ain.getSelfLink()); + } + + numLinksDiscovered.incrementAndGet(); + + /* + * If the current node is the search target, we want to see everything the node has to offer + * from the self-link and not filter it to a single node. + */ + + NodeProcessingTransaction txn = new NodeProcessingTransaction(); + txn.setProcessingNode(ain); + txn.setRequestParameters(null); + aaiWorkOnHand.incrementAndGet(); + supplyAsync(new PerformGizmoNodeSelfLinkProcessingTask(txn, null, gizmoAdapter), + graphExecutorService).whenComplete((nodeTxn, error) -> { + + if (error != null) { + + /* + * an error processing the self link should probably result in the node processing + * state shifting to ERROR + */ + + nodeTxn.getProcessingNode().setSelflinkRetrievalFailure(true); + + nodeTxn.getProcessingNode().changeState(NodeProcessingState.ERROR, + NodeProcessingAction.SELF_LINK_RESOLVE_ERROR); + + nodeTxn.getProcessingNode().setSelfLinkPendingResolve(false); + + } else { + + totalLinksRetrieved.incrementAndGet(); + + OperationResult opResult = nodeTxn.getOpResult(); + + if (opResult != null && opResult.wasSuccessful()) { + + if (!opResult.wasSuccessful()) { + numFailedLinkResolve.incrementAndGet(); + } + + if (opResult.isFromCache()) { + numSuccessfulLinkResolveFromCache.incrementAndGet(); + } else { + numSuccessfulLinkResolveFromFromServer.incrementAndGet(); + } + + // success path + nodeTxn.getProcessingNode().setOpResult(opResult); + nodeTxn.getProcessingNode().changeState( + NodeProcessingState.SELF_LINK_RESPONSE_UNPROCESSED, + NodeProcessingAction.SELF_LINK_RESOLVE_OK); + + nodeTxn.getProcessingNode().setSelfLinkProcessed(true); + nodeTxn.getProcessingNode().setSelfLinkPendingResolve(false); + + } else { + LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, + "Self Link retrieval for link," + txn.getSelfLinkWithModifiers() + + ", failed with error code," + nodeTxn.getOpResult().getResultCode() + + ", and message," + nodeTxn.getOpResult().getResult()); + + nodeTxn.getProcessingNode().setSelflinkRetrievalFailure(true); + nodeTxn.getProcessingNode().setSelfLinkProcessed(true); + + nodeTxn.getProcessingNode().changeState(NodeProcessingState.ERROR, + NodeProcessingAction.SELF_LINK_RESOLVE_ERROR); + + nodeTxn.getProcessingNode().setSelfLinkPendingResolve(false); + + } + } + + aaiWorkOnHand.decrementAndGet(); + + }); + + } + + } + + public GizmoRelationshipEntity getGizmoRelationshipEntity(String gizmoJsonResponse) { + + GizmoRelationshipEntity gizmoRelationship = null; + try { + gizmoRelationship = mapper.readValue(gizmoJsonResponse, GizmoRelationshipEntity.class); + } catch (IOException exc) { + LOG.error(AaiUiMsgs.ERROR_GENERIC, "Failed to map json to GizmoRelationshipEntity. Error: " + exc.getMessage()); + } + + return gizmoRelationship; + + } + + public String getPrimaryKeyValues(Map properties, List pkeyNames) { + + StringBuilder sb = new StringBuilder(64); + + if (pkeyNames.size() > 0) { + String primaryKey = properties.get(pkeyNames.get(0)); + if (primaryKey != null) { + sb.append(primaryKey); + } else { + // this should be a fatal error because unless we can + // successfully retrieve all the expected keys we'll end up + // with a garbage node + LOG.error(AaiUiMsgs.EXTRACTION_ERROR, "ERROR: Failed to extract" + " keyName, " + + pkeyNames.get(0) + ", from properties , " + properties); + return null; + } + + for (int i = 1; i < pkeyNames.size(); i++) { + + String kv = properties.get(pkeyNames.get(i)); + if (kv != null) { + sb.append("/").append(kv); + } else { + // this should be a fatal error because unless we can + // successfully retrieve all the expected keys we'll end up + // with a garbage node + LOG.error(AaiUiMsgs.EXTRACTION_ERROR, "ERROR: failed to extract keyName, " + + pkeyNames.get(i) + ", from properties, " + properties); + return null; + } + } + + return sb.toString(); + + } + + return null; + + } + + + + /** + * Find and mark root node. + * + * @param queryParams the query params + * @return true, if successful + */ + private void findAndMarkRootNode(QueryParams queryParams) { + + if (isRootNodeFound()) { + return; + } + + for (ActiveInventoryNode cacheNode : nodeCache.values()) { + + if (queryParams.getSearchTargetNodeId().equals(cacheNode.getNodeId())) { + cacheNode.setNodeDepth(0); + cacheNode.setRootNode(true); + LOG.info(AaiUiMsgs.ROOT_NODE_DISCOVERED, queryParams.getSearchTargetNodeId()); + setRootNodeFound(true); + } + } + + } + + public void addNode(ActiveInventoryNode node) { + + if (node == null) { + return; + } + + nodeCache.putIfAbsent(node.getNodeId(), node); + } + + public VisualizationConfigs getVisualizationConfigs() { + return visualizationConfigs; + } + + public void setVisualizationConfigs(VisualizationConfigs visualizationConfigs) { + this.visualizationConfigs = visualizationConfigs; + } + + public OxmEntityLookup getOxmEntityLookup() { + return oxmEntityLookup; + } + + public void setOxmEntityLookup(OxmEntityLookup oxmEntityLookup) { + this.oxmEntityLookup = oxmEntityLookup; + } + + public ObjectMapper getMapper() { + return mapper; + } + + public void setMapper(ObjectMapper mapper) { + this.mapper = mapper; + } + + private void dumpThrottledWorkOnHandLog() { + dumpThrottledWorkOnHandLog(false); + } + + private void dumpThrottledWorkOnHandLog(boolean override) { + + if ((lastProcessStatesSummaryLogInMs < 0) + || ((System.currentTimeMillis() > (lastProcessStatesSummaryLogInMs + 5000))) || override) { + + lastProcessStatesSummaryLogInMs = System.currentTimeMillis(); + + int numInit = 0; + int numReady = 0; + int numError = 0; + int numSelfLinkUnresolved = 0; + int numSelfLinkResponseUnprocessed = 0; + + for (ActiveInventoryNode cacheNode : nodeCache.values()) { + + switch (cacheNode.getState()) { + + case INIT: { + numInit++; + break; + } + + case READY: { + numReady++; + break; + } + case ERROR: { + numError++; + break; + } + + case SELF_LINK_UNRESOLVED: { + numSelfLinkUnresolved++; + break; + } + + case SELF_LINK_RESPONSE_UNPROCESSED: { + numSelfLinkResponseUnprocessed++; + break; + } + + default: + break; + } + + } + + LOG.info(AaiUiMsgs.INFO_GENERIC, + String.format( + "ProcessCurrentStates for ContextId=%s, [PendingTxns=%d, numInit=%d, numSelfLinkUnresolved=%d, numSelfLinkResponseUnProcessed=%d, numReady=%d, numError=%d]", + contextIdStr, aaiWorkOnHand.get(), numInit, numSelfLinkUnresolved, numSelfLinkResponseUnprocessed, + numReady, numError)); + } + + } + + /** + * Process current node states. + * + * @param rootNodeDiscovered the root node discovered + */ + private void processCurrentNodeStates(QueryParams queryParams) { + /* + * Force an evaluation of node depths before determining if we should limit state-based + * traversal or processing. + */ + + findAndMarkRootNode(queryParams); + + verifyOutboundNeighbors(); + + for (ActiveInventoryNode cacheNode : nodeCache.values()) { + + if (LOG.isDebugEnabled()) { + LOG.debug(AaiUiMsgs.DEBUG_GENERIC, "processCurrentNodeState(), nid = " + + cacheNode.getNodeId() + " , nodeDepth = " + cacheNode.getNodeDepth()); + } + + switch (cacheNode.getState()) { + + case INIT: { + processInitialState(cacheNode.getNodeId()); + break; + } + + case READY: + case ERROR: { + break; + } + + case SELF_LINK_UNRESOLVED: { + performSelfLinkResolve(cacheNode.getNodeId()); + break; + } + + case SELF_LINK_RESPONSE_UNPROCESSED: { + processSelfLinkResponse(cacheNode.getNodeId()); + break; + } + + default: + break; + } + + } + + dumpThrottledWorkOnHandLog(); + + } + + + + public int getNumSuccessfulLinkResolveFromCache() { + return numSuccessfulLinkResolveFromCache.get(); + } + + public int getNumSuccessfulLinkResolveFromFromServer() { + return numSuccessfulLinkResolveFromFromServer.get(); + } + + public int getNumFailedLinkResolve() { + return numFailedLinkResolve.get(); + } + + public InlineMessage getInlineMessage() { + return inlineMessage; + } + + public void setInlineMessage(InlineMessage inlineMessage) { + this.inlineMessage = inlineMessage; + } + + public ConcurrentHashMap getNodeCache() { + return nodeCache; + } + + + + /** + * Process initial state. + * + * @param nodeId the node id + */ + private void processInitialState(String nodeId) { + + if (nodeId == null) { + LOG.error(AaiUiMsgs.FAILED_TO_PROCESS_INITIAL_STATE, "Node id is null"); + return; + } + + ActiveInventoryNode cachedNode = nodeCache.get(nodeId); + + if (cachedNode == null) { + LOG.error(AaiUiMsgs.FAILED_TO_PROCESS_INITIAL_STATE, + "Node cannot be" + " found for nodeId, " + nodeId); + return; + } + + if (cachedNode.getSelfLink() == null) { + + if (cachedNode.getNodeId() == null) { + + /* + * if the self link is null at the INIT state, which could be valid if this node is a + * complex attribute group which didn't originate from a self-link, but in that situation + * both the node id and node key should already be set. + */ + + cachedNode.changeState(NodeProcessingState.ERROR, NodeProcessingAction.NODE_IDENTITY_ERROR); + + } + + if (cachedNode.getNodeId() != null) { + + /* + * This should be the success path branch if the self-link is not set + */ + + cachedNode.changeState(NodeProcessingState.SELF_LINK_RESPONSE_UNPROCESSED, + NodeProcessingAction.SELF_LINK_RESPONSE_PARSE_OK); + + } + + } else { + + if (cachedNode.hasResolvedSelfLink()) { + LOG.error(AaiUiMsgs.INVALID_RESOLVE_STATE_DURING_INIT); + cachedNode.changeState(NodeProcessingState.ERROR, + NodeProcessingAction.UNEXPECTED_STATE_TRANSITION); + } else { + cachedNode.changeState(NodeProcessingState.SELF_LINK_UNRESOLVED, + NodeProcessingAction.SELF_LINK_SET); + } + } + } + + /** + * Process skeleton node. + * + * @param skeletonNode the skeleton node + * @param queryParams the query params + */ + private void processSearchableEntity(SearchableEntity searchTargetEntity, + QueryParams queryParams) { + + if (searchTargetEntity == null) { + return; + } + + if (searchTargetEntity.getId() == null) { + LOG.error(AaiUiMsgs.FAILED_TO_PROCESS_SKELETON_NODE, "Failed to process skeleton" + + " node because nodeId is null for node, " + searchTargetEntity.getLink()); + return; + } + + ActiveInventoryNode newNode = + new ActiveInventoryNode(this.visualizationConfigs, oxmEntityLookup); + + newNode.setNodeId(searchTargetEntity.getId()); + + newNode.setNodeDepth(0); + newNode.setRootNode(true); + LOG.info(AaiUiMsgs.ROOT_NODE_DISCOVERED, queryParams.getSearchTargetNodeId()); + setRootNodeFound(true); + + newNode.setSelfLink(searchTargetEntity.getLink()); + + nodeCache.putIfAbsent(newNode.getNodeId(), newNode); + } + + private int getTotalWorkOnHand() { + + int numNodesWithPendingStates = 0; + + if (isRootNodeFound()) { + evaluateNodeDepths(); + } + + for (ActiveInventoryNode n : nodeCache.values()) { + + switch (n.getState()) { + + case READY: + case ERROR: { + // do nothing, these are our normal + // exit states + break; + } + + default: { + + /* + * for all other states, there is work to be done + */ + numNodesWithPendingStates++; + } + + } + + } + + return (aaiWorkOnHand.get() + numNodesWithPendingStates); + + } + + /** + * Checks for out standing work. + * + * @return true, if successful + */ + private void processOutstandingWork(QueryParams queryParams) { + + while (getTotalWorkOnHand() > 0) { + + /* + * Force an evaluation of node depths before determining if we should limit state-based + * traversal or processing. + */ + + processCurrentNodeStates(queryParams); + + try { + Thread.sleep(10); + } catch (InterruptedException exc) { + LOG.error(AaiUiMsgs.PROCESSING_LOOP_INTERUPTED, exc.getMessage()); + return; + } + + } + + dumpThrottledWorkOnHandLog(true); + + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.aai.sparky.viewandinspect.services.VisualizationContext#processSelfLinks(org.onap.aai. + * sparky.sync.entity.SearchableEntity, org.onap.aai.sparky.viewandinspect.entity.QueryParams) + */ + @Override + public void processSelfLinks(SearchableEntity searchtargetEntity, QueryParams queryParams) { + + try { + + + if (searchtargetEntity == null) { + LOG.error(AaiUiMsgs.SELF_LINK_PROCESSING_ERROR, + contextIdStr + " - Failed to" + " processSelfLinks, searchtargetEntity is null"); + return; + } + + long startTimeInMs = System.currentTimeMillis(); + + processSearchableEntity(searchtargetEntity, queryParams); + + /* + * This method is blocking until we decouple it with a CountDownLatch await condition, and + * make the internal graph processing more event-y. + */ + + processOutstandingWork(queryParams); + + long totalResolveTime = (System.currentTimeMillis() - startTimeInMs); + + long opTime = System.currentTimeMillis() - startTimeInMs; + + LOG.info(AaiUiMsgs.ALL_TRANSACTIONS_RESOLVED, String.valueOf(totalResolveTime), + String.valueOf(totalLinksRetrieved.get()), String.valueOf(opTime)); + + } catch (Exception exc) { + LOG.error(AaiUiMsgs.VISUALIZATION_OUTPUT_ERROR, exc.getMessage()); + } + + } + + /** + * Verify outbound neighbors. + */ + private void verifyOutboundNeighbors() { + + for (ActiveInventoryNode srcNode : nodeCache.values()) { + + for (String targetNodeId : srcNode.getOutboundNeighbors()) { + + ActiveInventoryNode targetNode = nodeCache.get(targetNodeId); + + if (targetNode != null && srcNode.getNodeId() != null) { + + targetNode.addInboundNeighbor(srcNode.getNodeId()); + + if (this.visualizationConfigs.makeAllNeighborsBidirectional()) { + targetNode.addOutboundNeighbor(srcNode.getNodeId()); + } + + } + + } + + } + + } + + /** + * Evaluate node depths. + */ + private void evaluateNodeDepths() { + + int numChanged = -1; + int numAttempts = 0; + + while (numChanged != 0) { + + numChanged = 0; + numAttempts++; + + for (ActiveInventoryNode srcNode : nodeCache.values()) { + + if (srcNode.getState() == NodeProcessingState.INIT) { + + /* + * this maybe the only state that we don't want to to process the node depth on, because + * typically it won't have any valid fields set, and it may remain in a partial state + * until we have processed the self-link. + */ + + continue; + + } + + for (String targetNodeId : srcNode.getOutboundNeighbors()) { + ActiveInventoryNode targetNode = nodeCache.get(targetNodeId); + + if (targetNode != null) { + + if (targetNode.changeDepth(srcNode.getNodeDepth() + 1)) { + numChanged++; + } + } + } + + for (String targetNodeId : srcNode.getInboundNeighbors()) { + ActiveInventoryNode targetNode = nodeCache.get(targetNodeId); + + if (targetNode != null) { + + if (targetNode.changeDepth(srcNode.getNodeDepth() + 1)) { + numChanged++; + } + } + } + } + + if (numAttempts >= MAX_DEPTH_EVALUATION_ATTEMPTS) { + LOG.info(AaiUiMsgs.MAX_EVALUATION_ATTEMPTS_EXCEEDED); + return; + } + + } + + if (LOG.isDebugEnabled()) { + if (numAttempts > 0) { + LOG.debug(AaiUiMsgs.DEBUG_GENERIC, + "Evaluate node depths completed in " + numAttempts + " attempts"); + } else { + LOG.debug(AaiUiMsgs.DEBUG_GENERIC, + "Evaluate node depths completed in 0 attempts because all nodes at correct depth"); + } + } + + } + + + /** + * Gets the entity type primary key name. + * + * @param entityType the entity type + * @return the entity type primary key name + */ + + + private String getEntityTypePrimaryKeyName(String entityType) { + + if (entityType == null) { + LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE, + "node primary key" + " name because entity type is null"); + return null; + } + + OxmEntityDescriptor descriptor = oxmEntityLookup.getEntityDescriptors().get(entityType); + + if (descriptor == null) { + LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE, + "oxm entity" + " descriptor for entityType = " + entityType); + return null; + } + + List pkeyNames = descriptor.getPrimaryKeyAttributeNames(); + + if (pkeyNames == null || pkeyNames.size() == 0) { + LOG.error(AaiUiMsgs.FAILED_TO_DETERMINE, + "node primary" + " key because descriptor primary key names is empty"); + return null; + } + + return NodeUtils.concatArray(pkeyNames, "/"); + + } + +} diff --git a/src/main/java/org/onap/aai/sparky/viewandinspect/services/BaseVisualizationService.java b/src/main/java/org/onap/aai/sparky/viewandinspect/services/BaseVisualizationService.java index ed5ec8d..9c3c7da 100644 --- a/src/main/java/org/onap/aai/sparky/viewandinspect/services/BaseVisualizationService.java +++ b/src/main/java/org/onap/aai/sparky/viewandinspect/services/BaseVisualizationService.java @@ -37,6 +37,7 @@ import org.onap.aai.sparky.config.oxm.OxmEntityLookup; import org.onap.aai.sparky.config.oxm.OxmModelLoader; import org.onap.aai.sparky.dal.ActiveInventoryAdapter; import org.onap.aai.sparky.dal.ElasticSearchAdapter; +import org.onap.aai.sparky.dal.GizmoAdapter; import org.onap.aai.sparky.logging.AaiUiMsgs; import org.onap.aai.sparky.subscription.config.SubscriptionConfig; import org.onap.aai.sparky.sync.config.ElasticSearchEndpointConfig; @@ -64,6 +65,7 @@ public class BaseVisualizationService implements VisualizationService { private ObjectMapper mapper = new ObjectMapper(); private final ActiveInventoryAdapter aaiAdapter; + private final GizmoAdapter gizmoAdapter; private final ElasticSearchAdapter esAdapter; private final ExecutorService aaiExecutorService; @@ -76,16 +78,18 @@ public class BaseVisualizationService implements VisualizationService { private ElasticSearchSchemaConfig schemaEConfig; private OxmEntityLookup oxmEntityLookup; - public BaseVisualizationService(OxmModelLoader loader, VisualizationConfigs visualizationConfigs, - ActiveInventoryAdapter aaiAdapter,ElasticSearchAdapter esAdapter, - ElasticSearchEndpointConfig endpointConfig, ElasticSearchSchemaConfig schemaConfig, int numActiveInventoryWorkers, - OxmEntityLookup oxmEntityLookup, SubscriptionConfig subscriptionConfig) throws Exception { + public BaseVisualizationService(OxmModelLoader loader, VisualizationConfigs visualizationConfigs, + ActiveInventoryAdapter aaiAdapter, GizmoAdapter gizmoAdapter, ElasticSearchAdapter esAdapter, + ElasticSearchEndpointConfig endpointConfig, ElasticSearchSchemaConfig schemaConfig, + int numActiveInventoryWorkers, OxmEntityLookup oxmEntityLookup, SubscriptionConfig subscriptionConfig) + throws Exception { this.visualizationConfigs = visualizationConfigs; this.endpointEConfig = endpointConfig; this.schemaEConfig = schemaConfig; this.oxmEntityLookup = oxmEntityLookup; this.subConfig = subscriptionConfig; + secureRandom = new SecureRandom(); @@ -94,6 +98,7 @@ public class BaseVisualizationService implements VisualizationService { */ this.aaiAdapter = aaiAdapter; + this.gizmoAdapter = gizmoAdapter; this.esAdapter = esAdapter; this.mapper = new ObjectMapper(); @@ -276,8 +281,14 @@ public class BaseVisualizationService implements VisualizationService { VisualizationContext visContext = null; long contextId = secureRandom.nextLong(); try { - visContext = new BaseVisualizationContext(contextId, this.aaiAdapter, aaiExecutorService, - this.visualizationConfigs, oxmEntityLookup); + if ( visualizationConfigs.isGizmoEnabled()) { + visContext = new BaseGizmoVisualizationContext(contextId, this.gizmoAdapter, aaiExecutorService, + this.visualizationConfigs, oxmEntityLookup); + } else { + visContext = new BaseVisualizationContext(contextId, this.aaiAdapter, aaiExecutorService, + this.visualizationConfigs, oxmEntityLookup); + } + contextMap.putIfAbsent(contextId, visContext); } catch (Exception e1) { LOG.error(AaiUiMsgs.EXCEPTION_CAUGHT, diff --git a/src/main/java/org/onap/aai/sparky/viewandinspect/task/PerformGizmoNodeSelfLinkProcessingTask.java b/src/main/java/org/onap/aai/sparky/viewandinspect/task/PerformGizmoNodeSelfLinkProcessingTask.java new file mode 100644 index 0000000..bccd7aa --- /dev/null +++ b/src/main/java/org/onap/aai/sparky/viewandinspect/task/PerformGizmoNodeSelfLinkProcessingTask.java @@ -0,0 +1,126 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017 Amdocs + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.sparky.viewandinspect.task; + +import java.util.Map; +import java.util.function.Supplier; + +import org.onap.aai.cl.api.Logger; +import org.onap.aai.cl.eelf.LoggerFactory; +import org.onap.aai.restclient.client.OperationResult; +import org.onap.aai.sparky.dal.GizmoAdapter; +import org.onap.aai.sparky.logging.AaiUiMsgs; +import org.onap.aai.sparky.viewandinspect.entity.NodeProcessingTransaction; +import org.slf4j.MDC; + +/** + * The Class PerformNodeSelfLinkProcessingTask. + */ +public class PerformGizmoNodeSelfLinkProcessingTask implements Supplier { + + private static final Logger logger = + LoggerFactory.getInstance().getLogger(PerformGizmoNodeSelfLinkProcessingTask.class); + + private NodeProcessingTransaction txn; + private GizmoAdapter gizmoAdapter; + private Map contextMap; + + /** + * Instantiates a new perform node self link processing task. + * + * @param txn the txn + * @param aaiProvider the aai provider + * @param aaiConfig the aai config + */ + /** + * + * @param txn + * @param requestParameters + * @param aaiProvider + * @param aaiConfig + */ + public PerformGizmoNodeSelfLinkProcessingTask(NodeProcessingTransaction txn, String requestParameters, + GizmoAdapter gizmoAdapter) { + this.gizmoAdapter = gizmoAdapter; + this.txn = txn; + this.contextMap = MDC.getCopyOfContextMap(); + } + + /* + * (non-Javadoc) + * + * @see java.util.function.Supplier#get() + */ + @Override + public NodeProcessingTransaction get() { + MDC.setContextMap(contextMap); + OperationResult opResult = new OperationResult(); + String link = txn.getSelfLink(); + + if (link == null) { + opResult.setResult(500, "Aborting self-link processing because self link is null"); + txn.setOpResult(opResult); + return txn; + } + + /** + * Rebuild the self link: + * + *
  • build the base url with the configured scheme + authority (server:port) + *
  • recombine baseUrl + originalEncodedLink + queryStringParameters + * + */ + + final String urlSchemeAndAuthority = gizmoAdapter.repairInventorySelfLink("", null); + + String parameters = txn.getRequestParameters(); + link = urlSchemeAndAuthority + link; + + if (parameters != null) { + link += parameters; + } + + if (logger.isDebugEnabled()) { + logger.debug(AaiUiMsgs.DEBUG_GENERIC, "Collecting " + link); + } + + try { + + opResult = gizmoAdapter.queryGizmoWithRetries(link, "application/json", + gizmoAdapter.getEndpointConfig().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/test/java/org/onap/aai/sparky/config/oxm/OxmEntityContainerLookup.java b/src/test/java/org/onap/aai/sparky/config/oxm/OxmEntityContainerLookup.java new file mode 100644 index 0000000..7d55e4d --- /dev/null +++ b/src/test/java/org/onap/aai/sparky/config/oxm/OxmEntityContainerLookup.java @@ -0,0 +1,99 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017 Amdocs + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.sparky.config.oxm; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.eclipse.persistence.dynamic.DynamicType; +import org.eclipse.persistence.internal.oxm.mappings.Descriptor; +import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext; + +public class OxmEntityContainerLookup implements OxmModelProcessor { + + private Collection searchableEntityGroups; + private Collection entityContainers; + + public OxmEntityContainerLookup() { + searchableEntityGroups = new ArrayList(); + entityContainers = new ArrayList(); + } + + @Override + public void processOxmModel(DynamicJAXBContext jaxbContext) { + + @SuppressWarnings("rawtypes") + List descriptorsList = jaxbContext.getXMLContext().getDescriptors(); + + for (@SuppressWarnings("rawtypes") + Descriptor desc : descriptorsList) { + + DynamicType entity = jaxbContext.getDynamicType(desc.getAlias()); + + @SuppressWarnings("unchecked") + Map properties = entity.getDescriptor().getProperties(); + + if (properties != null) { + + String container = properties.get("container"); + + if (container != null && !entityContainers.contains(container)) { + + entityContainers.add(container); + + if (properties.containsKey("searchable")) { + if (!searchableEntityGroups.contains(container)) { + searchableEntityGroups.add(container); + } + } + } + + } + + } + + } + + public Collection getSearchableEntityGroups() { + return searchableEntityGroups; + } + + public void setSearchableEntityGroups(Collection searchableEntityGroups) { + this.searchableEntityGroups = searchableEntityGroups; + } + + public Collection getEntityContainers() { + return entityContainers; + } + + public void setEntityContainers(Collection entityContainers) { + this.entityContainers = entityContainers; + } + + public boolean isEntityContainer(String entityType) { + return entityContainers.contains(entityType); + } + +} diff --git a/src/test/java/org/onap/aai/sparky/synchronizer/GizmoEntitySummarizer.java b/src/test/java/org/onap/aai/sparky/synchronizer/GizmoEntitySummarizer.java new file mode 100644 index 0000000..5ea5280 --- /dev/null +++ b/src/test/java/org/onap/aai/sparky/synchronizer/GizmoEntitySummarizer.java @@ -0,0 +1,251 @@ +package org.onap.aai.sparky.synchronizer; + +import static java.util.concurrent.CompletableFuture.supplyAsync; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.function.Supplier; + +import org.onap.aai.cl.api.Logger; +import org.onap.aai.cl.eelf.LoggerFactory; +import org.onap.aai.restclient.client.OperationResult; +import org.onap.aai.restclient.enums.RestAuthenticationMode; +import org.onap.aai.sparky.config.oxm.OxmModelLoader; +import org.onap.aai.sparky.dal.GizmoAdapter; +import org.onap.aai.sparky.dal.exception.ElasticSearchOperationException; +import org.onap.aai.sparky.dal.rest.RestClientConstructionException; +import org.onap.aai.sparky.dal.rest.config.RestEndpointConfig; +import org.onap.aai.sparky.logging.AaiUiMsgs; +import org.onap.aai.sparky.util.NodeUtils; +import org.onap.aai.sparky.util.OxmModelAndProcessorHelper; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; + +public class GizmoEntitySummarizer { + + protected ObjectMapper mapper; + protected OxmModelLoader oxmModelLoader; + private static final Logger logger = LoggerFactory.getInstance().getLogger(GizmoEntitySummarizer.class); + protected ExecutorService gizmoExecutor; + protected GizmoAdapter gizmoAdapter; + protected OxmModelAndProcessorHelper oxmHelper; + + /* + * We need to add another concept to the OxmModelLoader which is to generate + * a list of entity containers from the OXM JaxbContext + */ + + public GizmoEntitySummarizer() + throws ElasticSearchOperationException, IOException, RestClientConstructionException { + + OxmModelAndProcessorHelper.API_VERSION_OVERRIDE = 11; + + this.gizmoExecutor = NodeUtils.createNamedExecutor("GIZMO-WORKER", 5, logger); + + oxmHelper = OxmModelAndProcessorHelper.getInstance(); + this.oxmModelLoader = oxmHelper.getModelLoader(); + + this.mapper = new ObjectMapper(); + + RestEndpointConfig gizmoConfig = new RestEndpointConfig(); + + gizmoConfig.setEndpointIpAddress("10.147.138.153"); + gizmoConfig.setEndpointServerPort("9520"); + gizmoConfig.setNumRequestRetries(5); + gizmoConfig.setRestAuthenticationMode(RestAuthenticationMode.SSL_CERT); + gizmoConfig.setConnectTimeoutInMs(60000); + gizmoConfig.setReadTimeoutInMs(30000); + gizmoConfig.setCertFileName("client-cert-onap.p12"); + gizmoConfig.setCertPassword("OBF:1y0q1uvc1uum1uvg1pil1pjl1uuq1uvk1uuu1y10"); + gizmoConfig.setTruststoreFileName("synchronizer.jks"); + gizmoConfig.setValidateServerCertChain(false); + gizmoConfig.setValidateServerHostname(false); + + gizmoAdapter = new GizmoAdapter(oxmModelLoader, gizmoConfig); + + gizmoAdapter.setInventoryBasePath("/services/inventory/v12/"); + gizmoAdapter.setRelationshipsBasePath("/services/inventory/relationships/v12/"); + + } + + private Map getNumEntitiesPerType() { + + Collection containerTypes = oxmHelper.getOxmEntityContainerLookup().getEntityContainers(); + Collection links = new ArrayList(); + Map entityTypeCounts = new TreeMap(); + + final CountDownLatch latch = new CountDownLatch(containerTypes.size()); + + for (String entityType : containerTypes) { + + supplyAsync(new Supplier() { + + @Override + public Void get() { + + OperationResult typeLinksResult = null; + try { + typeLinksResult = gizmoAdapter.queryGizmoWithRetries( + gizmoAdapter.getFullInventoryUrl(entityType), "application/json", 1); + + if (typeLinksResult != null) { + + if (typeLinksResult.wasSuccessful() && typeLinksResult.getResult() != null) { + + JsonNode rootNode = mapper.readValue(typeLinksResult.getResult(), JsonNode.class); + + if (rootNode.isArray()) { + ArrayNode arrayNode = (ArrayNode) rootNode; + entityTypeCounts.put(entityType, new Integer(arrayNode.size())); + } else { + entityTypeCounts.put(entityType, new Integer(-1)); + } + + } else { + // -1 + entityTypeCounts.put(entityType, new Integer(-1)); + } + + } + + } catch (Exception exc) { + entityTypeCounts.put(entityType, new Integer(-1)); + } + + return null; + } + + }, gizmoExecutor).whenComplete((result, error) -> { + + latch.countDown(); + + if (error != null) { + logger.error(AaiUiMsgs.ERROR_GENERIC, + "An error occurred getting data from AAI. Error = " + error.getMessage()); + } + + }); + + } + + // System.out.println("self links size = " + selflinks.size()); + + try { + latch.await(); + } catch (InterruptedException e) { + + } + + return entityTypeCounts; + } + + private Map getNumRelationshipsPerType() { + + Map entityTypeCounts = new TreeMap(); + + final CountDownLatch latch = new CountDownLatch(1); + + supplyAsync(new Supplier() { + + @Override + public Void get() { + + OperationResult typeLinksResult = null; + try { + typeLinksResult = gizmoAdapter.queryGizmoWithRetries(gizmoAdapter.getFullRelationshipUrl("has"), + "application/json", 1); + + if (typeLinksResult != null) { + + if (typeLinksResult.wasSuccessful() && typeLinksResult.getResult() != null) { + + JsonNode rootNode = mapper.readValue(typeLinksResult.getResult(), JsonNode.class); + + if (rootNode.isArray()) { + ArrayNode arrayNode = (ArrayNode) rootNode; + entityTypeCounts.put("has", new Integer(arrayNode.size())); + } else { + entityTypeCounts.put("has", new Integer(-1)); + } + + } else { + // -1 + entityTypeCounts.put("has", new Integer(-1)); + } + + } else { + entityTypeCounts.put("has", new Integer(-1)); + } + + } catch (Exception exc) { + entityTypeCounts.put("has", new Integer(-1)); + } + + return null; + } + + }, gizmoExecutor).whenComplete((result, error) -> { + + latch.countDown(); + + if (error != null) { + logger.error(AaiUiMsgs.ERROR_GENERIC, + "An error occurred getting data from AAI. Error = " + error.getMessage()); + } + + }); + + // System.out.println("self links size = " + selflinks.size()); + + try { + latch.await(); + } catch (InterruptedException e) { + + } + + return entityTypeCounts; + } + + public void shutdown() { + this.gizmoExecutor.shutdown(); + } + + public static void main(String[] args) + throws ElasticSearchOperationException, IOException, RestClientConstructionException { + + System.setProperty("CONFIG_HOME", "X:\\2018_dev\\OSEAAI\\gizmo_integration\\onap_sparky-be\\appconfig-local\\"); + GizmoEntitySummarizer gizmoSummarizer = new GizmoEntitySummarizer(); + + Map entityCounts = gizmoSummarizer.getNumEntitiesPerType(); + Map relationshipCounts = gizmoSummarizer.getNumRelationshipsPerType(); + gizmoSummarizer.shutdown(); + + System.out.println("Gizmo Entities:"); + + for (Entry entry : entityCounts.entrySet()) { + String key = entry.getKey(); + Integer value = entry.getValue(); + + System.out.printf("\t%s : %d\n", key, value); + } + + System.out.println("\nGizmo Relationships:"); + + for (Entry entry : relationshipCounts.entrySet()) { + String key = entry.getKey(); + Integer value = entry.getValue(); + + System.out.printf("\t%s : %d\n", key, value); + } + + } + +} \ No newline at end of file diff --git a/src/test/java/org/onap/aai/sparky/util/OxmModelAndProcessorHelper.java b/src/test/java/org/onap/aai/sparky/util/OxmModelAndProcessorHelper.java index bf5df76..48c8290 100644 --- a/src/test/java/org/onap/aai/sparky/util/OxmModelAndProcessorHelper.java +++ b/src/test/java/org/onap/aai/sparky/util/OxmModelAndProcessorHelper.java @@ -5,6 +5,7 @@ import java.util.Set; import org.onap.aai.sparky.config.oxm.CrossEntityReferenceLookup; import org.onap.aai.sparky.config.oxm.GeoEntityLookup; +import org.onap.aai.sparky.config.oxm.OxmEntityContainerLookup; import org.onap.aai.sparky.config.oxm.OxmEntityLookup; import org.onap.aai.sparky.config.oxm.OxmModelLoader; import org.onap.aai.sparky.config.oxm.OxmModelProcessor; @@ -13,109 +14,123 @@ import org.onap.aai.sparky.config.oxm.SuggestionEntityLookup; import org.onap.aai.sparky.search.filters.config.FiltersConfig; public class OxmModelAndProcessorHelper { - - private static final int API_VERSION_OVERRIDE = -1; - - private OxmModelLoader modelLoader; - private Set processors; - - private CrossEntityReferenceLookup crossEntityReferenceLookup; - private GeoEntityLookup geoEntityLookup; - private OxmEntityLookup oxmEntityLookup; - private SearchableEntityLookup searchableEntityLookup; - private SuggestionEntityLookup suggestionEntityLookup; - private FiltersConfig filtersConfig; - - private static OxmModelAndProcessorHelper instance = null; - private OxmModelAndProcessorHelper() { - - this.filtersConfig = new FiltersConfig(SparkyTestConstants.FILTERS_JSON_FILE, SparkyTestConstants.VIEWS_JSON_FILE); - - this.crossEntityReferenceLookup = new CrossEntityReferenceLookup(); - this.geoEntityLookup = new GeoEntityLookup(); - this.oxmEntityLookup = new OxmEntityLookup(); - this.searchableEntityLookup = new SearchableEntityLookup(); - this.suggestionEntityLookup = new SuggestionEntityLookup(filtersConfig); - - this.processors = new HashSet(); - processors.add(crossEntityReferenceLookup); - processors.add(geoEntityLookup); - processors.add(oxmEntityLookup); - processors.add(searchableEntityLookup); - processors.add(suggestionEntityLookup); - - this.modelLoader = new OxmModelLoader(API_VERSION_OVERRIDE, processors); - modelLoader.loadLatestOxmModel(); - } - - public static OxmModelAndProcessorHelper getInstance() { - if (instance == null) { - instance = new OxmModelAndProcessorHelper(); - } - return instance; - } - - public OxmModelLoader getModelLoader() { - return modelLoader; - } - - public void setModelLoader(OxmModelLoader modelLoader) { - this.modelLoader = modelLoader; - } - - public Set getProcessors() { - return processors; - } - - public void setProcessors(Set processors) { - this.processors = processors; - } - - public CrossEntityReferenceLookup getCrossEntityReferenceLookup() { - return crossEntityReferenceLookup; - } - - public void setCrossEntityReferenceLookup(CrossEntityReferenceLookup crossEntityReferenceLookup) { - this.crossEntityReferenceLookup = crossEntityReferenceLookup; - } - - public GeoEntityLookup getGeoEntityLookup() { - return geoEntityLookup; - } - - public void setGeoEntityLookup(GeoEntityLookup geoEntityLookup) { - this.geoEntityLookup = geoEntityLookup; - } - - public OxmEntityLookup getOxmEntityLookup() { - return oxmEntityLookup; - } - - public void setOxmEntityLookup(OxmEntityLookup oxmEntityLookup) { - this.oxmEntityLookup = oxmEntityLookup; - } - - public SearchableEntityLookup getSearchableEntityLookup() { - return searchableEntityLookup; - } - - public void setSearchableEntityLookup(SearchableEntityLookup searchableEntityLookup) { - this.searchableEntityLookup = searchableEntityLookup; - } - - public SuggestionEntityLookup getSuggestionEntityLookup() { - return suggestionEntityLookup; - } - - public void setSuggestionEntityLookup(SuggestionEntityLookup suggestionEntityLookup) { - this.suggestionEntityLookup = suggestionEntityLookup; - } - - public FiltersConfig getFiltersConfig() { - return filtersConfig; - } - - public void setFiltersConfig(FiltersConfig filtersConfig) { - this.filtersConfig = filtersConfig; - } + + public static int API_VERSION_OVERRIDE = -1; + + private OxmModelLoader modelLoader; + private Set processors; + + private CrossEntityReferenceLookup crossEntityReferenceLookup; + private GeoEntityLookup geoEntityLookup; + private OxmEntityLookup oxmEntityLookup; + private SearchableEntityLookup searchableEntityLookup; + private SuggestionEntityLookup suggestionEntityLookup; + private OxmEntityContainerLookup oxmEntityContainerLookup; + private FiltersConfig filtersConfig; + + private static OxmModelAndProcessorHelper instance = null; + + private OxmModelAndProcessorHelper() { + + this.filtersConfig = new FiltersConfig(SparkyTestConstants.FILTERS_JSON_FILE, + SparkyTestConstants.VIEWS_JSON_FILE); + + this.crossEntityReferenceLookup = new CrossEntityReferenceLookup(); + this.geoEntityLookup = new GeoEntityLookup(); + this.oxmEntityLookup = new OxmEntityLookup(); + this.searchableEntityLookup = new SearchableEntityLookup(); + this.suggestionEntityLookup = new SuggestionEntityLookup(filtersConfig); + this.oxmEntityContainerLookup = new OxmEntityContainerLookup(); + + this.processors = new HashSet(); + processors.add(crossEntityReferenceLookup); + processors.add(geoEntityLookup); + processors.add(oxmEntityLookup); + processors.add(searchableEntityLookup); + processors.add(suggestionEntityLookup); + processors.add(oxmEntityContainerLookup); + + this.modelLoader = new OxmModelLoader(API_VERSION_OVERRIDE, processors); + modelLoader.loadLatestOxmModel(); + } + + public static OxmModelAndProcessorHelper getInstance() { + if (instance == null) { + instance = new OxmModelAndProcessorHelper(); + } + return instance; + } + + public OxmModelLoader getModelLoader() { + return modelLoader; + } + + public void setModelLoader(OxmModelLoader modelLoader) { + this.modelLoader = modelLoader; + } + + public Set getProcessors() { + return processors; + } + + public void setProcessors(Set processors) { + this.processors = processors; + } + + public CrossEntityReferenceLookup getCrossEntityReferenceLookup() { + return crossEntityReferenceLookup; + } + + public void setCrossEntityReferenceLookup(CrossEntityReferenceLookup crossEntityReferenceLookup) { + this.crossEntityReferenceLookup = crossEntityReferenceLookup; + } + + public GeoEntityLookup getGeoEntityLookup() { + return geoEntityLookup; + } + + public void setGeoEntityLookup(GeoEntityLookup geoEntityLookup) { + this.geoEntityLookup = geoEntityLookup; + } + + public OxmEntityLookup getOxmEntityLookup() { + return oxmEntityLookup; + } + + public void setOxmEntityLookup(OxmEntityLookup oxmEntityLookup) { + this.oxmEntityLookup = oxmEntityLookup; + } + + public SearchableEntityLookup getSearchableEntityLookup() { + return searchableEntityLookup; + } + + public void setSearchableEntityLookup(SearchableEntityLookup searchableEntityLookup) { + this.searchableEntityLookup = searchableEntityLookup; + } + + public SuggestionEntityLookup getSuggestionEntityLookup() { + return suggestionEntityLookup; + } + + public void setSuggestionEntityLookup(SuggestionEntityLookup suggestionEntityLookup) { + this.suggestionEntityLookup = suggestionEntityLookup; + } + + public FiltersConfig getFiltersConfig() { + return filtersConfig; + } + + public void setFiltersConfig(FiltersConfig filtersConfig) { + this.filtersConfig = filtersConfig; + } + + public OxmEntityContainerLookup getOxmEntityContainerLookup() { + return oxmEntityContainerLookup; + } + + public void setOxmEntityContainerLookup(OxmEntityContainerLookup oxmEntityContainerLookup) { + this.oxmEntityContainerLookup = oxmEntityContainerLookup; + } + } diff --git a/src/test/java/org/onap/aai/sparky/viewandinspect/BaseVisualizationServiceTest.java b/src/test/java/org/onap/aai/sparky/viewandinspect/BaseVisualizationServiceTest.java index bc1a80d..e51c629 100644 --- a/src/test/java/org/onap/aai/sparky/viewandinspect/BaseVisualizationServiceTest.java +++ b/src/test/java/org/onap/aai/sparky/viewandinspect/BaseVisualizationServiceTest.java @@ -1,6 +1,6 @@ package org.onap.aai.sparky.viewandinspect; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; import org.junit.Before; import org.junit.Test; @@ -9,6 +9,7 @@ import org.onap.aai.sparky.config.oxm.OxmEntityLookup; import org.onap.aai.sparky.config.oxm.OxmModelLoader; import org.onap.aai.sparky.dal.ActiveInventoryAdapter; import org.onap.aai.sparky.dal.ElasticSearchAdapter; +import org.onap.aai.sparky.dal.GizmoAdapter; import org.onap.aai.sparky.subscription.config.SubscriptionConfig; import org.onap.aai.sparky.sync.config.ElasticSearchEndpointConfig; import org.onap.aai.sparky.sync.config.ElasticSearchSchemaConfig; @@ -26,13 +27,16 @@ public class BaseVisualizationServiceTest { private ElasticSearchEndpointConfig endpointEConfig; private ElasticSearchSchemaConfig schemaEConfig; private OxmEntityLookup oxmEntityLookup; + private GizmoAdapter mockGizmoAdapter; private BaseVisualizationService baseVisService; @Before public void init() throws Exception { + this.mockAaiAdapter = Mockito.mock(ActiveInventoryAdapter.class); this.mockAaiAdapter = Mockito.mock(ActiveInventoryAdapter.class); this.mockEsAdapter = Mockito.mock(ElasticSearchAdapter.class); + this.mockGizmoAdapter = Mockito.mock(GizmoAdapter.class); this.visualizationConfigs = new VisualizationConfigs(); this.subConfig = new SubscriptionConfig(); this.endpointEConfig = new ElasticSearchEndpointConfig(); @@ -41,8 +45,9 @@ public class BaseVisualizationServiceTest { OxmModelLoader modelLoader = OxmModelAndProcessorHelper.getInstance().getModelLoader(); - this.baseVisService = new BaseVisualizationService(modelLoader, visualizationConfigs, mockAaiAdapter, - mockEsAdapter, endpointEConfig, schemaEConfig, 1, oxmEntityLookup, subConfig); + this.baseVisService = new BaseVisualizationService(modelLoader, visualizationConfigs, + mockAaiAdapter, mockGizmoAdapter, mockEsAdapter, endpointEConfig, schemaEConfig, 1, + oxmEntityLookup, subConfig); } @Test diff --git a/src/test/java/org/onap/aai/sparky/viewandinspect/sync/ViewInspectGizmoEntitySynchronizer.java b/src/test/java/org/onap/aai/sparky/viewandinspect/sync/ViewInspectGizmoEntitySynchronizer.java new file mode 100644 index 0000000..6d63a8a --- /dev/null +++ b/src/test/java/org/onap/aai/sparky/viewandinspect/sync/ViewInspectGizmoEntitySynchronizer.java @@ -0,0 +1,792 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017 Amdocs + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.sparky.viewandinspect.sync; + +import static java.util.concurrent.CompletableFuture.supplyAsync; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Deque; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.ExecutorService; +import java.util.function.Supplier; + +import org.onap.aai.cl.api.Logger; +import org.onap.aai.cl.eelf.LoggerFactory; +import org.onap.aai.cl.mdc.MdcContext; +import org.onap.aai.restclient.client.OperationResult; +import org.onap.aai.sparky.config.oxm.OxmEntityContainerLookup; +import org.onap.aai.sparky.config.oxm.OxmEntityDescriptor; +import org.onap.aai.sparky.config.oxm.OxmEntityLookup; +import org.onap.aai.sparky.config.oxm.SearchableEntityLookup; +import org.onap.aai.sparky.config.oxm.SearchableOxmEntityDescriptor; +import org.onap.aai.sparky.dal.GizmoAdapter; +import org.onap.aai.sparky.dal.NetworkTransaction; +import org.onap.aai.sparky.dal.rest.HttpMethod; +import org.onap.aai.sparky.logging.AaiUiMsgs; +import org.onap.aai.sparky.sync.AbstractEntitySynchronizer; +import org.onap.aai.sparky.sync.IndexSynchronizer; +import org.onap.aai.sparky.sync.config.ElasticSearchSchemaConfig; +import org.onap.aai.sparky.sync.config.NetworkStatisticsConfig; +import org.onap.aai.sparky.sync.entity.MergableEntity; +import org.onap.aai.sparky.sync.entity.SearchableEntity; +import org.onap.aai.sparky.sync.entity.SelfLinkDescriptor; +import org.onap.aai.sparky.sync.enumeration.OperationState; +import org.onap.aai.sparky.sync.enumeration.SynchronizerState; +import org.onap.aai.sparky.sync.task.PerformElasticSearchPut; +import org.onap.aai.sparky.sync.task.PerformElasticSearchRetrieval; +import org.onap.aai.sparky.sync.task.PerformElasticSearchUpdate; +import org.onap.aai.sparky.sync.task.PerformGizmoRetrieval; +import org.onap.aai.sparky.util.NodeUtils; +import org.slf4j.MDC; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.databind.node.ArrayNode; + +/** + * The Class SearchableEntitySynchronizer. + */ +public class ViewInspectGizmoEntitySynchronizer extends AbstractEntitySynchronizer + implements IndexSynchronizer { + + /** + * The Class RetrySearchableEntitySyncContainer. + */ + private class RetrySearchableEntitySyncContainer { + NetworkTransaction txn; + SearchableEntity se; + + /** + * Instantiates a new retry searchable entity sync container. + * + * @param txn the txn + * @param se the se + */ + public RetrySearchableEntitySyncContainer(NetworkTransaction txn, SearchableEntity se) { + this.txn = txn; + this.se = se; + } + + public NetworkTransaction getNetworkTransaction() { + return txn; + } + + public SearchableEntity getSearchableEntity() { + return se; + } + } + + private static final Logger LOG = + LoggerFactory.getInstance().getLogger(ViewInspectGizmoEntitySynchronizer.class); + + private boolean allWorkEnumerated; + private Deque selflinks; + private Deque retryQueue; + private Map retryLimitTracker; + protected ExecutorService esPutExecutor; + private OxmEntityLookup oxmEntityLookup; + private SearchableEntityLookup searchableEntityLookup; + private GizmoAdapter gizmoAdapter; + private OxmEntityContainerLookup entityContainerLookup; + + /** + * Instantiates a new searchable entity synchronizer. + * + * @param indexName the index name + * @throws Exception the exception + */ + public ViewInspectGizmoEntitySynchronizer(ElasticSearchSchemaConfig schemaConfig, + int internalSyncWorkers, int gizmoWorkers, int esWorkers, NetworkStatisticsConfig aaiStatConfig, + NetworkStatisticsConfig esStatConfig, OxmEntityLookup oxmEntityLookup, + SearchableEntityLookup searchableEntityLookup, OxmEntityContainerLookup entityContainerLookup) throws Exception { + super(LOG, "SES", internalSyncWorkers, gizmoWorkers, esWorkers, schemaConfig.getIndexName(), + aaiStatConfig, esStatConfig); + + this.oxmEntityLookup = oxmEntityLookup; + this.searchableEntityLookup = searchableEntityLookup; + this.entityContainerLookup = entityContainerLookup; + this.allWorkEnumerated = false; + this.selflinks = new ConcurrentLinkedDeque(); + this.retryQueue = new ConcurrentLinkedDeque(); + this.retryLimitTracker = new ConcurrentHashMap(); + this.synchronizerName = "Searchable Entity Synchronizer"; + this.esPutExecutor = NodeUtils.createNamedExecutor("SES-ES-PUT", 5, LOG); + this.aaiEntityStats.intializeEntityCounters( + searchableEntityLookup.getSearchableEntityDescriptors().keySet()); + this.esEntityStats.intializeEntityCounters( + searchableEntityLookup.getSearchableEntityDescriptors().keySet()); + this.syncDurationInMs = -1; + } + + + + public GizmoAdapter getGizmoAdapter() { + return gizmoAdapter; +} + + + +public void setGizmoAdapter(GizmoAdapter gizmoAdapter) { + this.gizmoAdapter = gizmoAdapter; +} + + + +/** + * Collect all the work. + * + * @return the operation state + */ + private OperationState collectAllTheWork() { + final Map contextMap = MDC.getCopyOfContextMap(); + + Collection searchableEntityGroups = entityContainerLookup.getSearchableEntityGroups(); + + if (searchableEntityGroups.isEmpty()) { + LOG.error(AaiUiMsgs.ERROR_LOADING_OXM_SEARCHABLE_ENTITIES); + return OperationState.ERROR; + } + + + try { + + /* + * launch a parallel async thread to process the documents for each entity-type (to max the + * of the configured executor anyway) + */ + + /*searchableEntityGroups = new ArrayList(); + searchableEntityGroups.add("pservers");*/ + + aaiWorkOnHand.set(searchableEntityGroups.size()); + + for (String searchableEntityGroup : searchableEntityGroups) { + + supplyAsync(new Supplier() { + + @Override + public Void get() { + MDC.setContextMap(contextMap); + OperationResult typeLinksResult = null; + try { + typeLinksResult = gizmoAdapter.getSelfLinksByEntityType(searchableEntityGroup); + aaiWorkOnHand.decrementAndGet(); + processEntityTypeSelfLinks(typeLinksResult); + } catch (Exception exc) { + + exc.printStackTrace(); + } + + return null; + } + + }, aaiExecutor).whenComplete((result, error) -> { + + if (error != null) { + LOG.error(AaiUiMsgs.ERROR_GENERIC, + "An error occurred getting data from AAI. Error = " + error.getMessage()); + } + }); + + } + + while (aaiWorkOnHand.get() != 0) { + + if (LOG.isDebugEnabled()) { + LOG.debug(AaiUiMsgs.WAIT_FOR_ALL_SELFLINKS_TO_BE_COLLECTED); + } + + Thread.sleep(1000); + } + + aaiWorkOnHand.set(selflinks.size()); + allWorkEnumerated = true; + syncEntityTypes(); + + while (!isSyncDone()) { + performRetrySync(); + Thread.sleep(1000); + } + + /* + * Make sure we don't hang on to retries that failed which could cause issues during future + * syncs + */ + retryLimitTracker.clear(); + + } catch (Exception exc) { + // TODO -> LOG, waht should be logged here? + } + + return OperationState.OK; + } + + /* (non-Javadoc) + * @see org.openecomp.sparky.synchronizer.IndexSynchronizer#doSync() + */ + @Override + public OperationState doSync() { + this.syncDurationInMs = -1; + String txnID = NodeUtils.getRandomTxnId(); + MdcContext.initialize(txnID, "SearchableEntitySynchronizer", "", "Sync", ""); + + resetCounters(); + this.allWorkEnumerated = false; + syncStartedTimeStampInMs = System.currentTimeMillis(); + collectAllTheWork(); + + return OperationState.OK; + } + + /** + * Process entity type self links. + * + * @param operationResult the operation result + */ + private void processEntityTypeSelfLinks(OperationResult operationResult) { + + JsonNode rootNode = null; + + final String jsonResult = operationResult.getResult(); + + if (jsonResult != null && jsonResult.length() > 0 && operationResult.wasSuccessful()) { + + try { + rootNode = mapper.readTree(jsonResult); + } catch (IOException exc) { + String message = "Could not deserialize JSON (representing operation result) as node tree. " + + "Operation result = " + jsonResult + ". " + exc.getLocalizedMessage(); + LOG.error(AaiUiMsgs.JSON_PROCESSING_ERROR, message); + } + + ArrayNode resultDataArrayNode = null; + + if (rootNode.isArray()) { + resultDataArrayNode = (ArrayNode) rootNode; + + Iterator elementIterator = resultDataArrayNode.elements(); + JsonNode element = null; + + while (elementIterator.hasNext()) { + element = elementIterator.next(); + + final String id = NodeUtils.getNodeFieldAsText(element, "id"); + final String type = NodeUtils.getNodeFieldAsText(element, "type"); + final String url = NodeUtils.getNodeFieldAsText(element, "url"); + + String resourceLink; + try { + resourceLink = gizmoAdapter.getFullInventoryUrl(type + "/" + id); + selflinks.add(new SelfLinkDescriptor(NodeUtils.extractRawGizmoPathWithoutVersion(resourceLink), null, type)); + } catch (Exception e) { + LOG.error(AaiUiMsgs.ERROR_GENERIC, "ERROR: Failed to determine resource link caused by " + e.getMessage()); + } + + } + } + } + + } + + /** + * Sync entity types. + */ + private void syncEntityTypes() { + + while (selflinks.peek() != null) { + + SelfLinkDescriptor linkDescriptor = selflinks.poll(); + aaiWorkOnHand.decrementAndGet(); + + OxmEntityDescriptor descriptor = null; + + if (linkDescriptor.getSelfLink() != null && linkDescriptor.getEntityType() != null) { + + descriptor = oxmEntityLookup.getEntityDescriptors().get(linkDescriptor.getEntityType()); + + if (descriptor == null) { + LOG.error(AaiUiMsgs.MISSING_ENTITY_DESCRIPTOR, linkDescriptor.getEntityType()); + continue; + } + + NetworkTransaction txn = new NetworkTransaction(); + txn.setDescriptor(descriptor); + txn.setLink(linkDescriptor.getSelfLink()); + txn.setOperationType(HttpMethod.GET); + txn.setEntityType(linkDescriptor.getEntityType()); + + aaiWorkOnHand.incrementAndGet(); + + supplyAsync(new PerformGizmoRetrieval(txn, gizmoAdapter), aaiExecutor) + .whenComplete((result, error) -> { + + aaiWorkOnHand.decrementAndGet(); + + if (error != null) { + LOG.error(AaiUiMsgs.AAI_RETRIEVAL_FAILED_GENERIC, error.getLocalizedMessage()); + } else { + if (result == null) { + LOG.error(AaiUiMsgs.AAI_RETRIEVAL_FAILED_FOR_SELF_LINK, + linkDescriptor.getSelfLink()); + } else { + updateActiveInventoryCounters(result); + fetchDocumentForUpsert(result); + } + } + }); + } + + } + + } + + /** + * Perform document upsert. + * + * @param esGetTxn the es get txn + * @param se the se + */ + protected void performDocumentUpsert(NetworkTransaction esGetTxn, SearchableEntity se) { + /** + *

    + *

      + * As part of the response processing we need to do the following: + *
    • 1. Extract the version (if present), it will be the ETAG when we use the + * Search-Abstraction-Service + *
    • 2. Spawn next task which is to do the PUT operation into elastic with or with the version + * tag + *
    • a) if version is null or RC=404, then standard put, no _update with version tag + *
    • b) if version != null, do PUT with _update?version= versionNumber in the URI to elastic + *
    + *

    + */ + String link = null; + try { + link = elasticSearchAdapter.buildElasticSearchGetDocUrl(getIndexName(), se.getId()); + } catch (Exception exc) { + LOG.error(AaiUiMsgs.ES_LINK_UPSERT, exc.getLocalizedMessage()); + return; + } + + String versionNumber = null; + boolean wasEntryDiscovered = false; + if (esGetTxn.getOperationResult().getResultCode() == 404) { + LOG.info(AaiUiMsgs.ES_SIMPLE_PUT, se.getEntityPrimaryKeyValue()); + } else if (esGetTxn.getOperationResult().getResultCode() == 200) { + wasEntryDiscovered = true; + try { + versionNumber = NodeUtils.extractFieldValueFromObject( + NodeUtils.convertJsonStrToJsonNode(esGetTxn.getOperationResult().getResult()), + "_version"); + } catch (IOException exc) { + String message = + "Error extracting version number from response, aborting searchable entity sync of " + + se.getEntityPrimaryKeyValue() + ". Error - " + exc.getLocalizedMessage(); + LOG.error(AaiUiMsgs.ERROR_EXTRACTING_FROM_RESPONSE, message); + return; + } + } else { + /* + * Not being a 200 does not mean a failure. eg 201 is returned for created. TODO -> Should we + * return. + */ + LOG.error(AaiUiMsgs.ES_OPERATION_RETURN_CODE, + String.valueOf(esGetTxn.getOperationResult().getResultCode())); + return; + } + + try { + String jsonPayload = null; + if (wasEntryDiscovered) { + try { + ArrayList sourceObject = new ArrayList(); + NodeUtils.extractObjectsByKey( + NodeUtils.convertJsonStrToJsonNode(esGetTxn.getOperationResult().getResult()), + "_source", sourceObject); + + if (!sourceObject.isEmpty()) { + String responseSource = NodeUtils.convertObjectToJson(sourceObject.get(0), false); + MergableEntity me = mapper.readValue(responseSource, MergableEntity.class); + ObjectReader updater = mapper.readerForUpdating(me); + MergableEntity merged = updater.readValue(NodeUtils.convertObjectToJson(se,false)); + jsonPayload = mapper.writeValueAsString(merged); + } + } catch (IOException exc) { + String message = + "Error extracting source value from response, aborting searchable entity sync of " + + se.getEntityPrimaryKeyValue() + ". Error - " + exc.getLocalizedMessage(); + LOG.error(AaiUiMsgs.ERROR_EXTRACTING_FROM_RESPONSE, message); + return; + } + } else { + jsonPayload = se.getAsJson(); + } + + if (wasEntryDiscovered) { + if (versionNumber != null && jsonPayload != null) { + + String requestPayload = elasticSearchAdapter.buildBulkImportOperationRequest(getIndexName(), + "default", se.getId(), versionNumber, jsonPayload); + + NetworkTransaction transactionTracker = new NetworkTransaction(); + transactionTracker.setEntityType(esGetTxn.getEntityType()); + transactionTracker.setDescriptor(esGetTxn.getDescriptor()); + transactionTracker.setOperationType(HttpMethod.PUT); + + esWorkOnHand.incrementAndGet(); + supplyAsync(new PerformElasticSearchUpdate(elasticSearchAdapter.getBulkUrl(), + requestPayload, elasticSearchAdapter, transactionTracker), esPutExecutor) + .whenComplete((result, error) -> { + + esWorkOnHand.decrementAndGet(); + + if (error != null) { + String message = "Searchable entity sync UPDATE PUT error - " + + error.getLocalizedMessage(); + LOG.error(AaiUiMsgs.ES_SEARCHABLE_ENTITY_SYNC_ERROR, message); + } else { + updateElasticSearchCounters(result); + processStoreDocumentResult(result, esGetTxn, se); + } + }); + } + + } else { + + if (link != null && jsonPayload != null) { + + NetworkTransaction updateElasticTxn = new NetworkTransaction(); + updateElasticTxn.setLink(link); + updateElasticTxn.setEntityType(esGetTxn.getEntityType()); + updateElasticTxn.setDescriptor(esGetTxn.getDescriptor()); + updateElasticTxn.setOperationType(HttpMethod.PUT); + + esWorkOnHand.incrementAndGet(); + supplyAsync(new PerformElasticSearchPut(jsonPayload, updateElasticTxn, elasticSearchAdapter), + esPutExecutor).whenComplete((result, error) -> { + + esWorkOnHand.decrementAndGet(); + + if (error != null) { + String message = + "Searchable entity sync UPDATE PUT error - " + error.getLocalizedMessage(); + LOG.error(AaiUiMsgs.ES_SEARCHABLE_ENTITY_SYNC_ERROR, message); + } else { + updateElasticSearchCounters(result); + processStoreDocumentResult(result, esGetTxn, se); + } + }); + } + } + } catch (Exception exc) { + String message = "Exception caught during searchable entity sync PUT operation. Message - " + + exc.getLocalizedMessage(); + LOG.error(AaiUiMsgs.ES_SEARCHABLE_ENTITY_SYNC_ERROR, message); + } + } + + /** + * Populate searchable entity document. + * + * @param doc the doc + * @param result the result + * @param resultDescriptor the result descriptor + * @throws JsonProcessingException the json processing exception + * @throws IOException Signals that an I/O exception has occurred. + */ + protected void populateSearchableEntityDocument(SearchableEntity doc, String result, + OxmEntityDescriptor resultDescriptor) throws JsonProcessingException, IOException { + + doc.setEntityType(resultDescriptor.getEntityName()); + + JsonNode entityNode = mapper.readTree(result); + + String id = NodeUtils.getNodeFieldAsText(entityNode, "id"); + String type = NodeUtils.getNodeFieldAsText(entityNode, "type"); + String url = NodeUtils.getNodeFieldAsText(entityNode, "url"); + + JsonNode properties = entityNode.get("properties"); + + Iterator fieldNames = properties.fieldNames(); + + + + List primaryKeyValues = new ArrayList(); + String pkeyValue = null; + + SearchableOxmEntityDescriptor searchableDescriptor = searchableEntityLookup.getSearchableEntityDescriptors().get(resultDescriptor.getEntityName()); + + for (String keyName : searchableDescriptor.getPrimaryKeyAttributeNames()) { + pkeyValue = NodeUtils.getNodeFieldAsText(properties, keyName); + if (pkeyValue != null) { + primaryKeyValues.add(pkeyValue); + } else { + String message = "populateSearchableEntityDocument(), pKeyValue is null for entityType = " + + resultDescriptor.getEntityName(); + LOG.warn(AaiUiMsgs.WARN_GENERIC, message); + } + } + + final String primaryCompositeKeyValue = NodeUtils.concatArray(primaryKeyValues, "/"); + doc.setEntityPrimaryKeyValue(primaryCompositeKeyValue); + + final List searchTagFields = searchableDescriptor.getSearchableAttributes(); + + /* + * Based on configuration, use the configured field names for this entity-Type to build a + * multi-value collection of search tags for elastic search entity search criteria. + */ + for (String searchTagField : searchTagFields) { + String searchTagValue = NodeUtils.getNodeFieldAsText(properties, searchTagField); + if (searchTagValue != null && !searchTagValue.isEmpty()) { + doc.addSearchTagWithKey(searchTagValue, searchTagField); + } + } + } + + /** + * Fetch document for upsert. + * + * @param txn the txn + */ + private void fetchDocumentForUpsert(NetworkTransaction txn) { + if (!txn.getOperationResult().wasSuccessful()) { + String message = "Self link failure. Result - " + txn.getOperationResult().getResult(); + LOG.error(AaiUiMsgs.ERROR_GENERIC, message); + return; + } + + SearchableOxmEntityDescriptor searchableDescriptor = searchableEntityLookup + .getSearchableEntityDescriptors().get(txn.getDescriptor().getEntityName()); + + try { + if (searchableDescriptor.hasSearchableAttributes()) { + + final String jsonResult = txn.getOperationResult().getResult(); + if (jsonResult != null && jsonResult.length() > 0) { + + SearchableEntity se = new SearchableEntity(); + se.setLink( txn.getLink() ); + populateSearchableEntityDocument(se, jsonResult, searchableDescriptor); + se.deriveFields(); + + + String link = null; + try { + link = elasticSearchAdapter.buildElasticSearchGetDocUrl(getIndexName(), se.getId()); + } catch (Exception exc) { + LOG.error(AaiUiMsgs.ES_FAILED_TO_CONSTRUCT_QUERY, exc.getLocalizedMessage()); + } + + if (link != null) { + NetworkTransaction n2 = new NetworkTransaction(); + n2.setLink(link); + n2.setEntityType(txn.getEntityType()); + n2.setDescriptor(txn.getDescriptor()); + n2.setOperationType(HttpMethod.GET); + + esWorkOnHand.incrementAndGet(); + + supplyAsync(new PerformElasticSearchRetrieval(n2, elasticSearchAdapter), esExecutor) + .whenComplete((result, error) -> { + + esWorkOnHand.decrementAndGet(); + + if (error != null) { + LOG.error(AaiUiMsgs.ES_RETRIEVAL_FAILED, error.getLocalizedMessage()); + } else { + updateElasticSearchCounters(result); + performDocumentUpsert(result, se); + } + }); + } + } + + } + } catch (JsonProcessingException exc) { + LOG.error(AaiUiMsgs.ERROR_GENERIC, "Processing error while fetching document for elasticsearch update. Error: " + exc.getMessage() ); + } catch (IOException exc) { + LOG.error(AaiUiMsgs.ERROR_GENERIC, "Processing error while fetching document for elasticsearch update. Error: " + exc.getMessage() ); + } + } + + /** + * Process store document result. + * + * @param esPutResult the es put result + * @param esGetResult the es get result + * @param se the se + */ + private void processStoreDocumentResult(NetworkTransaction esPutResult, + NetworkTransaction esGetResult, SearchableEntity se) { + + OperationResult or = esPutResult.getOperationResult(); + + if (!or.wasSuccessful()) { + if (or.getResultCode() == VERSION_CONFLICT_EXCEPTION_CODE) { + + if (shouldAllowRetry(se.getId())) { + esWorkOnHand.incrementAndGet(); + + RetrySearchableEntitySyncContainer rsc = + new RetrySearchableEntitySyncContainer(esGetResult, se); + retryQueue.push(rsc); + + String message = "Store document failed during searchable entity synchronization" + + " due to version conflict. Entity will be re-synced."; + LOG.warn(AaiUiMsgs.ES_SEARCHABLE_ENTITY_SYNC_ERROR, message); + } + } else { + String message = + "Store document failed during searchable entity synchronization with result code " + + or.getResultCode() + " and result message " + or.getResult(); + LOG.error(AaiUiMsgs.ES_SEARCHABLE_ENTITY_SYNC_ERROR, message); + } + } + } + + /** + * Perform retry sync. + */ + private void performRetrySync() { + while (retryQueue.peek() != null) { + + RetrySearchableEntitySyncContainer rsc = retryQueue.poll(); + if (rsc != null) { + + SearchableEntity se = rsc.getSearchableEntity(); + NetworkTransaction txn = rsc.getNetworkTransaction(); + + String link = null; + try { + /* + * In this retry flow the se object has already derived its fields + */ + link = elasticSearchAdapter.buildElasticSearchGetDocUrl(getIndexName(), se.getId()); + } catch (Exception exc) { + LOG.error(AaiUiMsgs.ES_FAILED_TO_CONSTRUCT_URI, exc.getLocalizedMessage()); + } + + if (link != null) { + NetworkTransaction retryTransaction = new NetworkTransaction(); + retryTransaction.setLink(link); + retryTransaction.setEntityType(txn.getEntityType()); + retryTransaction.setDescriptor(txn.getDescriptor()); + retryTransaction.setOperationType(HttpMethod.GET); + + /* + * IMPORTANT - DO NOT incrementAndGet the esWorkOnHand as this is a retry flow! We already + * called incrementAndGet when queuing the failed PUT! + */ + + supplyAsync(new PerformElasticSearchRetrieval(retryTransaction, elasticSearchAdapter), + esExecutor).whenComplete((result, error) -> { + + esWorkOnHand.decrementAndGet(); + + if (error != null) { + LOG.error(AaiUiMsgs.ES_RETRIEVAL_FAILED_RESYNC, error.getLocalizedMessage()); + } else { + updateElasticSearchCounters(result); + performDocumentUpsert(result, se); + } + }); + } + + } + } + } + + /** + * Should allow retry. + * + * @param id the id + * @return true, if successful + */ + private boolean shouldAllowRetry(String id) { + boolean isRetryAllowed = true; + if (retryLimitTracker.get(id) != null) { + Integer currentCount = retryLimitTracker.get(id); + if (currentCount.intValue() >= RETRY_COUNT_PER_ENTITY_LIMIT.intValue()) { + isRetryAllowed = false; + String message = "Searchable entity re-sync limit reached for " + id + + ", re-sync will no longer be attempted for this entity"; + LOG.error(AaiUiMsgs.ES_SEARCHABLE_ENTITY_SYNC_ERROR, message); + } else { + Integer newCount = new Integer(currentCount.intValue() + 1); + retryLimitTracker.put(id, newCount); + } + } else { + Integer firstRetryCount = new Integer(1); + retryLimitTracker.put(id, firstRetryCount); + } + + return isRetryAllowed; + } + + @Override + public SynchronizerState getState() { + if (!isSyncDone()) { + return SynchronizerState.PERFORMING_SYNCHRONIZATION; + } + + return SynchronizerState.IDLE; + + } + + /* (non-Javadoc) + * @see org.openecomp.sparky.synchronizer.IndexSynchronizer#getStatReport(boolean) + */ + @Override + public String getStatReport(boolean showFinalReport) { + syncDurationInMs = System.currentTimeMillis() - syncStartedTimeStampInMs; + return this.getStatReport(syncDurationInMs, showFinalReport); + } + + /* (non-Javadoc) + * @see org.openecomp.sparky.synchronizer.IndexSynchronizer#shutdown() + */ + @Override + public void shutdown() { + this.shutdownExecutors(); + } + + @Override + protected boolean isSyncDone() { + int totalWorkOnHand = aaiWorkOnHand.get() + esWorkOnHand.get(); + + if (totalWorkOnHand > 0 || !allWorkEnumerated) { + return false; + } + + return true; + } + +} diff --git a/src/test/java/org/onap/aai/sparky/viewandinspect/sync/ViewInspectGizmoSyncController.java b/src/test/java/org/onap/aai/sparky/viewandinspect/sync/ViewInspectGizmoSyncController.java new file mode 100644 index 0000000..1fdadfc --- /dev/null +++ b/src/test/java/org/onap/aai/sparky/viewandinspect/sync/ViewInspectGizmoSyncController.java @@ -0,0 +1,106 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017 Amdocs + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.sparky.viewandinspect.sync; + +import org.onap.aai.sparky.config.oxm.OxmEntityContainerLookup; +import org.onap.aai.sparky.config.oxm.OxmEntityLookup; +import org.onap.aai.sparky.config.oxm.SearchableEntityLookup; +import org.onap.aai.sparky.dal.ElasticSearchAdapter; +import org.onap.aai.sparky.dal.GizmoAdapter; +import org.onap.aai.sparky.sync.ElasticSearchIndexCleaner; +import org.onap.aai.sparky.sync.ElasticSearchSchemaFactory; +import org.onap.aai.sparky.sync.IndexCleaner; +import org.onap.aai.sparky.sync.IndexIntegrityValidator; +import org.onap.aai.sparky.sync.SyncControllerImpl; +import org.onap.aai.sparky.sync.SyncControllerRegistrar; +import org.onap.aai.sparky.sync.SyncControllerRegistry; +import org.onap.aai.sparky.sync.config.ElasticSearchEndpointConfig; +import org.onap.aai.sparky.sync.config.ElasticSearchSchemaConfig; +import org.onap.aai.sparky.sync.config.NetworkStatisticsConfig; +import org.onap.aai.sparky.sync.config.SyncControllerConfig; + +public class ViewInspectGizmoSyncController extends SyncControllerImpl + implements SyncControllerRegistrar { + + private SyncControllerRegistry syncControllerRegistry; + //private GizmoAdapter gizmoAdapter; + //private ElasticSearchAdapter esAdapter; + //private ElasticSearchSchemaConfig schemaConfig; + //private ElasticSearchEndpointConfig endpointConfig; + + public ViewInspectGizmoSyncController(SyncControllerConfig syncControllerConfig, + GizmoAdapter gizmoAdapter, ElasticSearchAdapter esAdapter, + ElasticSearchSchemaConfig schemaConfig, ElasticSearchEndpointConfig endpointConfig, + NetworkStatisticsConfig gizmoStatConfig, NetworkStatisticsConfig esStatConfig, + OxmEntityLookup oxmEntityLookup, + SearchableEntityLookup searchableEntityLookup, OxmEntityContainerLookup oxmEntityContainerLookup) throws Exception { + super(syncControllerConfig); + + // final String controllerName = "View and Inspect Entity Synchronizer"; + + //this.gizmoAdapter = gizmoAdapter; + //this.esAdapter = esAdapter; + //this.schemaConfig = schemaConfig; + //this.endpointConfig = endpointConfig; + + IndexIntegrityValidator indexValidator = new IndexIntegrityValidator(esAdapter, schemaConfig, + endpointConfig, ElasticSearchSchemaFactory.getIndexSchema(schemaConfig)); + + registerIndexValidator(indexValidator); + + ViewInspectGizmoEntitySynchronizer ses = new ViewInspectGizmoEntitySynchronizer(schemaConfig, + syncControllerConfig.getNumInternalSyncWorkers(), + syncControllerConfig.getNumSyncActiveInventoryWorkers(), + syncControllerConfig.getNumSyncElasticWorkers(), gizmoStatConfig, esStatConfig, + oxmEntityLookup, searchableEntityLookup, oxmEntityContainerLookup); + + ses.setGizmoAdapter(gizmoAdapter); + ses.setElasticSearchAdapter(esAdapter); + + registerEntitySynchronizer(ses); + + IndexCleaner indexCleaner = + new ElasticSearchIndexCleaner(esAdapter, endpointConfig, schemaConfig); + + registerIndexCleaner(indexCleaner); + + } + + public SyncControllerRegistry getSyncControllerRegistry() { + return syncControllerRegistry; + } + + public void setSyncControllerRegistry(SyncControllerRegistry syncControllerRegistry) { + this.syncControllerRegistry = syncControllerRegistry; + } + + @Override + public void registerController() { + if ( syncControllerRegistry != null ) { + if ( syncControllerConfig.isEnabled()) { + syncControllerRegistry.registerSyncController(this); + } + } + + } +} -- 2.16.6