Adding UI extensibility
[aai/sparky-be.git] / src / main / java / org / onap / aai / sparky / crossentityreference / sync / CrossEntityReferenceSynchronizer.java
  *
  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
  */
-package org.onap.aai.sparky.synchronizer;
+package org.onap.aai.sparky.crossentityreference.sync;
 
 import static java.util.concurrent.CompletableFuture.supplyAsync;
 
 import java.io.IOException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Deque;
@@ -38,30 +36,39 @@ 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.CrossEntityReference;
+import org.onap.aai.sparky.config.oxm.CrossEntityReferenceDescriptor;
+import org.onap.aai.sparky.config.oxm.CrossEntityReferenceLookup;
 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.NetworkTransaction;
 import org.onap.aai.sparky.dal.aai.config.ActiveInventoryConfig;
 import org.onap.aai.sparky.dal.elasticsearch.config.ElasticSearchConfig;
 import org.onap.aai.sparky.dal.rest.HttpMethod;
-import org.onap.aai.sparky.dal.rest.OperationResult;
 import org.onap.aai.sparky.logging.AaiUiMsgs;
-import org.onap.aai.sparky.synchronizer.config.SynchronizerConfiguration;
-import org.onap.aai.sparky.synchronizer.entity.IndexableCrossEntityReference;
-import org.onap.aai.sparky.synchronizer.entity.MergableEntity;
-import org.onap.aai.sparky.synchronizer.entity.SelfLinkDescriptor;
-import org.onap.aai.sparky.synchronizer.enumeration.OperationState;
-import org.onap.aai.sparky.synchronizer.enumeration.SynchronizerState;
-import org.onap.aai.sparky.synchronizer.task.PerformActiveInventoryRetrieval;
-import org.onap.aai.sparky.synchronizer.task.PerformElasticSearchPut;
-import org.onap.aai.sparky.synchronizer.task.PerformElasticSearchRetrieval;
-import org.onap.aai.sparky.synchronizer.task.PerformElasticSearchUpdate;
+import org.onap.aai.sparky.sync.AbstractEntitySynchronizer;
+import org.onap.aai.sparky.sync.IndexSynchronizer;
+import org.onap.aai.sparky.sync.SynchronizerConstants;
+import org.onap.aai.sparky.sync.config.ElasticSearchSchemaConfig;
+import org.onap.aai.sparky.sync.config.NetworkStatisticsConfig;
+import org.onap.aai.sparky.sync.entity.IndexableCrossEntityReference;
+import org.onap.aai.sparky.sync.entity.MergableEntity;
+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.PerformActiveInventoryRetrieval;
+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.util.NodeUtils;
-import org.onap.aai.cl.api.Logger;
-import org.onap.aai.cl.eelf.LoggerFactory;
 import org.slf4j.MDC;
 
-import org.onap.aai.cl.mdc.MdcContext;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectReader;
@@ -105,12 +112,13 @@ public class CrossEntityReferenceSynchronizer extends AbstractEntitySynchronizer
       LoggerFactory.getInstance().getLogger(CrossEntityReferenceSynchronizer.class);
 
   private static final String SERVICE_INSTANCE = "service-instance";
+
   private Deque<SelfLinkDescriptor> selflinks;
   private Deque<RetryCrossEntitySyncContainer> retryQueue;
   private Map<String, Integer> retryLimitTracker;
   private boolean isAllWorkEnumerated;
   protected ExecutorService esPutExecutor;
-  protected ActiveInventoryConfig aaiConfig;
+
 
   /**
    * Instantiates a new cross entity reference synchronizer.
@@ -118,27 +126,29 @@ public class CrossEntityReferenceSynchronizer extends AbstractEntitySynchronizer
    * @param indexName the index name
    * @throws Exception the exception
    */
