Changing the license and trademark
[aai/sparky-be.git] / src / main / java / org / openecomp / sparky / synchronizer / AutosuggestionSynchronizer.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
6  * Copyright © 2017 Amdocs
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *       http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  *
21  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22  */
23 package org.openecomp.sparky.synchronizer;
24
25 import static java.util.concurrent.CompletableFuture.supplyAsync;
26
27 import java.io.IOException;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collection;
31 import java.util.Deque;
32 import java.util.EnumSet;
33 import java.util.HashMap;
34 import java.util.Iterator;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.concurrent.ConcurrentHashMap;
38 import java.util.concurrent.ConcurrentLinkedDeque;
39 import java.util.concurrent.ExecutorService;
40 import java.util.concurrent.atomic.AtomicInteger;
41 import java.util.function.Supplier;
42
43 import org.openecomp.cl.api.Logger;
44 import org.openecomp.cl.eelf.LoggerFactory;
45 import org.openecomp.cl.mdc.MdcContext;
46 import org.openecomp.sparky.config.oxm.OxmEntityDescriptor;
47 import org.openecomp.sparky.dal.NetworkTransaction;
48 import org.openecomp.sparky.dal.aai.config.ActiveInventoryConfig;
49 import org.openecomp.sparky.dal.rest.HttpMethod;
50 import org.openecomp.sparky.dal.rest.OperationResult;
51 import org.openecomp.sparky.logging.AaiUiMsgs;
52 import org.openecomp.sparky.synchronizer.config.SynchronizerConfiguration;
53 import org.openecomp.sparky.synchronizer.entity.SelfLinkDescriptor;
54 import org.openecomp.sparky.synchronizer.entity.SuggestionSearchEntity;
55 import org.openecomp.sparky.synchronizer.enumeration.OperationState;
56 import org.openecomp.sparky.synchronizer.enumeration.SynchronizerState;
57 import org.openecomp.sparky.synchronizer.task.PerformActiveInventoryRetrieval;
58 import org.openecomp.sparky.synchronizer.task.PerformElasticSearchPut;
59 import org.openecomp.sparky.synchronizer.task.PerformElasticSearchRetrieval;
60 import org.openecomp.sparky.util.NodeUtils;
61 import org.openecomp.sparky.util.SuggestionsPermutation;
62 import org.slf4j.MDC;
63
64 import com.fasterxml.jackson.core.JsonProcessingException;
65 import com.fasterxml.jackson.databind.JsonNode;
66 import com.fasterxml.jackson.databind.node.ArrayNode;
67
68 /**
69  * The Class AutosuggestionSynchronizer.
70  */
71 public class AutosuggestionSynchronizer extends AbstractEntitySynchronizer
72     implements IndexSynchronizer {
73
74   private class RetrySuggestionEntitySyncContainer {
75     NetworkTransaction txn;
76     SuggestionSearchEntity ssec;
77
78     /**
79      * Instantiates a new RetrySuggestionEntitySyncContainer.
80      *
81      * @param txn the txn
82      * @param icer the icer
83      */
84     public RetrySuggestionEntitySyncContainer(NetworkTransaction txn, SuggestionSearchEntity icer) {
85       this.txn = txn;
86       this.ssec = icer;
87     }
88
89     public NetworkTransaction getNetworkTransaction() {
90       return txn;
91     }
92
93     public SuggestionSearchEntity getSuggestionSearchEntity() {
94       return ssec;
95     }
96   }
97
98   private static final Logger LOG =
99       LoggerFactory.getInstance().getLogger(AutosuggestionSynchronizer.class);
100   private static final String INSERTION_DATE_TIME_FORMAT = "yyyyMMdd'T'HHmmssZ";
101
102   private boolean allWorkEnumerated;
103   private Deque<SelfLinkDescriptor> selflinks;
104   private ConcurrentHashMap<String, AtomicInteger> entityCounters;
105   private boolean syncInProgress;
106   private Map<String, String> contextMap;
107   protected ExecutorService esPutExecutor;
108   private Deque<RetrySuggestionEntitySyncContainer> retryQueue;
109   private Map<String, Integer> retryLimitTracker;
110
111   /**
112    * Instantiates a new historical entity summarizer.
113    *
114    * @param indexName the index name
115    * @throws Exception the exception
116    */
117   public AutosuggestionSynchronizer(String indexName) throws Exception {
118     super(LOG, "ASES-" + indexName.toUpperCase(), 2, 5, 5, indexName); // multiple Autosuggestion
119                                                                        // Entity Synchronizer will
120                                                                        // run for different indices
121
122     this.allWorkEnumerated = false;
123     this.selflinks = new ConcurrentLinkedDeque<SelfLinkDescriptor>();
124     this.entityCounters = new ConcurrentHashMap<String, AtomicInteger>();
125     this.synchronizerName = "Autosuggestion Entity Synchronizer";
126     this.enabledStatFlags = EnumSet.of(StatFlag.AAI_REST_STATS, StatFlag.ES_REST_STATS);
127     this.syncInProgress = false;
128     this.contextMap = MDC.getCopyOfContextMap();
129     this.esPutExecutor = NodeUtils.createNamedExecutor("SUES-ES-PUT", 5, LOG);
130     this.syncDurationInMs = -1;
131   }
132
133   /**
134    * Collect all the work.
135    *
136    * @return the operation state
137    */
138   private OperationState collectAllTheWork() {
139     final Map<String, String> contextMap = MDC.getCopyOfContextMap();
140     Map<String, OxmEntityDescriptor> descriptorMap =
141         oxmModelLoader.getSuggestionSearchEntityDescriptors();
142
143     if (descriptorMap.isEmpty()) {
144       LOG.error(AaiUiMsgs.ERROR_LOADING_OXM_SUGGESTIBLE_ENTITIES);
145       LOG.info(AaiUiMsgs.ERROR_LOADING_OXM_SUGGESTIBLE_ENTITIES);
146       return OperationState.ERROR;
147     }
148
149     Collection<String> syncTypes = descriptorMap.keySet();
150
151     try {
152
153       /*
154        * launch a parallel async thread to process the documents for each entity-type (to max the of
155        * the configured executor anyway)
156        */
157
158       aaiWorkOnHand.set(syncTypes.size());
159
160       for (String key : syncTypes) {
161
162         supplyAsync(new Supplier<Void>() {
163
164           @Override
165           public Void get() {
166             MDC.setContextMap(contextMap);
167             OperationResult typeLinksResult = null;
168             try {
169               typeLinksResult = aaiDataProvider.getSelfLinksByEntityType(key);
170               aaiWorkOnHand.decrementAndGet();
171               processEntityTypeSelfLinks(typeLinksResult);
172             } catch (Exception exc) {
173               // TODO -> LOG, what should be logged here?
174             }
175
176             return null;
177           }
178
179         }, aaiExecutor).whenComplete((result, error) -> {
180
181           if (error != null) {
182             LOG.error(AaiUiMsgs.ERROR_GENERIC,
183                 "An error occurred getting data from AAI. Error = " + error.getMessage());
184           }
185         });
186
187       }
188
189       while (aaiWorkOnHand.get() != 0) {
190
191         if (LOG.isDebugEnabled()) {
192           LOG.debug(AaiUiMsgs.WAIT_FOR_ALL_SELFLINKS_TO_BE_COLLECTED);
193         }
194
195         Thread.sleep(1000);
196       }
197
198       aaiWorkOnHand.set(selflinks.size());
199       allWorkEnumerated = true;
200       syncEntityTypes();
201
202       while (!isSyncDone()) {
203         performRetrySync();
204         Thread.sleep(1000);
205       }
206
207       /*
208        * Make sure we don't hang on to retries that failed which could cause issues during future
209        * syncs
210        */
211       retryLimitTracker.clear();
212
213     } catch (Exception exc) {
214       // TODO -> LOG, waht should be logged here?
215     }
216
217     return OperationState.OK;
218
219   }
220
221   /*
222    * (non-Javadoc)
223    * 
224    * @see org.openecomp.sparky.synchronizer.IndexSynchronizer#doSync()
225    */
226   @Override
227   public OperationState doSync() {
228         this.syncDurationInMs = -1;
229         syncStartedTimeStampInMs = System.currentTimeMillis();
230     String txnID = NodeUtils.getRandomTxnId();
231     MdcContext.initialize(txnID, "AutosuggestionSynchronizer", "", "Sync", "");
232     
233     return collectAllTheWork();
234   }
235
236   /**
237    * Process entity type self links.
238    *
239    * @param operationResult the operation result
240    */
241   private void processEntityTypeSelfLinks(OperationResult operationResult) {
242
243     JsonNode rootNode = null;
244
245     final String jsonResult = operationResult.getResult();
246
247     if (jsonResult != null && jsonResult.length() > 0 && operationResult.wasSuccessful()) {
248
249       try {
250         rootNode = mapper.readTree(jsonResult);
251       } catch (IOException exc) {
252         String message = "Could not deserialize JSON (representing operation result) as node tree. "
253             + "Operation result = " + jsonResult + ". " + exc.getLocalizedMessage();
254         LOG.error(AaiUiMsgs.JSON_PROCESSING_ERROR, message);
255       }
256
257       JsonNode resultData = rootNode.get("result-data");
258       ArrayNode resultDataArrayNode = null;
259
260       if (resultData.isArray()) {
261         resultDataArrayNode = (ArrayNode) resultData;
262
263         Iterator<JsonNode> elementIterator = resultDataArrayNode.elements();
264         JsonNode element = null;
265
266         while (elementIterator.hasNext()) {
267           element = elementIterator.next();
268
269           final String resourceType = NodeUtils.getNodeFieldAsText(element, "resource-type");
270           final String resourceLink = NodeUtils.getNodeFieldAsText(element, "resource-link");
271
272           OxmEntityDescriptor descriptor = null;
273
274           if (resourceType != null && resourceLink != null) {
275
276             descriptor = oxmModelLoader.getEntityDescriptor(resourceType);
277
278             if (descriptor == null) {
279               LOG.error(AaiUiMsgs.MISSING_ENTITY_DESCRIPTOR, resourceType);
280               // go to next element in iterator
281               continue;
282             }
283             selflinks.add(new SelfLinkDescriptor(resourceLink,
284                 SynchronizerConfiguration.NODES_ONLY_MODIFIER, resourceType));
285
286
287           }
288         }
289       }
290     }
291   }
292
293   /**
294    * Sync entity types.
295    */
296   private void syncEntityTypes() {
297
298     while (selflinks.peek() != null) {
299
300       SelfLinkDescriptor linkDescriptor = selflinks.poll();
301       aaiWorkOnHand.decrementAndGet();
302
303       OxmEntityDescriptor descriptor = null;
304
305       if (linkDescriptor.getSelfLink() != null && linkDescriptor.getEntityType() != null) {
306
307         descriptor = oxmModelLoader.getEntityDescriptor(linkDescriptor.getEntityType());
308
309         if (descriptor == null) {
310           LOG.error(AaiUiMsgs.MISSING_ENTITY_DESCRIPTOR, linkDescriptor.getEntityType());
311           // go to next element in iterator
312           continue;
313         }
314
315         NetworkTransaction txn = new NetworkTransaction();
316         txn.setDescriptor(descriptor);
317         txn.setLink(linkDescriptor.getSelfLink());
318         txn.setOperationType(HttpMethod.GET);
319         txn.setEntityType(linkDescriptor.getEntityType());
320
321         aaiWorkOnHand.incrementAndGet();
322
323         supplyAsync(new PerformActiveInventoryRetrieval(txn, aaiDataProvider), aaiExecutor)
324             .whenComplete((result, error) -> {
325
326               aaiWorkOnHand.decrementAndGet();
327
328               if (error != null) {
329                 LOG.error(AaiUiMsgs.AAI_RETRIEVAL_FAILED_GENERIC, error.getLocalizedMessage());
330               } else {
331                 if (result == null) {
332                   LOG.error(AaiUiMsgs.AAI_RETRIEVAL_FAILED_FOR_SELF_LINK,
333                       linkDescriptor.getSelfLink());
334                 } else {
335                   updateActiveInventoryCounters(result);
336                   fetchDocumentForUpsert(result);
337                 }
338               }
339             });
340       }
341
342     }
343
344   }
345   /*
346    * Return a set of valid suggestion attributes for the provided entityName 
347    * that are present in the JSON
348    * @param node    JSON node in which the attributes should be found
349    * @param entityName  Name of the entity
350    * @return List of all valid suggestion attributes(key's)
351    */
352   public List<String> getSuggestionFromReponse(JsonNode node, String entityName) {
353     List<String> suggestableAttr = new ArrayList<String>();
354     HashMap<String, String> desc = oxmModelLoader.getOxmModel().get(entityName);
355     String attr = desc.get("suggestibleAttributes");
356     suggestableAttr = Arrays.asList(attr.split(","));
357     List<String> suggestableValue = new ArrayList<>();
358     for (String attribute : suggestableAttr) {
359       if (node.get(attribute) != null && node.get(attribute).asText().length() > 0) {
360         suggestableValue.add(attribute);
361       }
362     }
363     return suggestableValue;
364   }
365
366   /**
367    * Fetch all the documents for upsert. Based on the number of permutations that are available the
368    * number of documents will be different
369    *
370    * @param txn the txn
371    */
372   private void fetchDocumentForUpsert(NetworkTransaction txn) {
373     if (!txn.getOperationResult().wasSuccessful()) {
374       String message = "Self link failure. Result - " + txn.getOperationResult().getResult();
375       LOG.error(AaiUiMsgs.ERROR_GENERIC, message);
376       return;
377     }
378     try {
379       final String jsonResult = txn.getOperationResult().getResult();
380
381       if (jsonResult != null && jsonResult.length() > 0) {
382
383         // Step 1: Calculate the number of possible permutations of attributes
384         String entityName = txn.getDescriptor().getEntityName();
385         JsonNode entityNode = mapper.readTree(jsonResult);
386
387         SuggestionsPermutation suggPermutation = new SuggestionsPermutation();
388         ArrayList<ArrayList<String>> uniqueLists = suggPermutation
389             .getSuggestionsPermutation(getSuggestionFromReponse(entityNode, entityName));
390
391         // Now we have a list of all possible permutations for the status that are
392         // defined for this entity type. Try inserting a document for every combination.
393         for (ArrayList<String> uniqueList : uniqueLists) {
394           SuggestionSearchEntity sse = new SuggestionSearchEntity(oxmModelLoader);
395           sse.setSuggestableAttr(uniqueList);
396           sse.setPayloadFromResponse(entityNode);
397           sse.setLink(txn.getLink());
398           sse.setLink(ActiveInventoryConfig.extractResourcePath(txn.getLink()));
399           populateSuggestionSearchEntityDocument(sse, jsonResult, txn);
400           // The unique id for the document will be created at derive fields
401           sse.deriveFields();
402           // Insert the document only if it has valid statuses
403           if (sse.isSuggestableDoc()) {
404             String link = null;
405             try {
406               link = getElasticFullUrl("/" + sse.getId(), getIndexName());
407             } catch (Exception exc) {
408               LOG.error(AaiUiMsgs.ES_FAILED_TO_CONSTRUCT_QUERY, exc.getLocalizedMessage());
409             }
410
411             if (link != null) {
412               NetworkTransaction n2 = new NetworkTransaction();
413               n2.setLink(link);
414               n2.setEntityType(txn.getEntityType());
415               n2.setDescriptor(txn.getDescriptor());
416               n2.setOperationType(HttpMethod.GET);
417
418               esWorkOnHand.incrementAndGet();
419
420               supplyAsync(new PerformElasticSearchRetrieval(n2, esDataProvider), esExecutor)
421                   .whenComplete((result, error) -> {
422
423                     esWorkOnHand.decrementAndGet();
424
425                     if (error != null) {
426                       LOG.error(AaiUiMsgs.ES_RETRIEVAL_FAILED, error.getLocalizedMessage());
427                     } else {
428                       updateElasticSearchCounters(result);
429                       performDocumentUpsert(result, sse);
430                     }
431                   });
432             }
433           }
434         }
435       }
436     } catch (JsonProcessingException exc) {
437       // TODO -> LOG, waht should be logged here?
438     } catch (IOException exc) {
439       // TODO -> LOG, waht should be logged here?
440     }
441   }
442
443   protected void populateSuggestionSearchEntityDocument(SuggestionSearchEntity sse, String result,
444       NetworkTransaction txn) throws JsonProcessingException, IOException {
445
446     OxmEntityDescriptor resultDescriptor = txn.getDescriptor();
447
448     sse.setEntityType(resultDescriptor.getEntityName());
449
450     JsonNode entityNode = mapper.readTree(result);
451
452     List<String> primaryKeyValues = new ArrayList<String>();
453     String pkeyValue = null;
454
455     for (String keyName : resultDescriptor.getPrimaryKeyAttributeName()) {
456       pkeyValue = NodeUtils.getNodeFieldAsText(entityNode, keyName);
457       if (pkeyValue != null) {
458         primaryKeyValues.add(pkeyValue);
459       } else {
460         String message = "populateSuggestionSearchEntityDocument(),"
461             + " pKeyValue is null for entityType = " + resultDescriptor.getEntityName();
462         LOG.warn(AaiUiMsgs.WARN_GENERIC, message);
463       }
464     }
465
466     final String primaryCompositeKeyValue = NodeUtils.concatArray(primaryKeyValues, "/");
467     sse.setEntityPrimaryKeyValue(primaryCompositeKeyValue);
468     sse.generateSuggestionInputPermutations();
469   }
470
471   protected void performDocumentUpsert(NetworkTransaction esGetTxn, SuggestionSearchEntity sse) {
472     /**
473      * <p>
474      * <ul>
475      * As part of the response processing we need to do the following:
476      * <li>1. Extract the version (if present), it will be the ETAG when we use the
477      * Search-Abstraction-Service
478      * <li>2. Spawn next task which is to do the PUT operation into elastic with or with the version
479      * tag
480      * <li>a) if version is null or RC=404, then standard put, no _update with version tag
481      * <li>b) if version != null, do PUT with _update?version= versionNumber in the URI to elastic
482      * </ul>
483      * </p>
484      */
485     String link = null;
486     try {
487       link = getElasticFullUrl("/" + sse.getId(), getIndexName());
488     } catch (Exception exc) {
489       LOG.error(AaiUiMsgs.ES_LINK_UPSERT, exc.getLocalizedMessage());
490       return;
491     }
492
493     boolean wasEntryDiscovered = false;
494     if (esGetTxn.getOperationResult().getResultCode() == 404) {
495       LOG.info(AaiUiMsgs.ES_SIMPLE_PUT, sse.getEntityPrimaryKeyValue());
496     } else if (esGetTxn.getOperationResult().getResultCode() == 200) {
497       wasEntryDiscovered = true;
498     } else {
499       /*
500        * Not being a 200 does not mean a failure. eg 201 is returned for created. and 500 for es not
501        * found TODO -> Should we return.
502        */
503       LOG.error(AaiUiMsgs.ES_OPERATION_RETURN_CODE,
504           String.valueOf(esGetTxn.getOperationResult().getResultCode()));
505       return;
506     }
507     // Insert a new document only if the paylod is different.
508     // This is determined by hashing the payload and using it as a id for the document
509     //
510     if (!wasEntryDiscovered) {
511       try {
512         String jsonPayload = null;
513
514         jsonPayload = sse.getIndexDocumentJson();
515         if (link != null && jsonPayload != null) {
516
517           NetworkTransaction updateElasticTxn = new NetworkTransaction();
518           updateElasticTxn.setLink(link);
519           updateElasticTxn.setEntityType(esGetTxn.getEntityType());
520           updateElasticTxn.setDescriptor(esGetTxn.getDescriptor());
521           updateElasticTxn.setOperationType(HttpMethod.PUT);
522
523           esWorkOnHand.incrementAndGet();
524           supplyAsync(new PerformElasticSearchPut(jsonPayload, updateElasticTxn, esDataProvider),
525               esPutExecutor).whenComplete((result, error) -> {
526
527                 esWorkOnHand.decrementAndGet();
528
529                 if (error != null) {
530                   String message = "Suggestion search entity sync UPDATE PUT error - "
531                       + error.getLocalizedMessage();
532                   LOG.error(AaiUiMsgs.ES_SUGGESTION_SEARCH_ENTITY_SYNC_ERROR, message);
533                 } else {
534                   updateElasticSearchCounters(result);
535                   processStoreDocumentResult(result, esGetTxn, sse);
536                 }
537               });
538         }
539       } catch (Exception exc) {
540         String message =
541             "Exception caught during suggestion search entity sync PUT operation. Message - "
542                 + exc.getLocalizedMessage();
543         LOG.error(AaiUiMsgs.ES_SUGGESTION_SEARCH_ENTITY_SYNC_ERROR, message);
544       }
545     }
546   }
547
548   private void processStoreDocumentResult(NetworkTransaction esPutResult,
549       NetworkTransaction esGetResult, SuggestionSearchEntity sse) {
550
551     OperationResult or = esPutResult.getOperationResult();
552
553     if (!or.wasSuccessful()) {
554       if (or.getResultCode() == VERSION_CONFLICT_EXCEPTION_CODE) {
555
556         if (shouldAllowRetry(sse.getId())) {
557           esWorkOnHand.incrementAndGet();
558
559           RetrySuggestionEntitySyncContainer rssec =
560               new RetrySuggestionEntitySyncContainer(esGetResult, sse);
561           retryQueue.push(rssec);
562
563           String message = "Store document failed during suggestion search entity synchronization"
564               + " due to version conflict. Entity will be re-synced.";
565           LOG.warn(AaiUiMsgs.ES_SUGGESTION_SEARCH_ENTITY_SYNC_ERROR, message);
566         }
567       } else {
568         String message =
569             "Store document failed during suggestion search entity synchronization with result code "
570                 + or.getResultCode() + " and result message " + or.getResult();
571         LOG.error(AaiUiMsgs.ES_SUGGESTION_SEARCH_ENTITY_SYNC_ERROR, message);
572       }
573     }
574   }
575
576   /**
577    * Perform retry sync.
578    */
579   private void performRetrySync() {
580     while (retryQueue.peek() != null) {
581
582       RetrySuggestionEntitySyncContainer susc = retryQueue.poll();
583       if (susc != null) {
584
585         SuggestionSearchEntity sus = susc.getSuggestionSearchEntity();
586         NetworkTransaction txn = susc.getNetworkTransaction();
587
588         String link = null;
589         try {
590           /*
591            * In this retry flow the se object has already derived its fields
592            */
593           link = getElasticFullUrl("/" + sus.getId(), getIndexName());
594         } catch (Exception exc) {
595           LOG.error(AaiUiMsgs.ES_FAILED_TO_CONSTRUCT_URI, exc.getLocalizedMessage());
596         }
597
598         if (link != null) {
599           NetworkTransaction retryTransaction = new NetworkTransaction();
600           retryTransaction.setLink(link);
601           retryTransaction.setEntityType(txn.getEntityType());
602           retryTransaction.setDescriptor(txn.getDescriptor());
603           retryTransaction.setOperationType(HttpMethod.GET);
604
605           /*
606            * IMPORTANT - DO NOT incrementAndGet the esWorkOnHand as this is a retry flow! We already
607            * called incrementAndGet when queuing the failed PUT!
608            */
609
610           supplyAsync(new PerformElasticSearchRetrieval(retryTransaction, esDataProvider),
611               esExecutor).whenComplete((result, error) -> {
612
613                 esWorkOnHand.decrementAndGet();
614
615                 if (error != null) {
616                   LOG.error(AaiUiMsgs.ES_RETRIEVAL_FAILED_RESYNC, error.getLocalizedMessage());
617                 } else {
618                   updateElasticSearchCounters(result);
619                   performDocumentUpsert(result, sus);
620                 }
621               });
622         }
623
624       }
625     }
626   }
627
628   /**
629    * Should allow retry.
630    *
631    * @param id the id
632    * @return true, if successful
633    */
634   private boolean shouldAllowRetry(String id) {
635     boolean isRetryAllowed = true;
636     if (retryLimitTracker.get(id) != null) {
637       Integer currentCount = retryLimitTracker.get(id);
638       if (currentCount.intValue() >= RETRY_COUNT_PER_ENTITY_LIMIT.intValue()) {
639         isRetryAllowed = false;
640         String message = "Searchable entity re-sync limit reached for " + id
641             + ", re-sync will no longer be attempted for this entity";
642         LOG.error(AaiUiMsgs.ES_SEARCHABLE_ENTITY_SYNC_ERROR, message);
643       } else {
644         Integer newCount = new Integer(currentCount.intValue() + 1);
645         retryLimitTracker.put(id, newCount);
646       }
647     } else {
648       Integer firstRetryCount = new Integer(1);
649       retryLimitTracker.put(id, firstRetryCount);
650     }
651
652     return isRetryAllowed;
653   }
654
655
656
657   @Override
658   public SynchronizerState getState() {
659
660     if (!isSyncDone()) {
661       return SynchronizerState.PERFORMING_SYNCHRONIZATION;
662     }
663
664     return SynchronizerState.IDLE;
665
666   }
667
668   /*
669    * (non-Javadoc)
670    * 
671    * @see org.openecomp.sparky.synchronizer.IndexSynchronizer#getStatReport(boolean)
672    */
673   @Override
674   public String getStatReport(boolean showFinalReport) {
675           syncDurationInMs = System.currentTimeMillis() - syncStartedTimeStampInMs;
676           return getStatReport(syncDurationInMs, showFinalReport);
677   }
678
679   /*
680    * (non-Javadoc)
681    * 
682    * @see org.openecomp.sparky.synchronizer.IndexSynchronizer#shutdown()
683    */
684   @Override
685   public void shutdown() {
686     this.shutdownExecutors();
687   }
688
689   @Override
690   protected boolean isSyncDone() {
691
692     int totalWorkOnHand = aaiWorkOnHand.get() + esWorkOnHand.get();
693
694     if (LOG.isDebugEnabled()) {
695       LOG.debug(AaiUiMsgs.DEBUG_GENERIC, indexName + ", isSyncDone(), totalWorkOnHand = "
696           + totalWorkOnHand + " all work enumerated = " + allWorkEnumerated);
697     }
698
699     if (totalWorkOnHand > 0 || !allWorkEnumerated) {
700       return false;
701     }
702
703     this.syncInProgress = false;
704
705     return true;
706   }
707
708   /*
709    * (non-Javadoc)
710    * 
711    * @see org.openecomp.sparky.synchronizer.AbstractEntitySynchronizer#clearCache()
712    */
713   @Override
714   public void clearCache() {
715
716     if (syncInProgress) {
717       LOG.debug(AaiUiMsgs.DEBUG_GENERIC,
718           "Autosuggestion Entity Summarizer in progress, request to clear cache ignored");
719       return;
720     }
721
722     super.clearCache();
723     this.resetCounters();
724     if (entityCounters != null) {
725       entityCounters.clear();
726     }
727
728     allWorkEnumerated = false;
729
730   }
731
732 }