diff options
author | andre.schmid <andre.schmid@est.tech> | 2021-04-22 12:33:04 +0100 |
---|---|---|
committer | Christophe Closset <christophe.closset@intl.att.com> | 2021-04-29 15:15:34 +0000 |
commit | d24a4f0aa92d6ea0c83d82bf2448c43d8ebdddaa (patch) | |
tree | c468f0bd65706a445dbedac354b40ce3621b0e60 | |
parent | 116c4a25d915411617a3382db47e56930fb6a3ba (diff) |
Refactor data types cache
Avoids potential issue of data type cache changes by external parties,
by returning copies from the cache instead of the original.
Refactors the code for more clarity.
Change-Id: Ibb518bf638f2f4ee1f5e3869baaace374efb632a
Issue-ID: SDC-3569
Signed-off-by: André Schmid <andre.schmid@est.tech>
7 files changed, 446 insertions, 292 deletions
diff --git a/catalog-model/pom.xml b/catalog-model/pom.xml index 17b1a0e30b..7666076de6 100644 --- a/catalog-model/pom.xml +++ b/catalog-model/pom.xml @@ -13,6 +13,10 @@ <version>1.9.0-SNAPSHOT</version> </parent> + <properties> + <awaitility.version>4.0.3</awaitility.version> + </properties> + <dependencies> <dependency> <groupId>com.fasterxml.jackson.core</groupId> @@ -298,6 +302,13 @@ </dependency> <dependency> + <groupId>org.awaitility</groupId> + <artifactId>awaitility</artifactId> + <version>${awaitility.version}</version> + <scope>test</scope> + </dependency> + + <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy</artifactId> </dependency> diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/DataTypeDefinition.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/DataTypeDefinition.java index 6c285802d2..b61aa5a4c2 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/DataTypeDefinition.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/DataTypeDefinition.java @@ -37,17 +37,18 @@ public class DataTypeDefinition extends DataTypeDataDefinition { private List<PropertyConstraint> constraints; private List<PropertyDefinition> properties; - public DataTypeDefinition(DataTypeDataDefinition p) { - super(p); + public DataTypeDefinition(final DataTypeDataDefinition dataTypeDataDefinition) { + super(dataTypeDataDefinition); } - public DataTypeDefinition(DataTypeDefinition pd) { - this.setName(pd.getName()); - this.setDerivedFrom(pd.getDerivedFrom()); - this.setDerivedFromName(pd.getDerivedFromName()); - this.setUniqueId(pd.getUniqueId()); - this.setConstraints(pd.getConstraints()); - this.setDescription(pd.getDescription()); + public DataTypeDefinition(final DataTypeDefinition dataTypeDefinition) { + super(dataTypeDefinition); + this.setName(dataTypeDefinition.getName()); + this.setDerivedFrom(dataTypeDefinition.getDerivedFrom()); + this.setDerivedFromName(dataTypeDefinition.getDerivedFromName()); + this.setUniqueId(dataTypeDefinition.getUniqueId()); + this.setConstraints(dataTypeDefinition.getConstraints()); + this.setDescription(dataTypeDefinition.getDescription()); } public List<PropertyConstraint> safeGetConstraints() { diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/ApplicationCache.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/ApplicationCache.java index 4c58285c12..d0a071909c 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/ApplicationCache.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/ApplicationCache.java @@ -25,7 +25,7 @@ import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus; public interface ApplicationCache<T> { - public abstract Either<Map<String, T>, JanusGraphOperationStatus> getAll(); + Either<Map<String, T>, JanusGraphOperationStatus> getAll(); - public abstract Either<T, JanusGraphOperationStatus> get(String uniqueId); + Either<T, JanusGraphOperationStatus> get(String uniqueId); } diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/ApplicationDataTypeCache.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/ApplicationDataTypeCache.java index d6cc01b3ef..a1bafbc6f2 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/ApplicationDataTypeCache.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/ApplicationDataTypeCache.java @@ -20,36 +20,33 @@ package org.openecomp.sdc.be.model.cache; import fj.data.Either; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; +import java.util.Optional; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.stream.Collectors; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; -import javax.annotation.Resource; +import lombok.AccessLevel; import lombok.Getter; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.concurrent.BasicThreadFactory; -import org.apache.commons.lang3.tuple.ImmutablePair; import org.openecomp.sdc.be.config.BeEcompErrorManager; import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity; -import org.openecomp.sdc.be.config.Configuration.ApplicationL1CacheConfig; import org.openecomp.sdc.be.config.Configuration.ApplicationL1CacheInfo; import org.openecomp.sdc.be.config.ConfigurationManager; import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus; -import org.openecomp.sdc.be.datatypes.elements.DataTypeDataDefinition; import org.openecomp.sdc.be.model.DataTypeDefinition; import org.openecomp.sdc.be.model.operations.impl.PropertyOperation; import org.openecomp.sdc.be.resources.data.DataTypeData; +import org.openecomp.sdc.common.log.enums.EcompLoggerErrorCode; import org.openecomp.sdc.common.log.wrappers.Logger; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Component; @@ -58,49 +55,68 @@ import org.springframework.stereotype.Component; public class ApplicationDataTypeCache implements ApplicationCache<DataTypeDefinition>, Runnable { private static final String APPLICATION_DATA_TYPES_CACHE = "ApplicationDataTypesCache"; - private static final Logger log = Logger.getLogger(ApplicationDataTypeCache.class.getName()); - private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); - private final Lock r = rwl.readLock(); - private final Lock w = rwl.writeLock(); - ScheduledFuture<?> scheduledFuture = null; - private Map<String, DataTypeDefinition> data = new HashMap<>(); - private ScheduledExecutorService scheduledPollingService = Executors - .newScheduledThreadPool(1, new BasicThreadFactory.Builder().namingPattern("ApplicationDataTypeCacheThread-%d").build()); + private static final Logger log = Logger.getLogger(ApplicationDataTypeCache.class); + + private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); + private final PropertyOperation propertyOperation; + private final ApplicationEventPublisher applicationEventPublisher; + @Getter(AccessLevel.PACKAGE) + private final ScheduledExecutorService scheduledPollingService; + @Getter(AccessLevel.PACKAGE) + private ScheduledFuture<?> scheduledFuture = null; + private Map<String, DataTypeDefinition> dataTypesCacheMap = new HashMap<>(); private int firstRunDelayInSec = 30; private int pollingIntervalInSec = 60; - @Resource - private PropertyOperation propertyOperation; - @Autowired - private ApplicationEventPublisher applicationEventPublisher; + + public ApplicationDataTypeCache(final PropertyOperation propertyOperation, final ApplicationEventPublisher applicationEventPublisher) { + this.propertyOperation = propertyOperation; + this.applicationEventPublisher = applicationEventPublisher; + scheduledPollingService = Executors + .newScheduledThreadPool(1, new BasicThreadFactory.Builder().namingPattern("ApplicationDataTypeCacheThread-%d").build()); + } @PostConstruct - public void init() { - ApplicationL1CacheConfig applicationL1CacheConfig = ConfigurationManager.getConfigurationManager().getConfiguration().getApplicationL1Cache(); - if (applicationL1CacheConfig != null) { - if (applicationL1CacheConfig.getDatatypes() != null) { - ApplicationL1CacheInfo datatypesInfo = applicationL1CacheConfig.getDatatypes(); - if (datatypesInfo.getEnabled()) { - Integer intervalInSec = datatypesInfo.getPollIntervalInSec(); - if (intervalInSec != null) { - pollingIntervalInSec = intervalInSec; - } - Integer firstRunDelay = datatypesInfo.getFirstRunDelay(); - if (firstRunDelay != null) { - firstRunDelayInSec = firstRunDelay; - } - log.trace("ApplicationDataTypesCache polling interval is {} seconds.", pollingIntervalInSec); - if (scheduledPollingService != null) { - log.debug("Start ApplicationDataTypeCache polling task. polling interval {} seconds", pollingIntervalInSec); - scheduledFuture = scheduledPollingService - .scheduleAtFixedRate(this, firstRunDelayInSec, pollingIntervalInSec, TimeUnit.SECONDS); - } - } - } else { - BeEcompErrorManager.getInstance().logInternalFlowError(APPLICATION_DATA_TYPES_CACHE, "Cache is disabled", ErrorSeverity.INFO); - } - } else { - BeEcompErrorManager.getInstance().logInternalFlowError(APPLICATION_DATA_TYPES_CACHE, "Cache is disabled", ErrorSeverity.INFO); + void init() { + final Optional<ApplicationL1CacheInfo> dataTypeCacheConfigOptional = getDataTypeCacheConfig(); + if (dataTypeCacheConfigOptional.isEmpty()) { + BeEcompErrorManager.getInstance() + .logInternalFlowError(APPLICATION_DATA_TYPES_CACHE, "Data types cache is not configured and will be disabled", ErrorSeverity.INFO); + return; + } + final ApplicationL1CacheInfo dataTypesCacheInfo = dataTypeCacheConfigOptional.get(); + if (!Boolean.TRUE.equals(dataTypesCacheInfo.getEnabled())) { + BeEcompErrorManager.getInstance().logInternalFlowError(APPLICATION_DATA_TYPES_CACHE, "Data types cache is disabled", ErrorSeverity.INFO); + return; + } + loadConfigurationValues(dataTypesCacheInfo); + if (scheduledPollingService != null) { + log.debug("Starting ApplicationDataTypeCache polling task. Initial delay {}s and polling interval {}s", + firstRunDelayInSec, pollingIntervalInSec); + scheduledFuture = scheduledPollingService + .scheduleAtFixedRate(this, firstRunDelayInSec, pollingIntervalInSec, TimeUnit.SECONDS); + } + } + + private void loadConfigurationValues(final ApplicationL1CacheInfo dataTypesCacheInfo) { + final Integer firstRunDelay = dataTypesCacheInfo.getFirstRunDelay(); + if (firstRunDelay != null) { + firstRunDelayInSec = firstRunDelay; + } + log.trace("ApplicationDataTypesCache initial delay configured to {} seconds.", firstRunDelayInSec); + + final Integer intervalInSec = dataTypesCacheInfo.getPollIntervalInSec(); + if (intervalInSec != null) { + pollingIntervalInSec = intervalInSec; } + log.trace("ApplicationDataTypesCache polling interval configured to {} seconds.", pollingIntervalInSec); + } + + private Optional<ApplicationL1CacheInfo> getDataTypeCacheConfig() { + final var applicationL1CacheConfig = ConfigurationManager.getConfigurationManager().getConfiguration().getApplicationL1Cache(); + if (applicationL1CacheConfig == null || applicationL1CacheConfig.getDatatypes() == null) { + return Optional.empty(); + } + return Optional.ofNullable(applicationL1CacheConfig.getDatatypes()); } @PreDestroy @@ -147,138 +163,164 @@ public class ApplicationDataTypeCache implements ApplicationCache<DataTypeDefini @Override public Either<Map<String, DataTypeDefinition>, JanusGraphOperationStatus> getAll() { try { - r.lock(); - if (data == null || data.isEmpty()) { + readWriteLock.readLock().lock(); + if (MapUtils.isEmpty(dataTypesCacheMap)) { return getAllDataTypesFromGraph(); } - return Either.left(data); + return Either.left(new HashMap<>(dataTypesCacheMap)); } finally { - r.unlock(); + readWriteLock.readLock().unlock(); } } @Override public Either<DataTypeDefinition, JanusGraphOperationStatus> get(String uniqueId) { try { - r.lock(); - if (data == null || data.isEmpty()) { + readWriteLock.readLock().lock(); + if (MapUtils.isEmpty(dataTypesCacheMap)) { + return propertyOperation.getDataTypeByUid(uniqueId); + } + + final Optional<DataTypeDefinition> dataTypeDefinition = dataTypesCacheMap.values().stream() + .filter(p -> p.getUniqueId().equals(uniqueId)).findFirst(); + if (dataTypeDefinition.isEmpty()) { return propertyOperation.getDataTypeByUid(uniqueId); - } else { - DataTypeDefinition dataTypeDefinition = data.values().stream().filter(p -> p.getUniqueId().equals(uniqueId)).findFirst().orElse(null); - if (dataTypeDefinition == null) { - return propertyOperation.getDataTypeByUid(uniqueId); - } else { - return Either.left(dataTypeDefinition); - } } + return Either.left(new DataTypeDefinition(dataTypeDefinition.get())); } finally { - r.unlock(); + readWriteLock.readLock().unlock(); } } @Override public void run() { - log.trace("run() method. polling db to fetch data types"); try { - Long start = System.currentTimeMillis(); - log.trace("Start fetching all data types from db"); - Either<List<DataTypeData>, JanusGraphOperationStatus> allDataTypeNodes = propertyOperation.getAllDataTypeNodes(); - Long end = System.currentTimeMillis(); - log.trace("Finish fetching all data types from db. Took {} Milliseconds", (end - start)); - if (allDataTypeNodes.isRight()) { - JanusGraphOperationStatus status = allDataTypeNodes.right().value(); - if (status != JanusGraphOperationStatus.OK) { - log.debug("ApplicationDataTypesCache - Failed to fetch all data types nodes"); - BeEcompErrorManager.getInstance() - .logInternalConnectionError("FetchDataTypes", "Failed to fetch data types from graph(cache)", ErrorSeverity.INFO); - } - } else { - List<DataTypeData> list = allDataTypeNodes.left().value(); - if (list != null) { - Map<String, ImmutablePair<Long, Long>> dataTypeNameToModificationTime = list.stream().collect(Collectors - .toMap(p -> p.getDataTypeDataDefinition().getName(), p -> new ImmutablePair<>(p.getDataTypeDataDefinition().getCreationTime(), - p.getDataTypeDataDefinition().getModificationTime()))); - Map<String, ImmutablePair<Long, Long>> currentDataTypeToModificationTime = new HashMap<>(); - try { - r.lock(); - if (data != null) { - currentDataTypeToModificationTime = data.values().stream().collect(Collectors - .toMap(DataTypeDataDefinition::getName, p -> new ImmutablePair<>(p.getCreationTime(), p.getModificationTime()))); - } - } finally { - r.unlock(); - } - boolean isChanged = compareDataTypes(dataTypeNameToModificationTime, currentDataTypeToModificationTime); - if (isChanged) { - replaceAllData(); - } - } + final long startTime = System.currentTimeMillis(); + log.trace("Starting refresh data types cache job"); + if (hasDataTypesChanged()) { + log.info("Detected changes in the data types, updating the data type cache."); + refreshDataTypesCache(); } - } catch (Exception e) { - log.debug("unexpected error occured", e); - BeEcompErrorManager.getInstance() - .logInternalUnexpectedError(APPLICATION_DATA_TYPES_CACHE, "Failed to run refresh data types job", ErrorSeverity.INFO); + log.trace("Finished refresh data types cache job. Finished in {}ms", (System.currentTimeMillis() - startTime)); + } catch (final Exception e) { + var errorMsg = "Failed to run refresh data types cache job"; + log.error(EcompLoggerErrorCode.UNKNOWN_ERROR, ApplicationDataTypeCache.class.getName(), errorMsg, e); + BeEcompErrorManager.getInstance().logInternalUnexpectedError(APPLICATION_DATA_TYPES_CACHE, errorMsg, ErrorSeverity.INFO); } finally { try { propertyOperation.getJanusGraphGenericDao().commit(); - } catch (Exception e) { - log.trace("Failed to commit ApplicationDataTypeCache", e); + } catch (final Exception e) { + log.error(EcompLoggerErrorCode.UNKNOWN_ERROR, ApplicationDataTypeCache.class.getName(), + "Failed to commit ApplicationDataTypeCache", e); } } } - private boolean compareDataTypes(Map<String, ImmutablePair<Long, Long>> dataTypeNameToModificationTime, - Map<String, ImmutablePair<Long, Long>> currentDataTypeToModificationTime) { - if (dataTypeNameToModificationTime.size() != currentDataTypeToModificationTime.size()) { + private boolean hasDataTypesChanged() { + final List<DataTypeData> dataTypeListFromDatabase = findAllDataTypesLazy(); + final Map<String, DataTypeDefinition> dataTypesCacheCopyMap = copyDataTypeCache(); + + if (dataTypeListFromDatabase.size() != dataTypesCacheCopyMap.size()) { + log.debug("Total of cached data types '{}' differs from the actual '{}'", dataTypeListFromDatabase.size(), dataTypesCacheCopyMap.size()); return true; - } else { - Set<String> currentkeySet = currentDataTypeToModificationTime.keySet(); - Set<String> keySet = dataTypeNameToModificationTime.keySet(); - if (currentkeySet.containsAll(keySet)) { - for (Entry<String, ImmutablePair<Long, Long>> entry : dataTypeNameToModificationTime.entrySet()) { - String dataTypeName = entry.getKey(); - ImmutablePair<Long, Long> creationAndModificationTimes = entry.getValue(); - long creationTime = creationAndModificationTimes.getLeft() == null ? 0 : creationAndModificationTimes.getLeft().longValue(); - long modificationTime = creationAndModificationTimes.getRight() == null ? 0 : creationAndModificationTimes.getRight().longValue(); - ImmutablePair<Long, Long> currentEntry = currentDataTypeToModificationTime.get(dataTypeName); - long currentCreationTime = currentEntry.getLeft() == null ? 0 : currentEntry.getLeft().longValue(); - long currentModificationTime = currentEntry.getRight() == null ? 0 : currentEntry.getRight().longValue(); - if (creationTime > currentCreationTime || modificationTime > currentModificationTime) { - log.debug("Datatype {} was updated. Creation Time {} vs {}. Modification Time {} vs {}", dataTypeName, currentCreationTime, - creationTime, currentModificationTime, modificationTime); - return true; - } - } - } else { + } + + if (CollectionUtils.isEmpty(dataTypeListFromDatabase)) { + log.debug("Both data type cache and database are empty"); + return false; + } + + return hasDataTypesChanged(dataTypeListFromDatabase, dataTypesCacheCopyMap); + } + + private boolean hasDataTypesChanged(final List<DataTypeData> dataTypeListFromDatabase, final Map<String, DataTypeDefinition> dataTypesCacheCopyMap) { + return dataTypeListFromDatabase.stream().map(DataTypeData::getDataTypeDataDefinition).anyMatch(actualDataTypeDefinition -> { + final String dataTypeName = actualDataTypeDefinition.getName(); + final DataTypeDefinition cachedDataTypeDefinition = dataTypesCacheCopyMap.get(dataTypeName); + if (cachedDataTypeDefinition == null) { + log.debug("Datatype '{}' is not present in the cache. ", dataTypeName); return true; } + + final long cachedCreationTime = cachedDataTypeDefinition.getCreationTime() == null ? 0 : cachedDataTypeDefinition.getCreationTime(); + final long actualCreationTime = actualDataTypeDefinition.getCreationTime() == null ? 0 : actualDataTypeDefinition.getCreationTime(); + if (cachedCreationTime != actualCreationTime) { + log.debug("Datatype '{}' was updated. Cache/database creation time '{}'/'{}'.", + dataTypeName, cachedCreationTime, actualCreationTime); + return true; + } + final long cachedModificationTime = + cachedDataTypeDefinition.getModificationTime() == null ? 0 : cachedDataTypeDefinition.getModificationTime(); + final long actualModificationTime = + actualDataTypeDefinition.getModificationTime() == null ? 0 : actualDataTypeDefinition.getModificationTime(); + if (cachedModificationTime != actualModificationTime) { + log.debug("Datatype '{}' was updated. Cache/database modification time '{}'/'{}'.", + dataTypeName, cachedModificationTime, actualModificationTime); + return true; + } + + return false; + }); + } + + private Map<String, DataTypeDefinition> copyDataTypeCache() { + try { + readWriteLock.readLock().lock(); + return new HashMap<>(this.dataTypesCacheMap); + } finally { + readWriteLock.readLock().unlock(); } - return false; } - private void replaceAllData() { - Either<Map<String, DataTypeDefinition>, JanusGraphOperationStatus> allDataTypes = propertyOperation.getAllDataTypes(); + private void refreshDataTypesCache() { + final Map<String, DataTypeDefinition> dataTypesDefinitionMap = findAllDataTypesEager(); + if (dataTypesDefinitionMap.isEmpty()) { + return; + } + try { + readWriteLock.writeLock().lock(); + dataTypesCacheMap = dataTypesDefinitionMap; + onDataChangeEventEmit(); + BeEcompErrorManager.getInstance() + .logInternalFlowError("ReplaceDataTypesCache", "Succeed to replace the data types cache", ErrorSeverity.INFO); + } finally { + readWriteLock.writeLock().unlock(); + } + } + + private Map<String, DataTypeDefinition> findAllDataTypesEager() { + log.trace("Fetching data types from database, eager mode"); + final long startTime = System.currentTimeMillis(); + final Either<Map<String, DataTypeDefinition>, JanusGraphOperationStatus> allDataTypes = propertyOperation.getAllDataTypes(); + log.trace("Finish fetching data types from database. Took {}ms", (System.currentTimeMillis() - startTime)); if (allDataTypes.isRight()) { - JanusGraphOperationStatus status = allDataTypes.right().value(); - log.debug("Failed to fetch all data types from db. Status is {}", status); - } else { - try { - w.lock(); - data = allDataTypes.left().value(); - // send notification on data types change - onDataChangeEventEmit(data); - BeEcompErrorManager.getInstance() - .logInternalFlowError("ReplaceDataTypesCache", "Succeed to replace the data types cache", ErrorSeverity.INFO); - } finally { - w.unlock(); - } + final JanusGraphOperationStatus status = allDataTypes.right().value(); + var errorMsg= String.format("Failed to fetch data types from database. Status is %s", status); + log.error(EcompLoggerErrorCode.UNKNOWN_ERROR, ApplicationDataTypeCache.class.getName(), errorMsg); + BeEcompErrorManager.getInstance().logInternalConnectionError(APPLICATION_DATA_TYPES_CACHE, errorMsg, ErrorSeverity.ERROR); + return Collections.emptyMap(); } + return allDataTypes.left().value(); } - private void onDataChangeEventEmit(Map<String, DataTypeDefinition> newData) { - log.trace("Cache data has changed, sending event to all listening for this change."); - DataTypesCacheChangedEvent dataTypesCacheChangedEvent = new DataTypesCacheChangedEvent(this, newData); - applicationEventPublisher.publishEvent(dataTypesCacheChangedEvent); + private List<DataTypeData> findAllDataTypesLazy() { + log.trace("Fetching data types from database, lazy mode"); + final long startTime = System.currentTimeMillis(); + final Either<List<DataTypeData>, JanusGraphOperationStatus> allDataTypes = propertyOperation.getAllDataTypeNodes(); + log.trace("Finish fetching data types from database. Took {}ms", (System.currentTimeMillis() - startTime)); + if (allDataTypes.isRight()) { + final JanusGraphOperationStatus status = allDataTypes.right().value(); + var errorMsg= String.format("Failed to fetch data types from database. Status is %s", status); + log.error(EcompLoggerErrorCode.UNKNOWN_ERROR, ApplicationDataTypeCache.class.getName(), errorMsg); + BeEcompErrorManager.getInstance().logInternalConnectionError(APPLICATION_DATA_TYPES_CACHE, errorMsg, ErrorSeverity.ERROR); + return Collections.emptyList(); + } + return allDataTypes.left().value(); + } + + private void onDataChangeEventEmit() { + log.trace("Data type cache has changed, sending DataTypesCacheChangedEvent."); + applicationEventPublisher.publishEvent(new DataTypesCacheChangedEvent(this, copyDataTypeCache())); } /** @@ -287,11 +329,12 @@ public class ApplicationDataTypeCache implements ApplicationCache<DataTypeDefini public static class DataTypesCacheChangedEvent extends ApplicationEvent { @Getter - private Map<String, DataTypeDefinition> newData; + private final Map<String, DataTypeDefinition> newData; - public DataTypesCacheChangedEvent(Object source, Map<String, DataTypeDefinition> newData) { + public DataTypesCacheChangedEvent(final Object source, final Map<String, DataTypeDefinition> newData) { super(source); this.newData = newData; } } + } diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/AbstractOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/AbstractOperation.java index 1592782bfa..db379f5915 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/AbstractOperation.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/AbstractOperation.java @@ -42,7 +42,6 @@ import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum; import org.openecomp.sdc.be.model.DataTypeDefinition; import org.openecomp.sdc.be.model.IComplexDefaultValue; import org.openecomp.sdc.be.model.PropertyConstraint; -import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache; import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus; import org.openecomp.sdc.be.model.operations.impl.PropertyOperation.PropertyConstraintDeserialiser; import org.openecomp.sdc.be.model.tosca.ToscaPropertyType; @@ -60,8 +59,6 @@ public abstract class AbstractOperation { @Autowired protected HealingJanusGraphGenericDao janusGraphGenericDao; protected Gson gson = new Gson(); - @Autowired - protected ApplicationDataTypeCache applicationDataTypeCache; protected DataTypeValidatorConverter dataTypeValidatorConverter = DataTypeValidatorConverter.getInstance(); public <ElementDefinition> JanusGraphOperationStatus findAllResourceElementsDefinitionRecursively(String resourceId, diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/PropertyOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/PropertyOperation.java index 790646754d..06323644b7 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/PropertyOperation.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/PropertyOperation.java @@ -1562,14 +1562,10 @@ public class PropertyOperation extends AbstractOperation implements IPropertyOpe } public Either<List<DataTypeData>, JanusGraphOperationStatus> getAllDataTypeNodes() { - Either<List<DataTypeData>, JanusGraphOperationStatus> getAllDataTypes = janusGraphGenericDao - .getByCriteria(NodeTypeEnum.DataType, null, DataTypeData.class); - if (getAllDataTypes.isRight()) { - JanusGraphOperationStatus status = getAllDataTypes.right().value(); - if (status == JanusGraphOperationStatus.NOT_FOUND) { - status = JanusGraphOperationStatus.OK; - return Either.right(status); - } + final Either<List<DataTypeData>, JanusGraphOperationStatus> getAllDataTypes = + janusGraphGenericDao.getByCriteria(NodeTypeEnum.DataType, null, DataTypeData.class); + if (getAllDataTypes.isRight() && getAllDataTypes.right().value() == JanusGraphOperationStatus.NOT_FOUND) { + return Either.left(Collections.emptyList()); } return getAllDataTypes; } diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/cache/ApplicationDataTypeCacheTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/cache/ApplicationDataTypeCacheTest.java index 7186d2a8bf..9126b64659 100644 --- a/catalog-model/src/test/java/org/openecomp/sdc/be/model/cache/ApplicationDataTypeCacheTest.java +++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/cache/ApplicationDataTypeCacheTest.java @@ -7,9 +7,9 @@ * 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. @@ -20,175 +20,281 @@ package org.openecomp.sdc.be.model.cache; +import static org.awaitility.Awaitility.await; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + import fj.data.Either; -import mockit.Deencapsulation; -import org.apache.commons.lang3.tuple.ImmutablePair; -import org.junit.Before; -import org.junit.Test; +import java.time.Duration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import org.openecomp.sdc.be.config.Configuration; +import org.openecomp.sdc.be.config.Configuration.ApplicationL1CacheConfig; +import org.openecomp.sdc.be.config.Configuration.ApplicationL1CacheInfo; +import org.openecomp.sdc.be.config.ConfigurationManager; import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus; +import org.openecomp.sdc.be.datatypes.elements.DataTypeDataDefinition; import org.openecomp.sdc.be.model.DataTypeDefinition; import org.openecomp.sdc.be.model.operations.impl.PropertyOperation; import org.openecomp.sdc.be.resources.data.DataTypeData; -import org.openecomp.sdc.be.unittests.utils.ModelConfDependentTest; - -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; import org.springframework.context.ApplicationEventPublisher; -public class ApplicationDataTypeCacheTest extends ModelConfDependentTest{ +class ApplicationDataTypeCacheTest { + + @Mock + private PropertyOperation propertyOperation; + + @Mock + private ApplicationEventPublisher applicationEventPublisher; - @InjectMocks - private ApplicationDataTypeCache testSubject; - - @Mock - PropertyOperation propertyOperation; + @InjectMocks + private ApplicationDataTypeCache applicationDataTypeCache; - @Mock - ApplicationEventPublisher applicationEventPublisher; + private Map<String, DataTypeDefinition> dataTypeDefinitionMap; - @Before - public void setUpMocks() throws Exception { - MockitoAnnotations.initMocks(this); + private int schedulerFirstRunDelay = 0; + private int schedulerPollIntervalInSec = 2; + private boolean schedulerIsEnabled = true; + + @BeforeEach + public void beforeEach() { + MockitoAnnotations.openMocks(this); } + @AfterEach + public void afterEach() { + final ScheduledExecutorService scheduledPollingService = applicationDataTypeCache.getScheduledPollingService(); + if (scheduledPollingService == null) { + return; + } + + if (scheduledPollingService.isShutdown()) { + return; + } + + scheduledPollingService.shutdownNow(); + } @Test - public void testInit() throws Exception { - testSubject.init(); + void testInitSuccess() { + defaultInit(); + assertNotNull(applicationDataTypeCache.getScheduledFuture(), "The job should have been triggered"); } @Test - public void testDestroy() throws Exception { - testSubject.init(); - Deencapsulation.invoke(testSubject, "destroy"); + void testDestroySuccess() { + defaultInit(); + assertNotNull(applicationDataTypeCache.getScheduledFuture(), "The job should have been triggered"); + applicationDataTypeCache.destroy(); + assertNull(applicationDataTypeCache.getScheduledFuture(), "The job should have been stopped"); + assertTrue(applicationDataTypeCache.getScheduledPollingService().isShutdown(), "The scheduler should have been stopped"); } @Test - public void testShutdownExecutor() throws Exception { + void testDestroyWithoutSchedulerInitialization() { + mockEmptyConfiguration(); + applicationDataTypeCache.init(); + assertNotNull(applicationDataTypeCache.getScheduledPollingService(), "The scheduler should have been created"); + assertFalse(applicationDataTypeCache.getScheduledPollingService().isShutdown(), "The scheduler should have been running"); + assertNull(applicationDataTypeCache.getScheduledFuture(), "The job should not have been triggered"); + applicationDataTypeCache.destroy(); + assertTrue(applicationDataTypeCache.getScheduledPollingService().isShutdown(), "The scheduler should have been stopped"); + } - // default test - Deencapsulation.invoke(testSubject, "shutdownExecutor"); + @Test + void testInitEmptyConfiguration() { + mockEmptyConfiguration(); + applicationDataTypeCache.init(); + assertNull(applicationDataTypeCache.getScheduledFuture(), "The scheduler should not have started"); } @Test - public void testGetAllDataTypesFromGraph() throws Exception { - Either<Map<String, DataTypeDefinition>, JanusGraphOperationStatus> result; + void testInitCacheDisabled() { + final var applicationL1CacheInfo = new ApplicationL1CacheInfo(); + applicationL1CacheInfo.setEnabled(false); + mockConfiguration(applicationL1CacheInfo); + applicationDataTypeCache.init(); + assertNull(applicationDataTypeCache.getScheduledFuture(), "The scheduler should not have started"); + } - // default test - result = Deencapsulation.invoke(testSubject, "getAllDataTypesFromGraph"); + @Test + void testGetAllAfterInitialization() { + defaultInit(); + final ScheduledFuture<?> scheduledFuture = applicationDataTypeCache.getScheduledFuture(); + //waiting the cache to be filled + await().atMost(Duration.ofSeconds(schedulerPollIntervalInSec + 1)).until(() -> scheduledFuture.getDelay(TimeUnit.SECONDS) != 0); + assertDataTypeCache(dataTypeDefinitionMap); } @Test - public void testGetAll() throws Exception { - Either<Map<String, DataTypeDefinition>, JanusGraphOperationStatus> result; + void testCacheChangeWithDataTypeChange() { + defaultInit(); + final ScheduledFuture<?> scheduledFuture = applicationDataTypeCache.getScheduledFuture(); + //waiting the cache to be filled + await().atMost(Duration.ofSeconds(schedulerPollIntervalInSec + 1)).until(() -> scheduledFuture.getDelay(TimeUnit.SECONDS) != 0); + assertDataTypeCache(dataTypeDefinitionMap); + + final DataTypeDefinition testDataType1 = createDataTypeDefinition("test.data.type1", "test.data.type1", 101L, 1000L); + final DataTypeDefinition testDataType2 = createDataTypeDefinition("test.data.type2", "test.data.type2", 101L, 1002L); + final Map<String, DataTypeDefinition> modifiedDataTypeDefinitionMap = + Map.of(testDataType1.getName(), testDataType1, testDataType2.getName(), testDataType2); + when(propertyOperation.getAllDataTypes()).thenReturn(Either.left(modifiedDataTypeDefinitionMap)); + + final DataTypeData dataTypeData1 = createDataTypeData("test.data.type1", "test.data.type1", 101L, 101L); + final DataTypeData dataTypeData2 = createDataTypeData("test.data.type2", "test.data.type2", 101L, 1002L); - // default test - result = testSubject.getAll(); + when(propertyOperation.getAllDataTypeNodes()).thenReturn(Either.left(List.of(dataTypeData1, dataTypeData2))); + + await().atMost(Duration.ofSeconds(schedulerPollIntervalInSec + 1)).until(() -> scheduledFuture.getDelay(TimeUnit.SECONDS) == 0); + await().atMost(Duration.ofSeconds(schedulerPollIntervalInSec + 1)).until(() -> scheduledFuture.getDelay(TimeUnit.SECONDS) != 0); + assertDataTypeCache(modifiedDataTypeDefinitionMap); } @Test - public void testGet() throws Exception { - String uniqueId = ""; - Either<DataTypeDefinition, JanusGraphOperationStatus> result; + void testCacheChangeWithAddedDataType() { + defaultInit(); + final ScheduledFuture<?> scheduledFuture = applicationDataTypeCache.getScheduledFuture(); + //waiting the cache to be filled + await().until(() -> scheduledFuture.getDelay(TimeUnit.SECONDS) != 0); + assertDataTypeCache(dataTypeDefinitionMap); + + final Map<String, DataTypeDefinition> modifiedDataTypeDefinitionMap = new HashMap<>(); + final DataTypeDefinition testDataType1 = createDataTypeDefinition("test.data.type1", "test.data.type1", 1L, 1L); + modifiedDataTypeDefinitionMap.put(testDataType1.getName(), testDataType1); + final DataTypeDefinition testDataType3 = createDataTypeDefinition("test.data.type3", "test.data.type3", 1L, 1L); + modifiedDataTypeDefinitionMap.put(testDataType3.getName(), testDataType3); + when(propertyOperation.getAllDataTypes()).thenReturn(Either.left(modifiedDataTypeDefinitionMap)); + + final DataTypeData dataTypeData1 = createDataTypeData("test.data.type1", "test.data.type1", 1L, 1L); + final DataTypeData dataTypeData3 = createDataTypeData("test.data.type3", "test.data.type3", 1L, 1L); - // default test - result = testSubject.get(uniqueId); + when(propertyOperation.getAllDataTypeNodes()).thenReturn(Either.left(List.of(dataTypeData1, dataTypeData3))); + + await().atMost(Duration.ofSeconds(schedulerPollIntervalInSec + 1)).until(() -> scheduledFuture.getDelay(TimeUnit.SECONDS) == 0); + await().atMost(Duration.ofSeconds(schedulerPollIntervalInSec + 1)).until(() -> scheduledFuture.getDelay(TimeUnit.SECONDS) != 0); + assertDataTypeCache(modifiedDataTypeDefinitionMap); } @Test - public void testGet2() throws Exception { - String uniqueId = ""; - Either<DataTypeDefinition, JanusGraphOperationStatus> result; - - HashMap<String, DataTypeDefinition> a = new HashMap<>(); - DataTypeDefinition value1 = new DataTypeDefinition(); - value1.setUniqueId("mock"); - a.put("mock", value1); - Either<Map<String, DataTypeDefinition>, JanusGraphOperationStatus> value = Either.left(a); - Mockito.when(propertyOperation.getAllDataTypes()).thenReturn(value); - // default test - Deencapsulation.invoke(testSubject, "replaceAllData"); - result = testSubject.get(uniqueId); - } - - @Test - public void testRun() throws Exception { - testSubject.run(); + void testGetAllWithNoInitialization() { + final Map<String, DataTypeDefinition> dataTypeDefinitionMap = new HashMap<>(); + when(propertyOperation.getAllDataTypes()).thenReturn(Either.left(dataTypeDefinitionMap)); + final Either<Map<String, DataTypeDefinition>, JanusGraphOperationStatus> response = applicationDataTypeCache.getAll(); + assertNotNull(response); + assertTrue(response.isLeft()); } @Test - public void testRun2() throws Exception { - Either<List<DataTypeData>, JanusGraphOperationStatus> value = Either.right( - JanusGraphOperationStatus.GENERAL_ERROR); - Mockito.when(propertyOperation.getAllDataTypeNodes()).thenReturn(value); - testSubject.run(); + void testGetWhenCacheIsEmpty() { + var dataTypeDefinition = new DataTypeDefinition(); + when(propertyOperation.getDataTypeByUid("uniqueId")).thenReturn(Either.left(dataTypeDefinition)); + final Either<DataTypeDefinition, JanusGraphOperationStatus> dataTypeEither = applicationDataTypeCache.get("uniqueId"); + assertNotNull(dataTypeEither); + assertTrue(dataTypeEither.isLeft()); + assertEquals(dataTypeDefinition, dataTypeEither.left().value()); } - - @Test - public void testRun3() throws Exception { - LinkedList<DataTypeData> a = new LinkedList<>(); - a.add(new DataTypeData()); - Either<List<DataTypeData>, JanusGraphOperationStatus> value = Either.left(a); - Mockito.when(propertyOperation.getAllDataTypeNodes()).thenReturn(value); - - HashMap<String, DataTypeDefinition> a1 = new HashMap<>(); - DataTypeDefinition value1 = new DataTypeDefinition(); - value1.setUniqueId("mock"); - a1.put("mock", value1); - Either<Map<String, DataTypeDefinition>, JanusGraphOperationStatus> value2 = Either.left(a1); - Mockito.when(propertyOperation.getAllDataTypes()).thenReturn(value2); - - Deencapsulation.invoke(testSubject, "replaceAllData"); - testSubject.run(); - } - + @Test - public void testCompareDataTypes() throws Exception { - Map<String, ImmutablePair<Long, Long>> dataTypeNameToModificationTime = new HashMap<>(); - Map<String, ImmutablePair<Long, Long>> currentDataTypeToModificationTime = new HashMap<>(); - boolean result; + void testGetCacheHit() { + defaultInit(); + final ScheduledFuture<?> scheduledFuture = applicationDataTypeCache.getScheduledFuture(); + await().atMost(Duration.ofSeconds(schedulerPollIntervalInSec + 1)).until(() -> scheduledFuture.getDelay(TimeUnit.SECONDS) != 0); + final Either<DataTypeDefinition, JanusGraphOperationStatus> dataTypeEither = applicationDataTypeCache.get("test.data.type1"); + assertNotNull(dataTypeEither); + assertTrue(dataTypeEither.isLeft()); + final DataTypeDefinition actualDataTypeDefinition = dataTypeEither.left().value(); + final DataTypeDefinition expectedDataTypeDefinition = dataTypeDefinitionMap.get("test.data.type1"); + assertEquals(expectedDataTypeDefinition.getName(), actualDataTypeDefinition.getName()); + assertEquals(expectedDataTypeDefinition.getUniqueId(), actualDataTypeDefinition.getUniqueId()); + assertEquals(expectedDataTypeDefinition.getCreationTime(), actualDataTypeDefinition.getCreationTime()); + assertEquals(expectedDataTypeDefinition.getModificationTime(), actualDataTypeDefinition.getModificationTime()); + } + + private void defaultInit() { + var applicationL1CacheInfo = new ApplicationL1CacheInfo(); + applicationL1CacheInfo.setEnabled(schedulerIsEnabled); + applicationL1CacheInfo.setFirstRunDelay(schedulerFirstRunDelay); + applicationL1CacheInfo.setPollIntervalInSec(schedulerPollIntervalInSec); + mockConfiguration(applicationL1CacheInfo); + + dataTypeDefinitionMap = new HashMap<>(); + final DataTypeDefinition testDataType1 = createDataTypeDefinition("test.data.type1", "test.data.type1", 100L, 1000L); + dataTypeDefinitionMap.put(testDataType1.getName(), testDataType1); + final DataTypeDefinition testDataType2 = createDataTypeDefinition("test.data.type2", "test.data.type2", 101L, 1001L); + dataTypeDefinitionMap.put(testDataType2.getName(), testDataType2); + when(propertyOperation.getAllDataTypes()).thenReturn(Either.left(dataTypeDefinitionMap)); - // default test - result = Deencapsulation.invoke(testSubject, "compareDataTypes", dataTypeNameToModificationTime, currentDataTypeToModificationTime); + final DataTypeData dataTypeData1 = createDataTypeData("test.data.type1", testDataType1.getName(), 100L, 1000L); + final DataTypeData dataTypeData2 = createDataTypeData("test.data.type2", testDataType2.getName(), 101L, 1001L); + + when(propertyOperation.getAllDataTypeNodes()).thenReturn(Either.left(List.of(dataTypeData1, dataTypeData2))); + applicationDataTypeCache.init(); + } + + private DataTypeDefinition createDataTypeDefinition(String name, String uniqueId, long creationTime, long modificationTime) { + final DataTypeDefinition dataTypeDefinition = new DataTypeDefinition(); + dataTypeDefinition.setName(name); + dataTypeDefinition.setUniqueId(uniqueId); + dataTypeDefinition.setCreationTime(creationTime); + dataTypeDefinition.setModificationTime(modificationTime); + return dataTypeDefinition; + } + + private DataTypeData createDataTypeData(String name, String uniqueId, long creationTime, long modificationTime) { + final DataTypeData dataTypeData1 = new DataTypeData(); + dataTypeData1.setDataTypeDataDefinition(createDataTypeDataDefinition(name, uniqueId, creationTime, modificationTime)); + return dataTypeData1; + } + private DataTypeDataDefinition createDataTypeDataDefinition(String name, String uniqueId, long creationTime, long modificationTime) { + final DataTypeDataDefinition testDataType1DataDefinition = new DataTypeDataDefinition(); + testDataType1DataDefinition.setName(name); + testDataType1DataDefinition.setUniqueId(uniqueId); + testDataType1DataDefinition.setCreationTime(creationTime); + testDataType1DataDefinition.setModificationTime(modificationTime); + return testDataType1DataDefinition; + } + + private void mockConfiguration(final ApplicationL1CacheInfo applicationL1CacheInfo) { + final var applicationL1CacheConfig = new ApplicationL1CacheConfig(); + applicationL1CacheConfig.setDatatypes(applicationL1CacheInfo); + final var configuration = new Configuration(); + configuration.setApplicationL1Cache(applicationL1CacheConfig); + final var configurationManager = new ConfigurationManager(); + configurationManager.setConfiguration(configuration); } - @Test - public void testCompareDataTypes2() throws Exception { - Map<String, ImmutablePair<Long, Long>> dataTypeNameToModificationTime = new HashMap<>(); - Map<String, ImmutablePair<Long, Long>> currentDataTypeToModificationTime = new HashMap<>(); - boolean result; - - currentDataTypeToModificationTime.put("mock", ImmutablePair.of(1L, 2L)); - dataTypeNameToModificationTime.put("mock", ImmutablePair.of(5L, 6L)); - - // default test - result = Deencapsulation.invoke(testSubject, "compareDataTypes", dataTypeNameToModificationTime, currentDataTypeToModificationTime); - } - - @Test - public void testReplaceAllData() throws Exception { - HashMap<String, DataTypeDefinition> a = new HashMap<>(); - DataTypeDefinition value1 = new DataTypeDefinition(); - value1.setUniqueId("mock"); - a.put("mock", value1); - Either<Map<String, DataTypeDefinition>, JanusGraphOperationStatus> value = Either.left(a); - Mockito.when(propertyOperation.getAllDataTypes()).thenReturn(value); - // default test - Deencapsulation.invoke(testSubject, "replaceAllData"); - } - - @Test - public void testReplaceAllData2() throws Exception { - Either<Map<String, DataTypeDefinition>, JanusGraphOperationStatus> value = Either.right( - JanusGraphOperationStatus.GENERAL_ERROR); - Mockito.when(propertyOperation.getAllDataTypes()).thenReturn(value); - // default test - Deencapsulation.invoke(testSubject, "replaceAllData"); + private void mockEmptyConfiguration() { + final var applicationL1CacheConfig = new ApplicationL1CacheConfig(); + final var configuration = new Configuration(); + configuration.setApplicationL1Cache(applicationL1CacheConfig); + final var configurationManager = new ConfigurationManager(); + configurationManager.setConfiguration(configuration); + } + + public void assertDataTypeCache(final Map<String, DataTypeDefinition> expectedDataTypeCache) { + Either<Map<String, DataTypeDefinition>, JanusGraphOperationStatus> dataTypeCacheMapEither = applicationDataTypeCache.getAll(); + assertNotNull(dataTypeCacheMapEither); + assertTrue(dataTypeCacheMapEither.isLeft()); + final Map<String, DataTypeDefinition> actualDataTypeMap = dataTypeCacheMapEither.left().value(); + expectedDataTypeCache.forEach((dataType, dataTypeDefinition) -> { + final DataTypeDefinition actualDataTypeDefinition = actualDataTypeMap.get(dataType); + assertNotNull(actualDataTypeDefinition); + assertEquals(dataTypeDefinition.getCreationTime(), actualDataTypeDefinition.getCreationTime()); + assertEquals(dataTypeDefinition.getModificationTime(), actualDataTypeDefinition.getModificationTime()); + }); } } |