-  public CrossEntityReferenceSynchronizer(String indexName, ActiveInventoryConfig aaiConfig)
-      throws Exception {
-    super(LOG, "CERS", 2, 5, 5, indexName);
+  public CrossEntityReferenceSynchronizer(ElasticSearchSchemaConfig schemaConfig,
+      int internalSyncWorkers, int aaiWorkers, int esWorkers, NetworkStatisticsConfig aaiStatConfig,
+      NetworkStatisticsConfig esStatConfig) throws Exception {
+    super(LOG, "CERS", internalSyncWorkers, aaiWorkers, esWorkers, schemaConfig.getIndexName(),
+        aaiStatConfig, esStatConfig);
     this.selflinks = new ConcurrentLinkedDeque<SelfLinkDescriptor>();
     this.retryQueue = new ConcurrentLinkedDeque<RetryCrossEntitySyncContainer>();
     this.retryLimitTracker = new ConcurrentHashMap<String, Integer>();
     this.synchronizerName = "Cross Reference Entity Synchronizer";
     this.isAllWorkEnumerated = false;
     this.esPutExecutor = NodeUtils.createNamedExecutor("CERS-ES-PUT", 5, LOG);
-    this.aaiEntityStats.initializeCountersFromOxmEntityDescriptors(
-        oxmModelLoader.getCrossReferenceEntityDescriptors());
-    this.esEntityStats.initializeCountersFromOxmEntityDescriptors(
-        oxmModelLoader.getCrossReferenceEntityDescriptors());
-    this.aaiConfig = aaiConfig;
+    this.aaiEntityStats.intializeEntityCounters(
+        CrossEntityReferenceLookup.getInstance().getCrossReferenceEntityDescriptors().keySet());
+
+    this.esEntityStats.intializeEntityCounters(
+        CrossEntityReferenceLookup.getInstance().getCrossReferenceEntityDescriptors().keySet());
     this.syncDurationInMs = -1;
   }
 
   /*
    * (non-Javadoc)
    * 
-   * @see org.onap.aai.sparky.synchronizer.IndexSynchronizer#doSync()
+   * @see org.openecomp.sparky.synchronizer.IndexSynchronizer#doSync()
    */
   @Override
   public OperationState doSync() {
@@ -164,7 +174,7 @@ public class CrossEntityReferenceSynchronizer extends AbstractEntitySynchronizer
   /*
    * (non-Javadoc)
    * 
-   * @see org.onap.aai.sparky.synchronizer.IndexSynchronizer#getStatReport(boolean)
+   * @see org.openecomp.sparky.synchronizer.IndexSynchronizer#getStatReport(boolean)
    */
   @Override
   public String getStatReport(boolean showFinalReport) {
@@ -175,7 +185,7 @@ public class CrossEntityReferenceSynchronizer extends AbstractEntitySynchronizer
   /*
    * (non-Javadoc)
    * 
-   * @see org.onap.aai.sparky.synchronizer.IndexSynchronizer#shutdown()
+   * @see org.openecomp.sparky.synchronizer.IndexSynchronizer#shutdown()
    */
   @Override
   public void shutdown() {
@@ -200,8 +210,8 @@ public class CrossEntityReferenceSynchronizer extends AbstractEntitySynchronizer
    */
   private OperationState launchSyncFlow() {
     final Map<String, String> contextMap = MDC.getCopyOfContextMap();
-    Map<String, OxmEntityDescriptor> descriptorMap =
-        oxmModelLoader.getCrossReferenceEntityDescriptors();
+    Map<String, CrossEntityReferenceDescriptor> descriptorMap =
+        CrossEntityReferenceLookup.getInstance().getCrossReferenceEntityDescriptors();
 
     if (descriptorMap.isEmpty()) {
       LOG.error(AaiUiMsgs.ERROR_LOADING_OXM);
@@ -229,7 +239,7 @@ public class CrossEntityReferenceSynchronizer extends AbstractEntitySynchronizer
             MDC.setContextMap(contextMap);
             OperationResult typeLinksResult = null;
             try {
-              typeLinksResult = aaiDataProvider.getSelfLinksByEntityType(key);
+              typeLinksResult = aaiAdapter.getSelfLinksByEntityType(key);
               aaiWorkOnHand.decrementAndGet();
               processEntityTypeSelfLinks(typeLinksResult);
             } catch (Exception exc) {
@@ -286,11 +296,12 @@ public class CrossEntityReferenceSynchronizer extends AbstractEntitySynchronizer
       SelfLinkDescriptor linkDescriptor = selflinks.poll();
       aaiWorkOnHand.decrementAndGet();
 
-      OxmEntityDescriptor descriptor = null;
+      CrossEntityReferenceDescriptor descriptor = null;
 
       if (linkDescriptor.getSelfLink() != null && linkDescriptor.getEntityType() != null) {
 
-        descriptor = oxmModelLoader.getEntityDescriptor(linkDescriptor.getEntityType());
+        descriptor = CrossEntityReferenceLookup.getInstance().getCrossReferenceEntityDescriptors()
+            .get(linkDescriptor.getEntityType());
 
         if (descriptor == null) {
           LOG.error(AaiUiMsgs.MISSING_ENTITY_DESCRIPTOR, linkDescriptor.getEntityType());
@@ -302,13 +313,14 @@ public class CrossEntityReferenceSynchronizer extends AbstractEntitySynchronizer
 
           NetworkTransaction txn = new NetworkTransaction();
           txn.setDescriptor(descriptor);
-          txn.setLink(linkDescriptor.getSelfLink() + linkDescriptor.getDepthModifier());
+          txn.setLink(linkDescriptor.getSelfLink());
+          txn.setQueryParameters(linkDescriptor.getDepthModifier());
           txn.setOperationType(HttpMethod.GET);
           txn.setEntityType(linkDescriptor.getEntityType());
 
           aaiWorkOnHand.incrementAndGet();
 
-          supplyAsync(new PerformActiveInventoryRetrieval(txn, aaiDataProvider), aaiExecutor)
+          supplyAsync(new PerformActiveInventoryRetrieval(txn, aaiAdapter), aaiExecutor)
               .whenComplete((result, error) -> {
 
                 aaiWorkOnHand.decrementAndGet();
@@ -345,15 +357,14 @@ public class CrossEntityReferenceSynchronizer extends AbstractEntitySynchronizer
       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);
-        return;
+        // TODO // TODO -> LOG, waht should be logged here?
       }
 
       JsonNode resultData = rootNode.get("result-data");
       ArrayNode resultDataArrayNode = null;
 
+      CrossEntityReferenceLookup cerLookup = CrossEntityReferenceLookup.getInstance();
+
       if (resultData.isArray()) {
         resultDataArrayNode = (ArrayNode) resultData;
 
@@ -366,10 +377,10 @@ public class CrossEntityReferenceSynchronizer extends AbstractEntitySynchronizer
           final String resourceType = NodeUtils.getNodeFieldAsText(element, "resource-type");
           final String resourceLink = NodeUtils.getNodeFieldAsText(element, "resource-link");
 
-          OxmEntityDescriptor descriptor = null;
+          CrossEntityReferenceDescriptor descriptor = null;
 
           if (resourceType != null && resourceLink != null) {
-            descriptor = oxmModelLoader.getEntityDescriptor(resourceType);
+            descriptor = cerLookup.getCrossReferenceEntityDescriptors().get(resourceType);
 
             if (descriptor == null) {
               LOG.error(AaiUiMsgs.MISSING_ENTITY_DESCRIPTOR, resourceType);
@@ -378,7 +389,7 @@ public class CrossEntityReferenceSynchronizer extends AbstractEntitySynchronizer
             }
             if (descriptor.hasCrossEntityReferences()) {
               selflinks.add(new SelfLinkDescriptor(resourceLink,
-                  SynchronizerConfiguration.DEPTH_ALL_MODIFIER, resourceType));
+                  SynchronizerConstants.DEPTH_ALL_MODIFIER, resourceType));
             }
           }
         }
@@ -398,13 +409,14 @@ public class CrossEntityReferenceSynchronizer extends AbstractEntitySynchronizer
    */
   private String determineEntityQueryString(String entityType, JsonNode entityJsonNode) {
 
-    OxmEntityDescriptor entityDescriptor = oxmModelLoader.getEntityDescriptor(entityType);
+    OxmEntityDescriptor entityDescriptor =
+        OxmEntityLookup.getInstance().getEntityDescriptors().get(entityType);
 
     String queryString = null;
 
     if (entityDescriptor != null) {
 
-      final List<String> primaryKeyNames = entityDescriptor.getPrimaryKeyAttributeName();
+      final List<String> primaryKeyNames = entityDescriptor.getPrimaryKeyAttributeNames();
       final List<String> keyValues = new ArrayList<String>();
       NodeUtils.extractFieldValuesFromObject(entityJsonNode, primaryKeyNames, keyValues);
 
@@ -430,7 +442,10 @@ public class CrossEntityReferenceSynchronizer extends AbstractEntitySynchronizer
       return;
     }
 
-    if (txn.getDescriptor().hasCrossEntityReferences()) {
+    CrossEntityReferenceDescriptor cerDescriptor = CrossEntityReferenceLookup.getInstance()
+        .getCrossReferenceEntityDescriptors().get(txn.getDescriptor().getEntityName());
+
+    if (cerDescriptor != null && cerDescriptor.hasCrossEntityReferences()) {
 
       final String jsonResult = txn.getOperationResult().getResult();
 
@@ -448,182 +463,207 @@ public class CrossEntityReferenceSynchronizer extends AbstractEntitySynchronizer
          * <li>Rinse and repeat.
          */
 
-        OxmEntityDescriptor parentEntityDescriptor =
-            oxmModelLoader.getEntityDescriptor(txn.getEntityType());
+        CrossEntityReference cerDefinition = cerDescriptor.getCrossEntityReference();
+
+        if (cerDefinition != null) {
+          JsonNode convertedNode = null;
+          try {
+            convertedNode =
+                NodeUtils.convertJsonStrToJsonNode(txn.getOperationResult().getResult());
+
+            final String parentEntityQueryString =
+                determineEntityQueryString(txn.getEntityType(), convertedNode);
+
+            List<String> extractedParentEntityAttributeValues = new ArrayList<String>();
+
+            NodeUtils.extractFieldValuesFromObject(convertedNode,
+                cerDefinition.getReferenceAttributes(), extractedParentEntityAttributeValues);
+
+            List<JsonNode> nestedTargetEntityInstances = new ArrayList<JsonNode>();
+            NodeUtils.extractObjectsByKey(convertedNode, cerDefinition.getTargetEntityType(),
+                nestedTargetEntityInstances);
+
+            for (JsonNode targetEntityInstance : nestedTargetEntityInstances) {
+
+              if (cerDescriptor != null) {
+
+                String childEntityType = cerDefinition.getTargetEntityType();
+
+                List<String> childPrimaryKeyNames = cerDescriptor.getPrimaryKeyAttributeNames();
+
+                List<String> childKeyValues = new ArrayList<String>();
+                NodeUtils.extractFieldValuesFromObject(targetEntityInstance, childPrimaryKeyNames,
+                    childKeyValues);
+
+                String childEntityQueryKeyString =
+                    childEntityType + "." + NodeUtils.concatArray(childPrimaryKeyNames, "/") + ":"
+                        + NodeUtils.concatArray(childKeyValues);
+
+                /**
+                 * Build generic-query to query child instance self-link from AAI
+                 */
+                List<String> orderedQueryKeyParams = new ArrayList<String>();
+
+                /**
+                 * At present, there is an issue with resolving the self-link using the
+                 * generic-query with nothing more than the service-instance identifier and the
+                 * service-subscription. There is another level of detail we don't have access to
+                 * unless we parse it out of the service-subscription self-link, which is a coupling
+                 * I would like to avoid. Fortunately, there is a workaround, but only for
+                 * service-instances, which is presently our only use-case for the
+                 * cross-entity-reference in R1707. Going forwards hopefully there will be other
+                 * ways to resolve a child self-link using parental embedded meta data that we don't
+                 * currently have.
+                 * 
+                 * The work-around with the service-instance entity-type is that it's possible to
+                 * request the self-link using only the service-instance-id because of a historical
+                 * AAI functional query requirement that it be possible to query a service-instance
+                 * only by it's service-instance-id. This entity type is the only one in the system
+                 * that can be queried this way which makes it a very limited workaround, but good
+                 * enough for the current release.
+                 */
+
+                if (SERVICE_INSTANCE.equals(childEntityType)) {
+                  orderedQueryKeyParams.clear();
+                  orderedQueryKeyParams.add(childEntityQueryKeyString);
+                } else {
+                  orderedQueryKeyParams.add(parentEntityQueryString);
+                  orderedQueryKeyParams.add(childEntityQueryKeyString);
+                }
 
-        if (parentEntityDescriptor != null) {
+                String genericQueryStr = null;
+                try {
+                  genericQueryStr =
+                      aaiAdapter.getGenericQueryForSelfLink(childEntityType, orderedQueryKeyParams);
 
-          CrossEntityReference cerDefinition = parentEntityDescriptor.getCrossEntityReference();
+                  if (genericQueryStr != null) {
+                    aaiWorkOnHand.incrementAndGet();
 
-          if (cerDefinition != null) {
-            JsonNode convertedNode = null;
-            try {
-              convertedNode =
-                  NodeUtils.convertJsonStrToJsonNode(txn.getOperationResult().getResult());
+                    OperationResult aaiQueryResult = aaiAdapter.queryActiveInventoryWithRetries(
+                        genericQueryStr, "application/json", aaiAdapter.getNumRequestRetries());
 
-              final String parentEntityQueryString =
-                  determineEntityQueryString(txn.getEntityType(), convertedNode);
+                    aaiWorkOnHand.decrementAndGet();
 
-              List<String> extractedParentEntityAttributeValues = new ArrayList<String>();
+                    if (aaiQueryResult != null && aaiQueryResult.wasSuccessful()) {
 
-              NodeUtils.extractFieldValuesFromObject(convertedNode,
-                  cerDefinition.getReferenceAttributes(), extractedParentEntityAttributeValues);
+                      Collection<JsonNode> entityLinks = new ArrayList<JsonNode>();
+                      JsonNode genericQueryResult = null;
+                      try {
+                        genericQueryResult =
+                            NodeUtils.convertJsonStrToJsonNode(aaiQueryResult.getResult());
 
-              List<JsonNode> nestedTargetEntityInstances = new ArrayList<JsonNode>();
-              NodeUtils.extractObjectsByKey(convertedNode, cerDefinition.getTargetEntityType(),
-                  nestedTargetEntityInstances);
+                        if (genericQueryResult != null) {
 
-              for (JsonNode targetEntityInstance : nestedTargetEntityInstances) {
+                          NodeUtils.extractObjectsByKey(genericQueryResult, "resource-link",
+                              entityLinks);
 
-                OxmEntityDescriptor cerDescriptor = oxmModelLoader
-                    .getSearchableEntityDescriptor(cerDefinition.getTargetEntityType());
+                          String selfLink = null;
 
-                if (cerDescriptor != null) {
+                          if (entityLinks.size() != 1) {
+                            /**
+                             * an ambiguity exists where we can't reliably determine the self link,
+                             * this should be a permanent error
+                             */
+                            LOG.error(AaiUiMsgs.ENTITY_SYNC_FAILED_SELFLINK_AMBIGUITY,
+                                String.valueOf(entityLinks.size()));
+                          } else {
+                            selfLink = ((JsonNode) entityLinks.toArray()[0]).asText();
 
-                  String childEntityType = cerDefinition.getTargetEntityType();
+                            SearchableEntityLookup searchableEntityLookup =
+                                SearchableEntityLookup.getInstance();
 
-                  List<String> childPrimaryKeyNames = cerDescriptor.getPrimaryKeyAttributeName();
+                            SearchableOxmEntityDescriptor searchableDescriptor =
+                                searchableEntityLookup.getSearchableEntityDescriptors()
+                                    .get(txn.getEntityType());
 
-                  List<String> childKeyValues = new ArrayList<String>();
-                  NodeUtils.extractFieldValuesFromObject(targetEntityInstance, childPrimaryKeyNames,
-                      childKeyValues);
+                            if (searchableDescriptor != null
+                                && searchableDescriptor.getSearchableAttributes().size() > 0) {
 
-                  String childEntityQueryKeyString =
-                      childEntityType + "." + NodeUtils.concatArray(childPrimaryKeyNames, "/") + ":"
-                          + NodeUtils.concatArray(childKeyValues);
+                              IndexableCrossEntityReference icer =
+                                  getPopulatedDocument(targetEntityInstance, cerDescriptor);
 
-                  /**
-                   * Build generic-query to query child instance self-link from AAI
-                   */
-                  List<String> orderedQueryKeyParams = new ArrayList<String>();
-                  if (SERVICE_INSTANCE.equals(childEntityType)) {
-                    orderedQueryKeyParams.clear();
-                    orderedQueryKeyParams.add(childEntityQueryKeyString);
-                  } else {
-                    orderedQueryKeyParams.add(parentEntityQueryString);
-                    orderedQueryKeyParams.add(childEntityQueryKeyString);
-                  }
-                  String genericQueryStr = null;
-                  try {
-                    genericQueryStr = aaiDataProvider.getGenericQueryForSelfLink(childEntityType,
-                        orderedQueryKeyParams);
-
-                    if (genericQueryStr != null) {
-                      aaiWorkOnHand.incrementAndGet();
-                      OperationResult aaiQueryResult = aaiDataProvider
-                          .queryActiveInventoryWithRetries(genericQueryStr, "application/json",
-                              aaiConfig.getAaiRestConfig().getNumRequestRetries());
-                      aaiWorkOnHand.decrementAndGet();
-                      if (aaiQueryResult != null && aaiQueryResult.wasSuccessful()) {
-
-                        Collection<JsonNode> entityLinks = new ArrayList<JsonNode>();
-                        JsonNode genericQueryResult = null;
-                        try {
-                          genericQueryResult =
-                              NodeUtils.convertJsonStrToJsonNode(aaiQueryResult.getResult());
-
-                          if (genericQueryResult != null) {
-
-                            NodeUtils.extractObjectsByKey(genericQueryResult, "resource-link",
-                                entityLinks);
-
-                            String selfLink = null;
-
-                            if (entityLinks.size() != 1) {
-                              /**
-                               * an ambiguity exists where we can't reliably determine the self
-                               * link, this should be a permanent error
-                               */
-                              LOG.error(AaiUiMsgs.ENTITY_SYNC_FAILED_SELFLINK_AMBIGUITY,
-                                  String.valueOf(entityLinks.size()));
-                            } else {
-                              selfLink = ((JsonNode) entityLinks.toArray()[0]).asText();
-
-                              if (!cerDescriptor.getSearchableAttributes().isEmpty()) {
-
-                                IndexableCrossEntityReference icer =
-                                    getPopulatedDocument(targetEntityInstance, cerDescriptor);
-
-                                for (String parentCrossEntityReferenceAttributeValue : extractedParentEntityAttributeValues) {
-                                  icer.addCrossEntityReferenceValue(
-                                      parentCrossEntityReferenceAttributeValue);
-                                }
-
-                                icer.setLink(ActiveInventoryConfig.extractResourcePath(selfLink));
-
-                                icer.deriveFields();
-
-                                String link = null;
-                                try {
-                                  link = getElasticFullUrl("/" + icer.getId(), getIndexName());
-                                } 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, esDataProvider),
-                                      esExecutor).whenComplete((result, error) -> {
-
-                                        esWorkOnHand.decrementAndGet();
-
-                                        if (error != null) {
-                                          LOG.error(AaiUiMsgs.ES_RETRIEVAL_FAILED,
-                                              error.getLocalizedMessage());
-                                        } else {
-                                          updateElasticSearchCounters(result);
-                                          performDocumentUpsert(result, icer);
-                                        }
-                                      });
-                                }
+                              for (String parentCrossEntityReferenceAttributeValue : extractedParentEntityAttributeValues) {
+                                icer.addCrossEntityReferenceValue(
+                                    parentCrossEntityReferenceAttributeValue);
+                              }
+
+                              icer.setLink(ActiveInventoryConfig.extractResourcePath(selfLink));
+
+                              icer.deriveFields();
+
+                              String link = null;
+                              try {
+                                link = getElasticFullUrl("/" + icer.getId(), getIndexName());
+                              } 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, icer);
+                                      }
+                                    });
                               }
                             }
-                          } else {
-                            LOG.error(AaiUiMsgs.ENTITY_SYNC_FAILED_DURING_AAI_RESPONSE_CONVERSION);
                           }
-
-                        } catch (Exception exc) {
-                          LOG.error(AaiUiMsgs.JSON_CONVERSION_ERROR, JsonNode.class.toString(),
-                              exc.getLocalizedMessage());
+                        } else {
+                          LOG.error(AaiUiMsgs.ENTITY_SYNC_FAILED_DURING_AAI_RESPONSE_CONVERSION);
                         }
 
-                      } else {
-                        String message = "Entity sync failed because AAI query failed with error ";
-                        LOG.error(AaiUiMsgs.ENTITY_SYNC_FAILED_QUERY_ERROR, message);
+                      } catch (Exception exc) {
+                        LOG.error(AaiUiMsgs.JSON_CONVERSION_ERROR, JsonNode.class.toString(),
+                            exc.getLocalizedMessage());
                       }
 
                     } else {
-                      String message =
-                          "Entity Sync failed because generic query str could not be determined.";
+                      String message = "Entity sync failed because AAI query failed with error "
+                          + aaiQueryResult.getResult();
                       LOG.error(AaiUiMsgs.ENTITY_SYNC_FAILED_QUERY_ERROR, message);
                     }
-                  } catch (Exception exc) {
+
+                  } else {
                     String message =
-                        "Failed to sync entity because generation of generic query failed with error = "
-                            + exc.getMessage();
+                        "Entity Sync failed because generic query str could not be determined.";
                     LOG.error(AaiUiMsgs.ENTITY_SYNC_FAILED_QUERY_ERROR, message);
                   }
-
+                } catch (Exception exc) {
+                  String message =
+                      "Failed to sync entity because generation of generic query failed with error = "
+                          + exc.getMessage();
+                  LOG.error(AaiUiMsgs.ENTITY_SYNC_FAILED_QUERY_ERROR, message);
                 }
-              }
 
-            } catch (IOException ioe) {
-              LOG.error(AaiUiMsgs.JSON_PROCESSING_ERROR, ioe.getMessage());
+              }
             }
-          }
 
-        } else {
-          LOG.error(AaiUiMsgs.ENTITY_SYNC_FAILED_DESCRIPTOR_NOT_FOUND, txn.getEntityType());
+          } catch (IOException ioe) {
+            LOG.error(AaiUiMsgs.JSON_PROCESSING_ERROR, ioe.getMessage());
+          }
         }
+
       }
+
+    } else {
+      LOG.error(AaiUiMsgs.ENTITY_SYNC_FAILED_DESCRIPTOR_NOT_FOUND, txn.getEntityType());
     }
   }
 
@@ -694,7 +734,7 @@ public class CrossEntityReferenceSynchronizer extends AbstractEntitySynchronizer
             String responseSource = NodeUtils.convertObjectToJson(sourceObject.get(0), false);
             MergableEntity me = mapper.readValue(responseSource, MergableEntity.class);
             ObjectReader updater = mapper.readerForUpdating(me);
-            MergableEntity merged = updater.readValue(icer.getIndexDocumentJson());
+            MergableEntity merged = updater.readValue(icer.getAsJson());
             jsonPayload = mapper.writeValueAsString(merged);
           }
         } catch (IOException exc) {
@@ -703,14 +743,15 @@ public class CrossEntityReferenceSynchronizer extends AbstractEntitySynchronizer
           return;
         }
       } else {
-        jsonPayload = icer.getIndexDocumentJson();
+        jsonPayload = icer.getAsJson();
       }
 
       if (wasEntryDiscovered) {
         if (versionNumber != null && jsonPayload != null) {
 
-          String requestPayload = esDataProvider.buildBulkImportOperationRequest(getIndexName(),
-              ElasticSearchConfig.getConfig().getType(), icer.getId(), versionNumber, jsonPayload);
+          String requestPayload = elasticSearchAdapter.buildBulkImportOperationRequest(
+              getIndexName(), ElasticSearchConfig.getConfig().getType(), icer.getId(),
+              versionNumber, jsonPayload);
 
           NetworkTransaction transactionTracker = new NetworkTransaction();
           transactionTracker.setEntityType(esGetResult.getEntityType());
@@ -719,7 +760,7 @@ public class CrossEntityReferenceSynchronizer extends AbstractEntitySynchronizer
 
           esWorkOnHand.incrementAndGet();
           supplyAsync(new PerformElasticSearchUpdate(ElasticSearchConfig.getConfig().getBulkUrl(),
-              requestPayload, esDataProvider, transactionTracker), esPutExecutor)
+              requestPayload, elasticSearchAdapter, transactionTracker), esPutExecutor)
                   .whenComplete((result, error) -> {
 
                     esWorkOnHand.decrementAndGet();
@@ -743,7 +784,8 @@ public class CrossEntityReferenceSynchronizer extends AbstractEntitySynchronizer
           updateElasticTxn.setOperationType(HttpMethod.PUT);
 
           esWorkOnHand.incrementAndGet();
-          supplyAsync(new PerformElasticSearchPut(jsonPayload, updateElasticTxn, esDataProvider),
+          supplyAsync(
+              new PerformElasticSearchPut(jsonPayload, updateElasticTxn, elasticSearchAdapter),
               esPutExecutor).whenComplete((result, error) -> {
 
                 esWorkOnHand.decrementAndGet();
@@ -826,7 +868,7 @@ public class CrossEntityReferenceSynchronizer extends AbstractEntitySynchronizer
            * that for this request already when queuing the failed PUT!
            */
 
-          supplyAsync(new PerformElasticSearchRetrieval(retryTransaction, esDataProvider),
+          supplyAsync(new PerformElasticSearchRetrieval(retryTransaction, elasticSearchAdapter),
               esExecutor).whenComplete((result, error) -> {
 
                 esWorkOnHand.decrementAndGet();
@@ -882,14 +924,14 @@ public class CrossEntityReferenceSynchronizer extends AbstractEntitySynchronizer
   protected IndexableCrossEntityReference getPopulatedDocument(JsonNode entityNode,
       OxmEntityDescriptor resultDescriptor) throws JsonProcessingException, IOException {
 
-    IndexableCrossEntityReference icer = new IndexableCrossEntityReference(oxmModelLoader);
+    IndexableCrossEntityReference icer = new IndexableCrossEntityReference();
 
     icer.setEntityType(resultDescriptor.getEntityName());
 
     List<String> primaryKeyValues = new ArrayList<String>();
     String pkeyValue = null;
 
-    for (String keyName : resultDescriptor.getPrimaryKeyAttributeName()) {
+    for (String keyName : resultDescriptor.getPrimaryKeyAttributeNames()) {
       pkeyValue = NodeUtils.getNodeFieldAsText(entityNode, keyName);
       if (pkeyValue != null) {
         primaryKeyValues.add(pkeyValue);